forked from pEp.foundation/libetpan
1736 lines
48 KiB
C
1736 lines
48 KiB
C
/*
|
|
* libEtPan! -- a mail stuff library
|
|
*
|
|
* Copyright (C) 2001, 2005 - DINH Viet Hoa
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the libEtPan! project nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* $Id: mailmime_write_generic.c,v 1.13 2011/06/25 15:30:46 hoa Exp $
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include "mailmime_write_generic.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#ifdef HAVE_SYS_MMAN_H
|
|
# include <sys/mman.h>
|
|
#endif
|
|
#include <errno.h>
|
|
#ifdef WIN32
|
|
# include "win_etpan.h"
|
|
#endif
|
|
|
|
#include "mailimf_write_generic.h"
|
|
#include "mailmime_content.h"
|
|
#include "mailmime_types_helper.h"
|
|
#include "mailmime.h"
|
|
|
|
#include "syscall_wrappers.h"
|
|
|
|
#define MAX_MAIL_COL 78
|
|
|
|
#ifndef TRUE
|
|
#define TRUE 1
|
|
#endif
|
|
|
|
#ifndef FALSE
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
static int mailmime_field_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_field * field);
|
|
|
|
static int mailmime_id_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col, char * id);
|
|
|
|
static int mailmime_description_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col, char * descr);
|
|
|
|
static int mailmime_version_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col, uint32_t version);
|
|
|
|
static int mailmime_encoding_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_mechanism * encoding);
|
|
|
|
static int mailmime_language_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_language * language);
|
|
|
|
static int mailmime_disposition_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_disposition *
|
|
disposition);
|
|
|
|
static int
|
|
mailmime_disposition_param_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_disposition_parm * param);
|
|
|
|
static int mailmime_parameter_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_parameter * param);
|
|
|
|
/*
|
|
static int mailmime_content_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_content * content);
|
|
*/
|
|
|
|
static int mailmime_type_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_type * type);
|
|
|
|
static int
|
|
mailmime_discrete_type_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_discrete_type * discrete_type);
|
|
|
|
static int
|
|
mailmime_composite_type_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_composite_type * composite_type);
|
|
|
|
static int mailmime_sub_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime * build_info);
|
|
|
|
|
|
/* ***** */
|
|
|
|
int mailmime_fields_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col, struct mailmime_fields * fields)
|
|
{
|
|
int r;
|
|
clistiter * cur;
|
|
|
|
for(cur = clist_begin(fields->fld_list) ; cur != NULL ;
|
|
cur = clist_next(cur)) {
|
|
struct mailmime_field * field;
|
|
|
|
field = cur->data;
|
|
r = mailmime_field_write_driver(do_write, data, col, field);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
}
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
static int mailmime_field_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_field * field)
|
|
{
|
|
int r;
|
|
|
|
switch (field->fld_type) {
|
|
case MAILMIME_FIELD_TYPE:
|
|
r = mailmime_content_write_driver(do_write, data, col, field->fld_data.fld_content);
|
|
break;
|
|
|
|
case MAILMIME_FIELD_TRANSFER_ENCODING:
|
|
r = mailmime_encoding_write_driver(do_write, data, col, field->fld_data.fld_encoding);
|
|
break;
|
|
|
|
case MAILMIME_FIELD_ID:
|
|
r = mailmime_id_write_driver(do_write, data, col, field->fld_data.fld_id);
|
|
break;
|
|
|
|
case MAILMIME_FIELD_DESCRIPTION:
|
|
r = mailmime_description_write_driver(do_write, data, col, field->fld_data.fld_description);
|
|
break;
|
|
|
|
case MAILMIME_FIELD_VERSION:
|
|
r = mailmime_version_write_driver(do_write, data, col, field->fld_data.fld_version);
|
|
break;
|
|
|
|
case MAILMIME_FIELD_DISPOSITION:
|
|
r = mailmime_disposition_write_driver(do_write, data, col, field->fld_data.fld_disposition);
|
|
break;
|
|
|
|
case MAILMIME_FIELD_LANGUAGE:
|
|
r = mailmime_language_write_driver(do_write, data, col, field->fld_data.fld_language);
|
|
break;
|
|
|
|
default:
|
|
r = MAILIMF_ERROR_INVAL;
|
|
break;
|
|
}
|
|
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
static int mailmime_id_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col, char * id)
|
|
{
|
|
int r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "Content-ID: ", 12);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "<", 1);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, id, strlen(id));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, ">", 1);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
#if 0
|
|
* col = 0;
|
|
#endif
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
static int mailmime_description_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col, char * descr)
|
|
{
|
|
int r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "Content-Description: ", 21);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, descr, strlen(descr));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
#if 0
|
|
* col = 0;
|
|
#endif
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
static int mailmime_version_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col, uint32_t version)
|
|
{
|
|
int r;
|
|
char versionstr[40];
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "MIME-Version: ", 14);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
snprintf(versionstr, 40, "%i.%i", version >> 16, version & 0xFFFF);
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, versionstr, strlen(versionstr));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
#if 0
|
|
* col = 0;
|
|
#endif
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
static int mailmime_encoding_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_mechanism * encoding)
|
|
{
|
|
int r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "Content-Transfer-Encoding: ", 27);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
switch (encoding->enc_type) {
|
|
case MAILMIME_MECHANISM_7BIT:
|
|
r = mailimf_string_write_driver(do_write, data, col, "7bit", 4);
|
|
break;
|
|
|
|
case MAILMIME_MECHANISM_8BIT:
|
|
r = mailimf_string_write_driver(do_write, data, col, "8bit", 4);
|
|
break;
|
|
|
|
case MAILMIME_MECHANISM_BINARY:
|
|
r = mailimf_string_write_driver(do_write, data, col, "binary", 6);
|
|
break;
|
|
|
|
case MAILMIME_MECHANISM_QUOTED_PRINTABLE:
|
|
r = mailimf_string_write_driver(do_write, data, col, "quoted-printable", 16);
|
|
break;
|
|
|
|
case MAILMIME_MECHANISM_BASE64:
|
|
r = mailimf_string_write_driver(do_write, data, col, "base64", 6);
|
|
break;
|
|
|
|
case MAILMIME_MECHANISM_TOKEN:
|
|
r = mailimf_string_write_driver(do_write, data, col, encoding->enc_token,
|
|
strlen(encoding->enc_token));
|
|
break;
|
|
|
|
default:
|
|
r = MAILIMF_ERROR_INVAL;
|
|
break;
|
|
}
|
|
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
#if 0
|
|
* col = 0;
|
|
#endif
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
static int mailmime_language_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_language * language)
|
|
{
|
|
int r;
|
|
clistiter * cur;
|
|
int first;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "Content-Language: ", 18);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
first = TRUE;
|
|
|
|
for(cur = clist_begin(language->lg_list) ; cur != NULL ;
|
|
cur = clist_next(cur)) {
|
|
char * lang;
|
|
size_t len;
|
|
|
|
lang = clist_content(cur);
|
|
len = strlen(lang);
|
|
|
|
if (!first) {
|
|
r = mailimf_string_write_driver(do_write, data, col, ", ", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
}
|
|
else {
|
|
first = FALSE;
|
|
}
|
|
|
|
if (* col > 1) {
|
|
|
|
if (* col + len > MAX_MAIL_COL) {
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n ", 3);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
#if 0
|
|
* col = 1;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, lang, len);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
#if 0
|
|
* col = 0;
|
|
#endif
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
static int mailmime_disposition_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_disposition *
|
|
disposition)
|
|
{
|
|
struct mailmime_disposition_type * dsp_type;
|
|
int r;
|
|
clistiter * cur;
|
|
|
|
dsp_type = disposition->dsp_type;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "Content-Disposition: ", 21);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
switch (dsp_type->dsp_type) {
|
|
case MAILMIME_DISPOSITION_TYPE_INLINE:
|
|
r = mailimf_string_write_driver(do_write, data, col, "inline", 6);
|
|
break;
|
|
|
|
case MAILMIME_DISPOSITION_TYPE_ATTACHMENT:
|
|
r = mailimf_string_write_driver(do_write, data, col, "attachment", 10);
|
|
break;
|
|
|
|
case MAILMIME_DISPOSITION_TYPE_EXTENSION:
|
|
r = mailimf_string_write_driver(do_write, data, col, dsp_type->dsp_extension,
|
|
strlen(dsp_type->dsp_extension));
|
|
break;
|
|
|
|
default:
|
|
r = MAILIMF_ERROR_INVAL;
|
|
break;
|
|
}
|
|
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
for(cur = clist_begin(disposition->dsp_parms) ;
|
|
cur != NULL ; cur = clist_next(cur)) {
|
|
struct mailmime_disposition_parm * param;
|
|
|
|
param = cur->data;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "; ", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailmime_disposition_param_write_driver(do_write, data, col, param);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
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
|
|
mailmime_disposition_param_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_disposition_parm * param)
|
|
{
|
|
size_t len;
|
|
char sizestr[20];
|
|
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) {
|
|
case MAILMIME_DISPOSITION_PARM_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;
|
|
|
|
case MAILMIME_DISPOSITION_PARM_CREATION_DATE:
|
|
len = strlen("creation-date=") + strlen(param->pa_data.pa_creation_date);
|
|
break;
|
|
|
|
case MAILMIME_DISPOSITION_PARM_MODIFICATION_DATE:
|
|
len = strlen("modification-date=") +
|
|
strlen(param->pa_data.pa_modification_date);
|
|
break;
|
|
|
|
case MAILMIME_DISPOSITION_PARM_READ_DATE:
|
|
len = strlen("read-date=") + strlen(param->pa_data.pa_read_date);
|
|
break;
|
|
|
|
case MAILMIME_DISPOSITION_PARM_SIZE:
|
|
snprintf(sizestr, 20, "%lu", (unsigned long) param->pa_data.pa_size);
|
|
len = strlen("size=") + strlen(sizestr);
|
|
break;
|
|
|
|
case MAILMIME_DISPOSITION_PARM_PARAMETER:
|
|
len = strlen(param->pa_data.pa_parameter->pa_name) + 1 +
|
|
strlen(param->pa_data.pa_parameter->pa_value);
|
|
break;
|
|
|
|
default:
|
|
return MAILIMF_ERROR_INVAL;
|
|
}
|
|
|
|
|
|
// KG: FIXME - this could cause us trouble
|
|
if (* col > 1 && !extended_filename) {
|
|
|
|
if (* col + len > MAX_MAIL_COL) {
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n ", 3);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
#if 0
|
|
* col = 1;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
switch (param->pa_type) {
|
|
case MAILMIME_DISPOSITION_PARM_FILENAME:
|
|
if (extended_filename) {
|
|
if (has_encoded_filename && !has_extended_filename) {
|
|
r = mailimf_string_write_driver(do_write, data, col,
|
|
filename_key, filename_key_len);
|
|
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;
|
|
|
|
case MAILMIME_DISPOSITION_PARM_CREATION_DATE:
|
|
r = mailimf_string_write_driver(do_write, data, col, "creation-date=", 14);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_quoted_string_write_driver(do_write, data, col, param->pa_data.pa_creation_date,
|
|
strlen(param->pa_data.pa_creation_date));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
break;
|
|
|
|
case MAILMIME_DISPOSITION_PARM_MODIFICATION_DATE:
|
|
r = mailimf_string_write_driver(do_write, data, col, "modification-date=", 18);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_quoted_string_write_driver(do_write, data, col,
|
|
param->pa_data.pa_modification_date,
|
|
strlen(param->pa_data.pa_modification_date));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
break;
|
|
|
|
case MAILMIME_DISPOSITION_PARM_READ_DATE:
|
|
r = mailimf_string_write_driver(do_write, data, col, "read-date=", 10);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_quoted_string_write_driver(do_write, data, col, param->pa_data.pa_read_date,
|
|
strlen(param->pa_data.pa_read_date));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
break;
|
|
|
|
case MAILMIME_DISPOSITION_PARM_SIZE:
|
|
r = mailimf_string_write_driver(do_write, data, col, "size=", 5);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, sizestr, strlen(sizestr));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
break;
|
|
|
|
case MAILMIME_DISPOSITION_PARM_PARAMETER:
|
|
r = mailmime_parameter_write_driver(do_write, data, col, param->pa_data.pa_parameter);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
break;
|
|
}
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
static int mailmime_parameter_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_parameter * param)
|
|
{
|
|
int r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, param->pa_name,
|
|
strlen(param->pa_name));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "=", 1);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_quoted_string_write_driver(do_write, data, col, param->pa_value,
|
|
strlen(param->pa_value));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
int mailmime_content_type_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_content * content)
|
|
{
|
|
clistiter * cur;
|
|
size_t len;
|
|
int r;
|
|
|
|
r = mailmime_type_write_driver(do_write, data, col, content->ct_type);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "/", 1);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, content->ct_subtype,
|
|
strlen(content->ct_subtype));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
if (content->ct_parameters != NULL) {
|
|
for(cur = clist_begin(content->ct_parameters) ;
|
|
cur != NULL ; cur = clist_next(cur)) {
|
|
struct mailmime_parameter * param;
|
|
|
|
param = cur->data;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "; ", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
len = strlen(param->pa_name) + 1 + strlen(param->pa_value);
|
|
|
|
if (* col > 1) {
|
|
|
|
if (* col + len > MAX_MAIL_COL) {
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n ", 3);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
#if 0
|
|
* col = 1;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
r = mailmime_parameter_write_driver(do_write, data, col, param);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
int mailmime_content_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_content * content)
|
|
{
|
|
int r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "Content-Type: ", 14);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailmime_content_type_write_driver(do_write, data, col, content);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
static int mailmime_type_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_type * type)
|
|
{
|
|
int r;
|
|
|
|
switch (type->tp_type) {
|
|
case MAILMIME_TYPE_DISCRETE_TYPE:
|
|
r = mailmime_discrete_type_write_driver(do_write, data, col, type->tp_data.tp_discrete_type);
|
|
break;
|
|
|
|
case MAILMIME_TYPE_COMPOSITE_TYPE:
|
|
r = mailmime_composite_type_write_driver(do_write, data, col, type->tp_data.tp_composite_type);
|
|
break;
|
|
|
|
default:
|
|
r = MAILIMF_ERROR_INVAL;
|
|
break;
|
|
}
|
|
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
static int
|
|
mailmime_discrete_type_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_discrete_type * discrete_type)
|
|
{
|
|
int r;
|
|
|
|
switch (discrete_type->dt_type) {
|
|
case MAILMIME_DISCRETE_TYPE_TEXT:
|
|
r = mailimf_string_write_driver(do_write, data, col, "text", 4);
|
|
break;
|
|
|
|
case MAILMIME_DISCRETE_TYPE_IMAGE:
|
|
r = mailimf_string_write_driver(do_write, data, col, "image", 5);
|
|
break;
|
|
|
|
case MAILMIME_DISCRETE_TYPE_AUDIO:
|
|
r = mailimf_string_write_driver(do_write, data, col, "audio", 5);
|
|
break;
|
|
|
|
case MAILMIME_DISCRETE_TYPE_VIDEO:
|
|
r = mailimf_string_write_driver(do_write, data, col, "video", 5);
|
|
break;
|
|
|
|
case MAILMIME_DISCRETE_TYPE_APPLICATION:
|
|
r = mailimf_string_write_driver(do_write, data, col, "application", 11);
|
|
break;
|
|
|
|
case MAILMIME_DISCRETE_TYPE_EXTENSION:
|
|
r = mailimf_string_write_driver(do_write, data, col, discrete_type->dt_extension,
|
|
strlen(discrete_type->dt_extension));
|
|
break;
|
|
|
|
default:
|
|
r = MAILIMF_ERROR_INVAL;
|
|
break;
|
|
}
|
|
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
static int
|
|
mailmime_composite_type_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_composite_type * composite_type)
|
|
{
|
|
int r;
|
|
|
|
switch (composite_type->ct_type) {
|
|
case MAILMIME_COMPOSITE_TYPE_MESSAGE:
|
|
r = mailimf_string_write_driver(do_write, data, col, "message", 7);
|
|
break;
|
|
|
|
case MAILMIME_COMPOSITE_TYPE_MULTIPART:
|
|
r = mailimf_string_write_driver(do_write, data, col, "multipart", 9);
|
|
break;
|
|
|
|
case MAILMIME_COMPOSITE_TYPE_EXTENSION:
|
|
r = mailimf_string_write_driver(do_write, data, col, composite_type->ct_token,
|
|
strlen(composite_type->ct_token));
|
|
break;
|
|
|
|
default:
|
|
r = MAILIMF_ERROR_INVAL;
|
|
break;
|
|
}
|
|
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ****************************************************************** */
|
|
/* message */
|
|
|
|
/*
|
|
static int mailmime_data_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_data * data,
|
|
int is_text);
|
|
*/
|
|
|
|
static int mailmime_text_content_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col, int encoding,
|
|
int istext,
|
|
const char * text, size_t size);
|
|
|
|
/*
|
|
static int mailmime_base64_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
char * text, size_t size);
|
|
|
|
static int mailmime_quoted_printable_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col, int istext,
|
|
char * text, size_t size);
|
|
*/
|
|
|
|
static int mailmime_part_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime * build_info)
|
|
{
|
|
clistiter * cur;
|
|
int first;
|
|
int r;
|
|
char * boundary;
|
|
int istext;
|
|
int res;
|
|
|
|
istext = TRUE;
|
|
boundary = NULL;
|
|
|
|
if (build_info->mm_content_type != NULL) {
|
|
if (build_info->mm_type == MAILMIME_MULTIPLE) {
|
|
boundary = mailmime_extract_boundary(build_info->mm_content_type);
|
|
if (boundary == NULL) {
|
|
boundary = mailmime_generate_boundary();
|
|
if (boundary == NULL) {
|
|
res = MAILIMF_ERROR_MEMORY;
|
|
goto err;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (build_info->mm_content_type->ct_type->tp_type ==
|
|
MAILMIME_TYPE_DISCRETE_TYPE) {
|
|
if (build_info->mm_content_type->ct_type->tp_data.tp_discrete_type->dt_type !=
|
|
MAILMIME_DISCRETE_TYPE_TEXT)
|
|
istext = FALSE;
|
|
}
|
|
}
|
|
|
|
switch (build_info->mm_type) {
|
|
case MAILMIME_SINGLE:
|
|
|
|
/* 1-part body */
|
|
|
|
if (build_info->mm_data.mm_single != NULL) {
|
|
r = mailmime_data_write_driver(do_write, data, col, build_info->mm_data.mm_single, istext);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case MAILMIME_MULTIPLE:
|
|
|
|
/* multi-part */
|
|
|
|
|
|
/* preamble */
|
|
|
|
if (build_info->mm_data.mm_multipart.mm_preamble != NULL) {
|
|
r = mailmime_data_write_driver(do_write, data, col,
|
|
build_info->mm_data.mm_multipart.mm_preamble, TRUE);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
}
|
|
|
|
/* sub-parts */
|
|
|
|
first = TRUE;
|
|
|
|
for(cur = clist_begin(build_info->mm_data.mm_multipart.mm_mp_list) ;
|
|
cur != NULL ; cur = clist_next(cur)) {
|
|
struct mailmime * subpart;
|
|
|
|
subpart = cur->data;
|
|
|
|
if (!first) {
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
}
|
|
else {
|
|
first = FALSE;
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "--", 2);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
|
|
if (boundary == NULL) {
|
|
// Make clang static analyzer happy.
|
|
res = MAILIMF_ERROR_MEMORY;
|
|
goto free_boundary;
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, boundary, strlen(boundary));
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
|
|
r = mailmime_sub_write_driver(do_write, data, col, subpart);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "--", 2);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
|
|
if (boundary == NULL) {
|
|
// Make clang static analyzer happy.
|
|
res = MAILIMF_ERROR_MEMORY;
|
|
goto free_boundary;
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, boundary, strlen(boundary));
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "--", 2);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
|
|
/* epilogue */
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
|
|
if (build_info->mm_data.mm_multipart.mm_epilogue != NULL) {
|
|
r = mailmime_data_write_driver(do_write, data, col,
|
|
build_info->mm_data.mm_multipart.mm_epilogue, TRUE);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case MAILMIME_MESSAGE:
|
|
|
|
if (build_info->mm_data.mm_message.mm_fields != NULL) {
|
|
r = mailimf_fields_write_driver(do_write, data, col,
|
|
build_info->mm_data.mm_message.mm_fields);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
}
|
|
|
|
if (build_info->mm_mime_fields != NULL) {
|
|
int r;
|
|
clistiter * cur;
|
|
|
|
for(cur = clist_begin(build_info->mm_mime_fields->fld_list) ;
|
|
cur != NULL ;
|
|
cur = clist_next(cur)) {
|
|
struct mailmime_field * field;
|
|
|
|
/* only MIME-Version field */
|
|
field = cur->data;
|
|
if (field->fld_type == MAILMIME_FIELD_VERSION) {
|
|
r = mailmime_field_write_driver(do_write, data, col, field);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* encapsuled message */
|
|
|
|
if (build_info->mm_data.mm_message.mm_msg_mime != NULL) {
|
|
r = mailmime_sub_write_driver(do_write, data, col,
|
|
build_info->mm_data.mm_message.mm_msg_mime);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto free_boundary;
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
free(boundary);
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
|
|
free_boundary:
|
|
free(boundary);
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
|
|
static int mailmime_sub_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime * build_info)
|
|
{
|
|
int r;
|
|
|
|
#if 0
|
|
* col = 0;
|
|
#endif
|
|
/* MIME field - Content-Type */
|
|
|
|
if (build_info->mm_content_type != NULL) {
|
|
r = mailmime_content_write_driver(do_write, data, col, build_info->mm_content_type);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
}
|
|
|
|
/* other MIME fields */
|
|
|
|
if (build_info->mm_type != MAILMIME_MESSAGE) {
|
|
if (build_info->mm_mime_fields != NULL) {
|
|
r = mailmime_fields_write_driver(do_write, data, col, build_info->mm_mime_fields);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
}
|
|
}
|
|
else {
|
|
if (build_info->mm_mime_fields != NULL) {
|
|
int r;
|
|
clistiter * cur;
|
|
|
|
/* filter out MIME-Version */
|
|
for(cur = clist_begin(build_info->mm_mime_fields->fld_list) ;
|
|
cur != NULL ;
|
|
cur = clist_next(cur)) {
|
|
struct mailmime_field * field;
|
|
|
|
field = cur->data;
|
|
if (field->fld_type != MAILMIME_FIELD_VERSION) {
|
|
r = mailmime_field_write_driver(do_write, data, col, field);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
return r;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
#if 0
|
|
* col = 0;
|
|
#endif
|
|
|
|
return mailmime_part_write_driver(do_write, data, col, build_info);
|
|
}
|
|
|
|
int mailmime_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime * build_info)
|
|
{
|
|
if (build_info->mm_parent != NULL)
|
|
return mailmime_sub_write_driver(do_write, data, col, build_info);
|
|
else
|
|
return mailmime_part_write_driver(do_write, data, col, build_info);
|
|
}
|
|
|
|
|
|
int mailmime_data_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
struct mailmime_data * mime_data,
|
|
int istext)
|
|
{
|
|
int fd;
|
|
int r;
|
|
char * text;
|
|
struct stat buf;
|
|
int res;
|
|
|
|
switch (mime_data->dt_type) {
|
|
case MAILMIME_DATA_TEXT:
|
|
|
|
if (mime_data->dt_encoded) {
|
|
r = mailimf_string_write_driver(do_write, data, col,
|
|
mime_data->dt_data.dt_text.dt_data,
|
|
mime_data->dt_data.dt_text.dt_length);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
}
|
|
else {
|
|
r = mailmime_text_content_write_driver(do_write, data, col, mime_data->dt_encoding, istext,
|
|
mime_data->dt_data.dt_text.dt_data,
|
|
mime_data->dt_data.dt_text.dt_length);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
}
|
|
|
|
break;
|
|
|
|
case MAILMIME_DATA_FILE:
|
|
fd = Open(mime_data->dt_data.dt_filename, O_RDONLY);
|
|
if (fd < 0) {
|
|
res = MAILIMF_ERROR_FILE;
|
|
goto err;
|
|
}
|
|
|
|
r = fstat(fd, &buf);
|
|
if (r < 0) {
|
|
res = MAILIMF_ERROR_FILE;
|
|
goto Close;
|
|
}
|
|
|
|
if (buf.st_size != 0) {
|
|
text = mmap(NULL, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
if (text == (char *)MAP_FAILED) {
|
|
res = MAILIMF_ERROR_FILE;
|
|
goto Close;
|
|
}
|
|
|
|
if (mime_data->dt_encoded) {
|
|
r = mailimf_string_write_driver(do_write, data, col, text, buf.st_size);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto unmap;
|
|
}
|
|
}
|
|
else {
|
|
r = mailmime_text_content_write_driver(do_write, data, col, mime_data->dt_encoding, istext,
|
|
text, buf.st_size);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = r;
|
|
goto unmap;
|
|
}
|
|
}
|
|
|
|
munmap(text, buf.st_size);
|
|
}
|
|
Close(fd);
|
|
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
break;
|
|
|
|
unmap:
|
|
munmap(text, buf.st_size);
|
|
Close:
|
|
Close(fd);
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
static int mailmime_text_content_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col, int encoding,
|
|
int istext,
|
|
const char * text, size_t size)
|
|
{
|
|
switch (encoding) {
|
|
case MAILMIME_MECHANISM_QUOTED_PRINTABLE:
|
|
return mailmime_quoted_printable_write_driver(do_write, data, col, istext, text, size);
|
|
break;
|
|
|
|
case MAILMIME_MECHANISM_BASE64:
|
|
return mailmime_base64_write_driver(do_write, data, col, text, size);
|
|
break;
|
|
|
|
case MAILMIME_MECHANISM_7BIT:
|
|
case MAILMIME_MECHANISM_8BIT:
|
|
case MAILMIME_MECHANISM_BINARY:
|
|
default:
|
|
return mailimf_string_write_driver(do_write, data, col, text, size);
|
|
}
|
|
}
|
|
|
|
|
|
static const char base64_encoding[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
#define BASE64_MAX_COL 76
|
|
|
|
int mailmime_base64_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
const char * text, size_t size)
|
|
{
|
|
int a;
|
|
int b;
|
|
int c;
|
|
size_t remains;
|
|
const char * p;
|
|
size_t count;
|
|
char ogroup[4];
|
|
int r;
|
|
|
|
remains = size;
|
|
p = text;
|
|
|
|
while (remains > 0) {
|
|
switch (remains) {
|
|
case 1:
|
|
a = (unsigned char) p[0];
|
|
b = 0;
|
|
c = 0;
|
|
count = 1;
|
|
break;
|
|
case 2:
|
|
a = (unsigned char) p[0];
|
|
b = (unsigned char) p[1];
|
|
c = 0;
|
|
count = 2;
|
|
break;
|
|
default:
|
|
a = (unsigned char) p[0];
|
|
b = (unsigned char) p[1];
|
|
c = (unsigned char) p[2];
|
|
count = 3;
|
|
break;
|
|
}
|
|
|
|
ogroup[0]= base64_encoding[a >> 2];
|
|
ogroup[1]= base64_encoding[((a & 3) << 4) | (b >> 4)];
|
|
ogroup[2]= base64_encoding[((b & 0xF) << 2) | (c >> 6)];
|
|
ogroup[3]= base64_encoding[c & 0x3F];
|
|
|
|
switch (count) {
|
|
case 1:
|
|
ogroup[2]= '=';
|
|
ogroup[3]= '=';
|
|
break;
|
|
case 2:
|
|
ogroup[3]= '=';
|
|
break;
|
|
}
|
|
|
|
if (* col + 4 > BASE64_MAX_COL) {
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
#if 0
|
|
* col = 0;
|
|
#endif
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, ogroup, 4);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
remains -= count;
|
|
p += count;
|
|
}
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
#if 0
|
|
#define MAX_WRITE_SIZE 512
|
|
#endif
|
|
|
|
enum {
|
|
STATE_INIT,
|
|
STATE_CR,
|
|
STATE_SPACE,
|
|
STATE_SPACE_CR
|
|
};
|
|
|
|
#if 0
|
|
static inline int write_try_buf(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
char ** pstart, size_t * plen)
|
|
{
|
|
int r;
|
|
|
|
if (* plen >= MAX_WRITE_SIZE) {
|
|
r = mailimf_string_write_driver(do_write, data, col, * pstart, * plen);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
* plen = 0;
|
|
}
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
#endif
|
|
|
|
static inline int write_remaining(int (* do_write)(void *, const char *, size_t), void * data, int * col,
|
|
const char ** pstart, size_t * plen)
|
|
{
|
|
int r;
|
|
|
|
if (* plen > 0) {
|
|
r = mailimf_string_write_driver(do_write, data, col, * pstart, * plen);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
* plen = 0;
|
|
}
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
#define QP_MAX_COL 72
|
|
|
|
int mailmime_quoted_printable_write_driver(int (* do_write)(void *, const char *, size_t), void * data, int * col, int istext,
|
|
const char * text, size_t size)
|
|
{
|
|
size_t i;
|
|
const char * start;
|
|
size_t len;
|
|
char hexstr[6];
|
|
int r;
|
|
int state;
|
|
|
|
start = text;
|
|
len = 0;
|
|
state = STATE_INIT;
|
|
|
|
i = 0;
|
|
while (i < size) {
|
|
unsigned char ch;
|
|
|
|
if (* col + len > QP_MAX_COL) {
|
|
r = write_remaining(do_write, data, col, &start, &len);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
start = text + i;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "=\r\n", 3);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
}
|
|
|
|
ch = text[i];
|
|
|
|
switch (state) {
|
|
|
|
case STATE_INIT:
|
|
switch (ch) {
|
|
case ' ':
|
|
case '\t':
|
|
state = STATE_SPACE;
|
|
len ++;
|
|
i ++;
|
|
break;
|
|
|
|
case '\r':
|
|
state = STATE_CR;
|
|
i ++;
|
|
break;
|
|
|
|
case '!':
|
|
case '"':
|
|
case '#':
|
|
case '$':
|
|
case '@':
|
|
case '[':
|
|
case '\\':
|
|
case ']':
|
|
case '^':
|
|
case '`':
|
|
case '{':
|
|
case '|':
|
|
case '}':
|
|
case '~':
|
|
case '=':
|
|
case '?':
|
|
case '_':
|
|
case 'F': /* there is no more 'From' at the beginning of a line */
|
|
r = write_remaining(do_write, data, col, &start, &len);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
start = text + i + 1;
|
|
|
|
snprintf(hexstr, 6, "=%02X", ch);
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, hexstr, 3);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
i ++;
|
|
break;
|
|
|
|
default:
|
|
if (istext && (ch == '\n')) {
|
|
r = write_remaining(do_write, data, col, &start, &len);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
start = text + i + 1;
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
i ++;
|
|
break;
|
|
}
|
|
else {
|
|
if (((ch >= 33) && (ch <= 60)) || ((ch >= 62) && (ch <= 126))) {
|
|
len ++;
|
|
i ++;
|
|
}
|
|
else {
|
|
r = write_remaining(do_write, data, col, &start, &len);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
start = text + i + 1;
|
|
|
|
snprintf(hexstr, 6, "=%02X", ch);
|
|
|
|
r = mailimf_string_write_driver(do_write, data, col, hexstr, 3);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
i ++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_CR:
|
|
switch (ch) {
|
|
case '\n':
|
|
r = write_remaining(do_write, data, col, &start, &len);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
start = text + i + 1;
|
|
r = mailimf_string_write_driver(do_write, data, col, "\r\n", 2);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
i ++;
|
|
state = STATE_INIT;
|
|
break;
|
|
|
|
default:
|
|
r = write_remaining(do_write, data, col, &start, &len);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
start = text + i;
|
|
snprintf(hexstr, 6, "=%02X", '\r');
|
|
r = mailimf_string_write_driver(do_write, data, col, hexstr, 3);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
state = STATE_INIT;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_SPACE:
|
|
switch (ch) {
|
|
case '\r':
|
|
state = STATE_SPACE_CR;
|
|
i ++;
|
|
break;
|
|
|
|
case '\n':
|
|
r = write_remaining(do_write, data, col, &start, &len);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
start = text + i + 1;
|
|
snprintf(hexstr, 6, "=%02X\r\n", text[i - 1]);
|
|
r = mailimf_string_write_driver(do_write, data, col, hexstr, strlen(hexstr));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
state = STATE_INIT;
|
|
i ++;
|
|
break;
|
|
|
|
case ' ':
|
|
case '\t':
|
|
len ++;
|
|
i ++;
|
|
break;
|
|
|
|
default:
|
|
#if 0
|
|
len += 2;
|
|
state = STATE_INIT;
|
|
i ++;
|
|
#endif
|
|
state = STATE_INIT;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case STATE_SPACE_CR:
|
|
switch (ch) {
|
|
case '\n':
|
|
r = write_remaining(do_write, data, col, &start, &len);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
start = text + i + 1;
|
|
snprintf(hexstr, 6, "=%02X\r\n", text[i - 2]);
|
|
r = mailimf_string_write_driver(do_write, data, col, hexstr, strlen(hexstr));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
state = STATE_INIT;
|
|
i ++;
|
|
break;
|
|
|
|
default:
|
|
r = write_remaining(do_write, data, col, &start, &len);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
start = text + i + 1;
|
|
snprintf(hexstr, 6, "%c=%02X", text[i - 2], '\r');
|
|
r = mailimf_string_write_driver(do_write, data, col, hexstr, strlen(hexstr));
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
state = STATE_INIT;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
r = write_remaining(do_write, data, col, &start, &len);
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return r;
|
|
|
|
return MAILIMF_NO_ERROR;
|
|
}
|