text: /*
   * 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.17 2010/04/05 17:46:53 hoa Exp $
   */
  
  #ifdef HAVE_CONFIG_H
  #	include <config.h>
  #endif
  
  #include "mailprivacy_tools.h"
  #include "mailprivacy_tools_private.h"
  
  #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>
  
  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) {
      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);
        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;
  }
  
  
  struct mailmime_parameter *
  mailmime_param_new_with_data(char * name, char * value)
  {
    char * param_name;
    char * param_value;
    struct mailmime_parameter * param;
  
    param_name = strdup(name);
    if (param_name == NULL)
      goto err;
    
    param_value = strdup(value);
    if (param_value == NULL)
      goto free_name;
    
    param = mailmime_parameter_new(param_name, param_value);
    if (param == NULL)
      goto free_value;
    
    return param;
    
   free_value:
    free(param_value);
   free_name:
    free(param_name);
   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)
  {
  #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);
        
        status = system(command);
        
        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)) {
          r = write(passphrase_input[1], passphrase, strlen(passphrase));
          if (r != (int) strlen(passphrase)) {
            close(passphrase_input[1]);
            return ERROR_PASSPHRASE_FILE;
          }
        }
        else {
          /* dummy password */
          r = write(passphrase_input[1], "*dummy*", 7);
          if (r != 7) {
            close(passphrase_input[1]);
            return ERROR_PASSPHRASE_FILE;
          }
        }
        close(passphrase_input[1]);
        
        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
  }
  
  
  