libetpan/src/engine/mailprivacy_tools.c

1594 lines
35 KiB
C

/*
* libEtPan! -- a mail 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: mailprivacy_tools.c,v 1.18 2011/01/06 00:09:52 hoa Exp $
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "mailprivacy_tools.h"
#include "mailprivacy_tools_private.h"
#if __APPLE__
#include <TargetConditionals.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef WIN32
# include "win_etpan.h"
#else
# include <libgen.h>
# include <sys/mman.h>
# include <sys/wait.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libetpan/mailmessage.h>
#include <ctype.h>
#include "mailprivacy.h"
#include <libetpan/libetpan-config.h>
#include <libetpan/data_message_driver.h>
#include <errno.h>
#include "../data-types/syscall_wrappers.h"
void mailprivacy_mime_clear(struct mailmime * mime)
{
struct mailmime_data * data;
clistiter * cur;
switch (mime->mm_type) {
case MAILMIME_SINGLE:
data = mime->mm_data.mm_single;
if (data != NULL) {
if (data->dt_type == MAILMIME_DATA_FILE)
unlink(data->dt_data.dt_filename);
}
break;
case MAILMIME_MULTIPLE:
data = mime->mm_data.mm_multipart.mm_preamble;
if (data != NULL) {
if (data->dt_type == MAILMIME_DATA_FILE)
unlink(data->dt_data.dt_filename);
}
data = mime->mm_data.mm_multipart.mm_epilogue;
if (data != NULL) {
if (data->dt_type == MAILMIME_DATA_FILE)
unlink(data->dt_data.dt_filename);
}
for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ;
cur != NULL ; cur = clist_next(cur)) {
struct mailmime * submime;
submime = clist_content(cur);
mailprivacy_mime_clear(submime);
}
break;
case MAILMIME_MESSAGE:
if (mime->mm_data.mm_message.mm_msg_mime != NULL) {
mailprivacy_mime_clear(mime->mm_data.mm_message.mm_msg_mime);
}
break;
}
}
static FILE * get_tmp_file(char * filename)
{
int fd;
mode_t old_mask;
FILE * f;
old_mask = umask(0077);
fd = Mkstemp(filename);
umask(old_mask);
if (fd == -1)
return NULL;
f = Fdopen(fd, "r+");
if (f == NULL) {
int r;
Close(fd);
unlink(filename);
}
return f;
}
FILE * mailprivacy_get_tmp_file(struct mailprivacy * privacy,
char * filename, size_t size)
{
snprintf(filename, size, "%s/libetpan-privacy-XXXXXX", privacy->tmp_dir);
return get_tmp_file(filename);
}
int mailprivacy_get_tmp_filename(struct mailprivacy * privacy,
char * filename, size_t size)
{
FILE * f;
f = mailprivacy_get_tmp_file(privacy, filename, size);
if (f == NULL)
return MAIL_ERROR_FILE;
Fclose(f);
return MAIL_NO_ERROR;
}
static char * dup_file(struct mailprivacy * privacy,
char * source_filename)
{
char filename[PATH_MAX];
FILE * dest_f;
int r;
struct stat stat_info;
char * dest_filename;
char * mapping;
size_t written;
int fd;
dest_f = mailprivacy_get_tmp_file(privacy, filename, sizeof(filename));
if (dest_f == NULL)
goto err;
dest_filename = strdup(filename);
if (dest_filename == NULL)
goto close_dest;
fd = Open(source_filename, O_RDONLY);
if (fd < 0)
goto free_dest;
r = fstat(fd, &stat_info);
if (r < 0)
goto close_src;
mapping = mmap(NULL, stat_info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (mapping == (char *)MAP_FAILED)
goto close_src;
written = Fwrite(mapping, 1, stat_info.st_size, dest_f);
if (written != (size_t) stat_info.st_size)
goto unmap;
munmap(mapping, stat_info.st_size);
Close(fd);
Fclose(dest_f);
return dest_filename;
unmap:
munmap(mapping, stat_info.st_size);
close_src:
Close(fd);
free_dest:
free(dest_filename);
close_dest:
Fclose(dest_f);
err:
return NULL;
}
/*
mime_data_replace()
write a mime part to a file and change the reference of mailmime_data
to the file.
*/
static int mime_data_replace(struct mailprivacy * privacy,
int encoding_type,
struct mailmime_data * data,
int reencode)
{
char filename[PATH_MAX];
FILE * f;
size_t written;
char * dup_filename;
int res;
int r;
int decoded;
if (data->dt_type != MAILMIME_DATA_TEXT) {
res = MAIL_NO_ERROR;
goto err;
}
f = mailprivacy_get_tmp_file(privacy, filename, sizeof(filename));
if (f == NULL) {
res = MAIL_ERROR_FILE;
goto err;
}
decoded = 0;
if (reencode) {
if (encoding_type != -1) {
char * content;
size_t content_len;
size_t cur_token;
cur_token = 0;
r = mailmime_part_parse(data->dt_data.dt_text.dt_data,
data->dt_data.dt_text.dt_length,
&cur_token, encoding_type, &content, &content_len);
if (r == MAILIMF_NO_ERROR) {
/* write decoded */
written = Fwrite(content, 1, content_len, f);
if (written != content_len) {
Fclose(f);
unlink(filename);
res = MAIL_ERROR_FILE;
goto err;
}
mmap_string_unref(content);
decoded = 1;
data->dt_encoded = 0;
}
}
}
if (!decoded) {
written = Fwrite(data->dt_data.dt_text.dt_data, 1,
data->dt_data.dt_text.dt_length, f);
if (written != data->dt_data.dt_text.dt_length) {
Fclose(f);
unlink(filename);
res = MAIL_ERROR_FILE;
goto err;
}
}
Fclose(f);
dup_filename = strdup(filename);
if (dup_filename == NULL) {
unlink(filename);
res = MAIL_ERROR_MEMORY;
goto err;
}
data->dt_type = MAILMIME_DATA_FILE;
data->dt_data.dt_filename = dup_filename;
return MAIL_NO_ERROR;
err:
return res;
}
/*
recursive_replace_single_parts()
write all parts of the given mime part to file.
*/
static int recursive_replace_single_parts(struct mailprivacy * privacy,
struct mailmime * mime, int reencode)
{
int r;
int res;
clistiter * cur;
mime->mm_mime_start = NULL;
switch(mime->mm_type) {
case MAILMIME_SINGLE:
if (mime->mm_data.mm_single != NULL) {
int encoding_type;
struct mailmime_single_fields single_fields;
mailmime_single_fields_init(&single_fields, mime->mm_mime_fields,
mime->mm_content_type);
if (single_fields.fld_encoding != NULL)
encoding_type = single_fields.fld_encoding->enc_type;
else
encoding_type = -1;
r = mime_data_replace(privacy, encoding_type,
mime->mm_data.mm_single, reencode);
if (r != MAIL_NO_ERROR) {
res = r;
goto err;
}
}
break;
case MAILMIME_MULTIPLE:
if (mime->mm_data.mm_multipart.mm_preamble != NULL) {
r = mime_data_replace(privacy, -1,
mime->mm_data.mm_multipart.mm_preamble, reencode);
if (r != MAIL_NO_ERROR) {
res = r;
goto err;
}
}
if (mime->mm_data.mm_multipart.mm_epilogue != NULL) {
r = mime_data_replace(privacy, -1,
mime->mm_data.mm_multipart.mm_epilogue, reencode);
if (r != MAIL_NO_ERROR) {
res = r;
goto err;
}
}
for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ;
cur != NULL ; cur = clist_next(cur)) {
struct mailmime * child;
child = clist_content(cur);
r = recursive_replace_single_parts(privacy, child, reencode);
if (r != MAIL_NO_ERROR) {
res = r;
goto err;
}
}
break;
case MAILMIME_MESSAGE:
if (mime->mm_data.mm_message.mm_msg_mime != NULL) {
r = recursive_replace_single_parts(privacy,
mime->mm_data.mm_message.mm_msg_mime, reencode);
if (r != MAIL_NO_ERROR) {
res = r;
goto err;
}
}
break;
}
return MAIL_NO_ERROR;
err:
return res;
}
/*
mailprivacy_get_mime()
parse the message in MIME structure,
all single MIME parts are stored in files.
privacy can be set to NULL to disable privacy check.
*/
int mailprivacy_get_mime(struct mailprivacy * privacy,
int check_privacy, int reencode,
char * content, size_t content_len,
struct mailmime ** result_mime)
{
struct mailmime * mime;
mailmessage * msg;
int r;
int res;
#if 0
int check_privacy;
check_privacy = (privacy != NULL);
#endif
/*
use message data driver, get bodystructure and
convert all the data part in MAILMIME_SINGLE to files.
*/
msg = data_message_init(content, content_len);
if (msg == NULL) {
res = MAIL_ERROR_MEMORY;
goto err;
}
#if 0
if (msg->mime == NULL) {
if (check_privacy) {
r = mailprivacy_msg_get_bodystructure(privacy, msg, &mime);
}
else {
/*
don't use etpan_msg_get_bodystructure because it is not useful
and to avoid loops due to security part
*/
r = mailmessage_get_bodystructure(msg, &mime);
}
}
else {
mime = msg->mime;
}
#endif
if (check_privacy)
r = mailprivacy_msg_get_bodystructure(privacy, msg, &mime);
else
r = mailmessage_get_bodystructure(msg, &mime);
if (r != MAIL_NO_ERROR) {
res = r;
goto free_msg;
}
/*
should be done so that the MIME structure need not to be unregistered.
*/
mailprivacy_recursive_unregister_mime(privacy, mime);
r = recursive_replace_single_parts(privacy, mime, reencode);
if (r != MAIL_NO_ERROR) {
res = r;
goto clear_mime;
}
data_message_detach_mime(msg);
#if 0
if (check_privacy)
mailprivacy_msg_flush(privacy, msg);
else
mailmessage_flush(msg);
#endif
mailprivacy_msg_flush(privacy, msg);
mailmessage_free(msg);
* result_mime = mime;
return MAIL_NO_ERROR;
clear_mime:
mailprivacy_mime_clear(mime);
mailprivacy_msg_flush(privacy, msg);
free_msg:
mailmessage_free(msg);
err:
return res;
}
#ifndef LIBETPAN_SYSTEM_BASENAME
static char * libetpan_basename(char * filename)
{
char * next;
char * p;
p = filename;
next = strchr(p, '/');
while (next != NULL) {
p = next;
next = strchr(p + 1, '/');
}
if (p == filename)
return filename;
else
return p + 1;
}
#else
#define libetpan_basename(a) basename(a)
#endif
struct mailmime *
mailprivacy_new_file_part(struct mailprivacy * privacy,
char * filename,
char * default_content_type, int default_encoding)
{
char basename_buf[PATH_MAX];
char * name;
struct mailmime_mechanism * encoding;
struct mailmime_content * content;
struct mailmime * mime;
int r;
char * dup_filename;
struct mailmime_fields * mime_fields;
int encoding_type;
char * content_type_str;
int do_encoding;
if (filename != NULL) {
strncpy(basename_buf, filename, PATH_MAX);
name = libetpan_basename(basename_buf);
}
else {
name = NULL;
}
encoding = NULL;
/* default content-type */
if (default_content_type == NULL)
content_type_str = "application/octet-stream";
else
content_type_str = default_content_type;
content = mailmime_content_new_with_str(content_type_str);
if (content == NULL) {
goto free_content;
}
do_encoding = 1;
if (content->ct_type->tp_type == MAILMIME_TYPE_COMPOSITE_TYPE) {
struct mailmime_composite_type * composite;
composite = content->ct_type->tp_data.tp_composite_type;
switch (composite->ct_type) {
case MAILMIME_COMPOSITE_TYPE_MESSAGE:
if (strcasecmp(content->ct_subtype, "rfc822") == 0)
do_encoding = 0;
break;
case MAILMIME_COMPOSITE_TYPE_MULTIPART:
do_encoding = 0;
break;
}
}
if (do_encoding) {
if (default_encoding == -1)
encoding_type = MAILMIME_MECHANISM_BASE64;
else
encoding_type = default_encoding;
/* default Content-Transfer-Encoding */
encoding = mailmime_mechanism_new(encoding_type, NULL);
if (encoding == NULL) {
goto free_content;
}
}
mime_fields = mailmime_fields_new_with_data(encoding,
NULL, NULL, NULL, NULL);
if (mime_fields == NULL) {
goto free_content;
}
mime = mailmime_new_empty(content, mime_fields);
if (mime == NULL) {
goto free_mime_fields;
}
if ((filename != NULL) && (mime->mm_type == MAILMIME_SINGLE)) {
/*
duplicates the file so that the file can be deleted when
the MIME part is done
*/
dup_filename = dup_file(privacy, filename);
if (dup_filename == NULL) {
goto free_mime;
}
r = mailmime_set_body_file(mime, dup_filename);
if (r != MAILIMF_NO_ERROR) {
free(dup_filename);
goto free_mime;
}
}
return mime;
free_mime:
mailmime_free(mime);
goto err;
free_mime_fields:
mailmime_fields_free(mime_fields);
mailmime_content_free(content);
goto err;
free_content:
if (encoding != NULL)
mailmime_mechanism_free(encoding);
if (content != NULL)
mailmime_content_free(content);
err:
return NULL;
}
int mailmime_substitute(struct mailmime * old_mime,
struct mailmime * new_mime)
{
struct mailmime * parent;
parent = old_mime->mm_parent;
if (parent == NULL)
return MAIL_ERROR_INVAL;
if (old_mime->mm_parent_type == MAILMIME_MESSAGE)
parent->mm_data.mm_message.mm_msg_mime = new_mime;
else /* MAILMIME_MULTIPLE */
old_mime->mm_multipart_pos->data = new_mime;
new_mime->mm_parent = parent;
new_mime->mm_parent_type = old_mime->mm_parent_type;
/* detach old_mime */
old_mime->mm_parent = NULL;
old_mime->mm_parent_type = MAILMIME_NONE;
return MAIL_NO_ERROR;
}
/* write mime headers and body to a file, CR LF fixed */
int mailprivacy_fetch_mime_body_to_file(struct mailprivacy * privacy,
char * filename, size_t size,
mailmessage * msg, struct mailmime * mime)
{
int r;
int res;
FILE * f;
char * content;
size_t content_len;
int col;
if (mime->mm_parent_type == MAILMIME_NONE) {
res = MAIL_ERROR_INVAL;
goto err;
}
f = mailprivacy_get_tmp_file(privacy, filename, size);
if (f == NULL) {
res = MAIL_ERROR_FETCH;
goto err;
}
r = mailprivacy_msg_fetch_section_mime(privacy, msg, mime,
&content, &content_len);
if (r != MAIL_NO_ERROR) {
res = MAIL_ERROR_FETCH;
goto Close;
}
col = 0;
r = mailimf_string_write(f, &col, content, content_len);
mailprivacy_msg_fetch_result_free(privacy, msg, content);
if (r != MAILIMF_NO_ERROR) {
res = r;
goto Close;
}
r = mailprivacy_msg_fetch_section(privacy, msg, mime,
&content, &content_len);
if (r != MAIL_NO_ERROR) {
res = MAIL_ERROR_FETCH;
goto Close;
}
r = mailimf_string_write(f, &col, content, content_len);
mailprivacy_msg_fetch_result_free(privacy, msg, content);
if (r != MAILIMF_NO_ERROR) {
res = r;
goto Close;
}
Fclose(f);
return MAIL_NO_ERROR;
Close:
Fclose(f);
unlink(filename);
err:
return res;
}
int mailprivacy_get_part_from_file(struct mailprivacy * privacy,
int check_security, int reencode, char * filename,
struct mailmime ** result_mime)
{
int fd;
struct mailmime * mime;
int r;
struct stat stat_info;
int res;
char * mapping;
fd = Open(filename, O_RDONLY);
if (fd < 0) {
res = MAIL_ERROR_FILE;
goto err;
}
r = fstat(fd, &stat_info);
if (r < 0) {
res = MAIL_ERROR_FILE;
goto Close;
}
mapping = mmap(NULL, stat_info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (mapping == (char *)MAP_FAILED) {
res = MAIL_ERROR_FILE;
goto Close;
}
mime = NULL;
/* check recursive parts if privacy is set */
r = mailprivacy_get_mime(privacy, check_security, reencode,
mapping, stat_info.st_size, &mime);
if (r != MAIL_NO_ERROR) {
res = r;
goto unmap;
}
if (mime->mm_type == MAILMIME_MESSAGE) {
struct mailmime * submime;
submime = mime->mm_data.mm_message.mm_msg_mime;
if (mime->mm_data.mm_message.mm_msg_mime != NULL) {
mailmime_remove_part(submime);
mailmime_free(mime);
mime = submime;
}
}
munmap(mapping, stat_info.st_size);
Close(fd);
* result_mime = mime;
return MAIL_NO_ERROR;
unmap:
munmap(mapping, stat_info.st_size);
Close:
Close(fd);
err:
return res;
}
int mail_quote_filename(char * result, size_t size, char * path)
{
char * p;
char * result_p;
size_t remaining;
result_p = result;
remaining = size;
for(p = path ; * p != '\0' ; p ++) {
int quote_not_needed;
#if 0
quote_not_needed = (isalpha(* p) || isdigit(* p) || (* p == '/'));
#else
quote_not_needed = (* p != '\\') && (* p != '\'') && (* p != '\"');
#endif
if (quote_not_needed) {
if (remaining > 0) {
* result_p = * p;
result_p ++;
remaining --;
}
else {
result[size - 1] = '\0';
return -1;
}
}
else {
if (remaining >= 2) {
* result_p = '\\';
result_p ++;
* result_p = * p;
result_p ++;
remaining -= 2;
}
else {
result[size - 1] = '\0';
return -1;
}
}
}
if (remaining > 0) {
* result_p = '\0';
}
else {
result[size - 1] = '\0';
return -1;
}
return 0;
}
static void prepare_mime_single(struct mailmime * mime)
{
struct mailmime_single_fields single_fields;
int encoding;
int r;
if (mime->mm_mime_fields != NULL) {
mailmime_single_fields_init(&single_fields, mime->mm_mime_fields,
mime->mm_content_type);
if (single_fields.fld_encoding != NULL) {
encoding = single_fields.fld_encoding->enc_type;
switch (encoding) {
case MAILMIME_MECHANISM_8BIT:
case MAILMIME_MECHANISM_7BIT:
case MAILMIME_MECHANISM_BINARY:
single_fields.fld_encoding->enc_type =
MAILMIME_MECHANISM_QUOTED_PRINTABLE;
break;
}
}
else {
struct mailmime_mechanism * mechanism;
struct mailmime_field * field;
mechanism =
mailmime_mechanism_new(MAILMIME_MECHANISM_QUOTED_PRINTABLE, NULL);
if (mechanism == NULL)
return;
field = mailmime_field_new(MAILMIME_FIELD_TRANSFER_ENCODING,
NULL, mechanism, NULL, NULL, 0, NULL, NULL, NULL);
if (field == NULL) {
mailmime_mechanism_free(mechanism);
return;
}
r = clist_append(mime->mm_mime_fields->fld_list, field);
if (r < 0) {
mailmime_field_free(field);
return;
}
}
}
if (mime->mm_type == MAILMIME_SINGLE) {
switch (mime->mm_data.mm_single->dt_encoding) {
case MAILMIME_MECHANISM_8BIT:
case MAILMIME_MECHANISM_7BIT:
case MAILMIME_MECHANISM_BINARY:
mime->mm_data.mm_single->dt_encoding =
MAILMIME_MECHANISM_QUOTED_PRINTABLE;
mime->mm_data.mm_single->dt_encoded = 0;
break;
}
}
}
/*
mailprivacy_prepare_mime()
we assume we built ourself the message.
*/
void mailprivacy_prepare_mime(struct mailmime * mime)
{
clistiter * cur;
switch (mime->mm_type) {
case MAILMIME_SINGLE:
if (mime->mm_data.mm_single != NULL) {
prepare_mime_single(mime);
}
break;
case MAILMIME_MULTIPLE:
for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ;
cur != NULL ; cur = clist_next(cur)) {
struct mailmime * child;
child = clist_content(cur);
mailprivacy_prepare_mime(child);
}
break;
case MAILMIME_MESSAGE:
if (mime->mm_data.mm_message.mm_msg_mime) {
mailprivacy_prepare_mime(mime->mm_data.mm_message.mm_msg_mime);
}
break;
}
}
char * mailprivacy_dup_imf_file(struct mailprivacy * privacy,
char * source_filename)
{
char filename[PATH_MAX];
FILE * dest_f;
int r;
struct stat stat_info;
char * dest_filename;
char * mapping;
int fd;
int col;
dest_f = mailprivacy_get_tmp_file(privacy,
filename, sizeof(filename));
if (dest_f == NULL)
goto err;
dest_filename = strdup(filename);
if (dest_filename == NULL)
goto close_dest;
fd = Open(source_filename, O_RDONLY);
if (fd < 0)
goto free_dest;
r = fstat(fd, &stat_info);
if (r < 0)
goto close_src;
mapping = mmap(NULL, stat_info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (mapping == (char *)MAP_FAILED)
goto close_src;
col = 0;
r = mailimf_string_write(dest_f, &col, mapping, stat_info.st_size);
if (r != MAILIMF_NO_ERROR)
goto unmap;
munmap(mapping, stat_info.st_size);
Close(fd);
Fclose(dest_f);
return dest_filename;
unmap:
munmap(mapping, stat_info.st_size);
close_src:
Close(fd);
free_dest:
free(dest_filename);
close_dest:
Fclose(dest_f);
err:
return NULL;
}
/* TODO : better function to duplicate mime fields, currenly such inelegant */
struct mailmime_fields *
mailprivacy_mime_fields_dup(struct mailprivacy * privacy,
struct mailmime_fields * mime_fields)
{
FILE * f;
char tmp_file[PATH_MAX];
int col;
int r;
struct mailmime_fields * dup_mime_fields;
int fd;
char * mapping;
struct stat stat_info;
struct mailimf_fields * fields;
size_t cur_token;
f = mailprivacy_get_tmp_file(privacy, tmp_file, sizeof(tmp_file));
if (f == NULL)
goto err;
col = 0;
r = mailmime_fields_write(f, &col, mime_fields);
if (r != MAILIMF_NO_ERROR)
goto unlink;
Fflush(f);
fd = fileno(f);
if (fd == -1)
goto unlink;
r = fstat(fd, &stat_info);
if (r < 0)
goto unlink;
mapping = mmap(NULL, stat_info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (mapping == (char *)MAP_FAILED)
goto unlink;
cur_token = 0;
r = mailimf_optional_fields_parse(mapping, stat_info.st_size,
&cur_token, &fields);
if (r != MAILIMF_NO_ERROR)
goto unmap;
r = mailmime_fields_parse(fields, &dup_mime_fields);
mailimf_fields_free(fields);
if (r != MAILIMF_NO_ERROR)
goto unmap;
munmap(mapping, stat_info.st_size);
Fclose(f);
unlink(tmp_file);
return dup_mime_fields;
unmap:
munmap(mapping, stat_info.st_size);
unlink:
Fclose(f);
unlink(tmp_file);
err:
return NULL;
}
struct mailmime_parameter *
mailmime_parameter_dup(struct mailmime_parameter * param)
{
char * name;
char * value;
struct mailmime_parameter * dup_param;
name = strdup(param->pa_name);
if (name == NULL)
goto err;
value = strdup(param->pa_value);
if (value == NULL)
goto free_name;
dup_param = mailmime_parameter_new(name, value);
if (dup_param == NULL)
goto free_value;
return dup_param;
free_value:
free(value);
free_name:
free(name);
err:
return NULL;
}
struct mailmime_composite_type *
mailmime_composite_type_dup(struct mailmime_composite_type * composite_type)
{
struct mailmime_composite_type * dup_composite;
char * token;
token = NULL;
if (composite_type->ct_token != NULL) {
token = strdup(composite_type->ct_token);
if (token == NULL)
goto err;
}
dup_composite = mailmime_composite_type_new(composite_type->ct_type, token);
if (dup_composite == NULL)
goto free_token;
return dup_composite;
free_token:
if (token != NULL)
free(token);
err:
return NULL;
}
struct mailmime_discrete_type *
mailmime_discrete_type_dup(struct mailmime_discrete_type * discrete_type)
{
struct mailmime_discrete_type * dup_discrete;
char * extension;
extension = NULL;
if (discrete_type->dt_extension != NULL) {
extension = strdup(discrete_type->dt_extension);
if (extension == NULL)
goto err;
}
dup_discrete = mailmime_discrete_type_new(discrete_type->dt_type, extension);
if (dup_discrete == NULL)
goto free_extension;
return dup_discrete;
free_extension:
if (extension != NULL)
free(extension);
err:
return NULL;
}
struct mailmime_type * mailmime_type_dup(struct mailmime_type * type)
{
struct mailmime_type * dup_type;
struct mailmime_discrete_type * discrete_type;
struct mailmime_composite_type * composite_type;
discrete_type = NULL;
composite_type = NULL;
switch (type->tp_type) {
case MAILMIME_TYPE_DISCRETE_TYPE:
discrete_type =
mailmime_discrete_type_dup(type->tp_data.tp_discrete_type);
if (discrete_type == NULL)
goto err;
break;
composite_type =
mailmime_composite_type_dup(type->tp_data.tp_composite_type);
if (composite_type == NULL)
goto free_discrete;
}
dup_type = mailmime_type_new(type->tp_type, discrete_type, composite_type);
if (dup_type == NULL)
goto free_composite;
return dup_type;
free_composite:
if (composite_type != NULL)
mailmime_composite_type_free(composite_type);
free_discrete:
if (discrete_type != NULL)
mailmime_discrete_type_free(discrete_type);
err:
return NULL;
}
struct mailmime_content *
mailmime_content_dup(struct mailmime_content * content)
{
clist * list;
struct mailmime_type * type;
int r;
struct mailmime_content * dup_content;
char * subtype;
type = mailmime_type_dup(content->ct_type);
if (type == NULL)
goto err;
subtype = strdup(content->ct_subtype);
if (subtype == NULL)
goto free_type;
list = clist_new();
if (list == NULL)
goto free_subtype;
if (content->ct_parameters != NULL) {
clistiter * cur;
for(cur = clist_begin(content->ct_parameters) ;
cur != NULL ; cur = clist_next(cur)) {
struct mailmime_parameter * param;
param = mailmime_parameter_dup(clist_content(cur));
if (param == NULL)
goto free_list;
r = clist_append(list, param);
if (r < 0) {
mailmime_parameter_free(param);
goto free_list;
}
}
}
dup_content = mailmime_content_new(type, subtype, list);
if (dup_content == NULL)
goto free_list;
return dup_content;
free_list:
clist_foreach(list, (clist_func) mailmime_parameter_free, NULL);
free_subtype:
free(subtype);
free_type:
mailmime_type_free(type);
err:
return NULL;
}
int mailprivacy_fetch_decoded_to_file(struct mailprivacy * privacy,
char * filename, size_t size,
mailmessage * msg, struct mailmime * mime)
{
int r;
int res;
FILE * f;
char * content;
size_t content_len;
size_t written;
struct mailmime_single_fields single_fields;
int encoding;
size_t cur_token;
char * parsed_content;
size_t parsed_content_len;
mailmime_single_fields_init(&single_fields, mime->mm_mime_fields,
mime->mm_content_type);
if (single_fields.fld_encoding != NULL)
encoding = single_fields.fld_encoding->enc_type;
else
encoding = MAILMIME_MECHANISM_8BIT;
r = mailprivacy_msg_fetch_section(privacy, msg, mime,
&content, &content_len);
if (r != MAIL_NO_ERROR) {
res = MAIL_ERROR_FETCH;
goto err;
}
cur_token = 0;
r = mailmime_part_parse(content, content_len, &cur_token,
encoding, &parsed_content, &parsed_content_len);
mailprivacy_msg_fetch_result_free(privacy, msg, content);
if (r != MAILIMF_NO_ERROR) {
res = MAIL_ERROR_PARSE;
goto err;
}
f = mailprivacy_get_tmp_file(privacy, filename, size);
if (f == NULL) {
res = MAIL_ERROR_FETCH;
goto free_fetch;
}
written = Fwrite(parsed_content, 1, parsed_content_len, f);
if (written != parsed_content_len) {
res = MAIL_ERROR_FILE;
goto Close;
}
Fclose(f);
mmap_string_unref(parsed_content);
return MAIL_NO_ERROR;
Close:
Fclose(f);
unlink(filename);
free_fetch:
mmap_string_unref(parsed_content);
err:
return res;
}
#ifdef HAVE_MINGW32_SYSTEM
/* Define to 1 do enable debugging. */
#define DEBUG_W32_SPAWN 0
/* Create pipe where the read end is inheritable. */
static int
create_inheritable_pipe (HANDLE filedes[2])
{
HANDLE r, w, h;
SECURITY_ATTRIBUTES sec_attr;
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
if (!CreatePipe (&r, &w, &sec_attr, 0))
return -1;
if (!DuplicateHandle (GetCurrentProcess(), r,
GetCurrentProcess(), &h, 0,
TRUE, DUPLICATE_SAME_ACCESS ))
{
CloseHandle (r);
CloseHandle (w);
return -1;
}
CloseHandle (w);
w = h;
filedes[0] = r;
filedes[1] = w;
return 0;
}
#endif /*HAVE_MINGW32_SYSTEM*/
int mailprivacy_spawn_and_wait(char * command, char * passphrase,
char * stdoutfile, char * stderrfile,
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
int res;
SECURITY_ATTRIBUTES sec_attr;
PROCESS_INFORMATION pi =
{
NULL, /* Returns process handle. */
0, /* Returns primary thread handle. */
0, /* Returns pid. */
0 /* Returns tid. */
};
STARTUPINFO si;
int cr_flags;
HANDLE wp_passphrase[2];
HANDLE fd_out;
HANDLE fd_err;
int code;
/* Prepare security attributes. */
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = TRUE;
fd_out = CreateFile (stdoutfile,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
&sec_attr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (fd_out == INVALID_HANDLE_VALUE)
{
res = ERROR_PASSPHRASE_FILE;
goto err;
}
fd_err = CreateFile (stderrfile,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
&sec_attr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (fd_err == INVALID_HANDLE_VALUE)
{
res = ERROR_PASSPHRASE_FILE;
goto close_out;
}
/* Create a pipe for the passphrase. */
if (create_inheritable_pipe (wp_passphrase))
{
res = ERROR_PASSPHRASE_FILE;
goto close_err;
}
/* Prepare security attributes. */
memset (&sec_attr, 0, sizeof sec_attr);
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
/* Start the process. */
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = DEBUG_W32_SPAWN ? SW_SHOW : SW_MINIMIZE;
si.hStdInput = wp_passphrase[0];
si.hStdOutput = fd_out;
si.hStdError = fd_err;
cr_flags = (CREATE_DEFAULT_ERROR_MODE
| GetPriorityClass (GetCurrentProcess ())
| CREATE_SUSPENDED);
if (!CreateProcess (NULL, /* Program to start. */
command, /* Command line arguments. */
&sec_attr, /* Process security attributes. */
&sec_attr, /* Thread security attributes. */
TRUE, /* Inherit handles. */
cr_flags, /* Creation flags. */
NULL, /* Environment. */
NULL, /* Use current drive/directory. */
&si, /* Startup information. */
&pi /* Returns process information. */
))
{
CloseHandle (wp_passphrase[0]);
CloseHandle (wp_passphrase[1]);
res = ERROR_PASSPHRASE_COMMAND;
goto close_err;
}
/* Close the other end of the pipe. */
CloseHandle (wp_passphrase[0]);
CloseHandle (fd_out);
CloseHandle (fd_err);
/* Process has been created suspended; resume it now. */
ResumeThread (pi.hThread);
CloseHandle (pi.hThread);
if ((passphrase != NULL) && (strlen(passphrase) > 0)) {
DWORD written;
WriteFile (wp_passphrase[1], passphrase, strlen(passphrase),
&written, NULL);
}
else {
DWORD written;
/* dummy password */
WriteFile(wp_passphrase[1], "*dummy*", 7, &written, NULL);
}
CloseHandle(wp_passphrase[1]);
code = WaitForSingleObject (pi.hProcess, INFINITE);
if (code == WAIT_OBJECT_0)
{
DWORD exc;
if (GetExitCodeProcess (pi.hProcess, &exc))
{
if (exc)
*bad_passphrase = 1;
}
else
res = ERROR_PASSPHRASE_COMMAND;
}
else
res = ERROR_PASSPHRASE_COMMAND;
CloseHandle (pi.hProcess);
return 0;
close_err:
CloseHandle (fd_err);
close_out:
CloseHandle (fd_out);
err:
return res;
#else
int res;
int fd_out;
int fd_err;
pid_t pid;
int passphrase_input[2];
int r;
fd_out = Open(stdoutfile, O_RDWR | O_CREAT | O_TRUNC, 0600);
if (fd_out < 0) {
res = ERROR_PASSPHRASE_FILE;
goto err;
}
fd_err = Open(stderrfile, O_RDWR | O_CREAT | O_TRUNC, 0600);
if (fd_err < 0) {
res = ERROR_PASSPHRASE_FILE;
goto close_out;
}
r = pipe(passphrase_input);
if (r < 0) {
res = ERROR_PASSPHRASE_FILE;
goto close_err;
}
pid = fork();
switch (pid) {
case -1:
{
Close(passphrase_input[0]);
Close(passphrase_input[1]);
res = ERROR_PASSPHRASE_COMMAND;
goto close_err;
}
case 0:
/* child */
{
int status;
/* close unneeded fd */
Close(passphrase_input[1]);
Dup2(passphrase_input[0], 0);
Close(passphrase_input[0]);
Dup2(fd_out, 1);
Close(fd_out);
Dup2(fd_err, 2);
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
// 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
// called again
status = system(command);
#endif
exit(WEXITSTATUS(status));
}
break;
default:
/* parent */
{
int status;
/* close unneeded fd */
Close(fd_err);
Close(fd_out);
Close(passphrase_input[0]);
if ((passphrase != NULL) && (strlen(passphrase) > 0)) {
do {
r = (int) Write(passphrase_input[1], passphrase, strlen(passphrase));
} while (r == -1 && errno == EINTR);
if (r != (int) strlen(passphrase)) {
Close(passphrase_input[1]);
return ERROR_PASSPHRASE_FILE;
}
}
else {
/* dummy password */
r = (int) Write(passphrase_input[1], "*dummy*", 7);
if (r != 7) {
Close(passphrase_input[1]);
return ERROR_PASSPHRASE_FILE;
}
}
Close(passphrase_input[1]);
r = Waitpid(pid, &status, 0);
if (WEXITSTATUS(status) != 0)
*bad_passphrase = 1;
return MAIL_NO_ERROR;
}
break;
}
close_err:
Close(fd_err);
close_out:
Close(fd_out);
err:
return res;
#endif
}