/*
 * 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$
 */

#include "mailprivacy_gnupg.h"

#include "mailprivacy.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
#include "mailprivacy_tools.h"
#include <libetpan/mailmime.h>
#include <libetpan/libetpan-config.h>

static int pgp_is_encrypted(struct mailmime * mime)
{
  if (mime->mm_content_type != NULL) {
    clistiter * cur;
    
    if (strcasecmp(mime->mm_content_type->ct_subtype, "encrypted") != 0)
      return 0;
    
    for(cur = clist_begin(mime->mm_content_type->ct_parameters) ; cur != NULL ;
        cur = clist_next(cur)) {
      struct mailmime_parameter * param;
      
      param = clist_content(cur);
      
      if ((strcasecmp(param->pa_name, "protocol") == 0) &&
          (strcasecmp(param->pa_value, "application/pgp-encrypted") == 0))
        return 1;
    }
  }
  
  return 0;
}

static int pgp_is_signed(struct mailmime * mime)
{
  if (mime->mm_content_type != NULL) {
    clistiter * cur;
    
    if (strcasecmp(mime->mm_content_type->ct_subtype, "signed") != 0)
      return 0;
    
    for(cur = clist_begin(mime->mm_content_type->ct_parameters) ;
        cur != NULL ; cur = clist_next(cur)) {
      struct mailmime_parameter * param;
      
      param = clist_content(cur);
      
      if ((strcasecmp(param->pa_name, "protocol") == 0) &&
          (strcasecmp(param->pa_value, "application/pgp-signature") == 0))
        return 1;
    }
  }
  
  return 0;
}

#define PGP_SIGNED "-----BEGIN PGP SIGNED MESSAGE-----"

int pgp_is_clearsigned(char * data, size_t len)
{
  if (len >= strlen(PGP_SIGNED))
    if (strncmp(data, PGP_SIGNED, sizeof(PGP_SIGNED) - 1) == 0)
      return 1;

  return 0;
}

#define PGP_CRYPTED "-----BEGIN PGP MESSAGE-----"

int pgp_is_crypted_armor(char * data, size_t len)
{
  if (len >= strlen(PGP_CRYPTED))
    if (strncmp(data, PGP_CRYPTED, sizeof(PGP_CRYPTED) - 1) == 0)
      return 1;

  return 0;
}


#define BUF_SIZE 1024

enum {
  NO_ERROR_PGP = 0,
  ERROR_PGP_CHECK,
  ERROR_PGP_COMMAND,
  ERROR_PGP_FILE,
};

/* write output to a file */

static int get_pgp_output(FILE * dest_f, char * command)
{
  FILE * p;
  char buf[BUF_SIZE];
  size_t size;
  int res;
  int status;
  char command_redirected[PATH_MAX];
  
  snprintf(command_redirected, sizeof(command_redirected), "%s 2>&1", command);
  
  /*
    flush buffer so that it is not flushed more than once when forking
  */
  fflush(dest_f);
  
  p = popen(command_redirected, "r");
  if (p == NULL) {
    res = ERROR_PGP_COMMAND;
    goto err;
  }
  
  while ((size = fread(buf, 1, sizeof(buf), p)) != 0) {
    size_t written;
    
    written = fwrite(buf, 1, size, dest_f);
    if (written != size) {
      res = ERROR_PGP_FILE;
      goto close;
    }
  }
  status = pclose(p);
  
  if (WEXITSTATUS(status) != 0)
    return ERROR_PGP_CHECK;
  else
    return NO_ERROR_PGP;
  
 close:
  pclose(p);
 err:
  return res;
}

#define PGP_DECRYPT_DESCRIPTION "PGP encrypted part\r\n"
#define PGP_DECRYPT_FAILED "PGP decryption FAILED\r\n"
#define PGP_DECRYPT_SUCCESS "PGP decryption success\r\n"

/* extracted from mailprivacy_smime.c -- begin */

static char * get_first_from_addr(struct mailmime * mime)
{
  clistiter * cur;
  struct mailimf_single_fields single_fields;
  struct mailimf_fields * fields;
  struct mailimf_mailbox * mb;
  
  if (mime->mm_type != MAILMIME_MESSAGE)
    return NULL;
  
  fields = mime->mm_data.mm_message.mm_fields;
  if (fields == NULL)
    return NULL;
  
  mailimf_single_fields_init(&single_fields, fields);
  
  if (single_fields.fld_from == NULL)
    return NULL;
  
  cur = clist_begin(single_fields.fld_from->frm_mb_list->mb_list);
  if (cur == NULL)
    return NULL;
  
  mb = clist_content(cur);
  
  return mb->mb_addr_spec;
}

/* extracted from mailprivacy_smime.c -- end */

static int pgp_decrypt(struct mailprivacy * privacy,
    mailmessage * msg,
    struct mailmime * mime, struct mailmime ** result)
{
  char default_key[PATH_MAX];
  struct mailmime * version_mime;
  struct mailmime * encrypted_mime;
  clistiter * cur;
  char encrypted_filename[PATH_MAX];
  char description_filename[PATH_MAX];
  FILE * description_f;
  char decrypted_filename[PATH_MAX];
  FILE * decrypted_f;
  char command[PATH_MAX];
  struct mailmime * description_mime;
  struct mailmime * decrypted_mime;
  int r;
  int res;
  int decrypt_ok;
  char quoted_decrypted_filename[PATH_MAX];
  char quoted_encrypted_filename[PATH_MAX];
  struct mailmime * multipart;
  char * email;
  
  /* get the two parts of the PGP message */
  
  cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list);
  if (cur == NULL) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }
  
  version_mime = clist_content(cur);
  cur = clist_next(cur);
  if (cur == NULL) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }
  
  encrypted_mime = clist_content(cur);
  
  /* fetch the second section, that's the useful one */
  
  r = mailprivacy_fetch_decoded_to_file(privacy,
      encrypted_filename, sizeof(encrypted_filename),
      msg, encrypted_mime);
  if (r != MAIL_NO_ERROR) {
    res = r;
    goto err;
  }
  
  /* we are in a safe directory */
  
  decrypted_f = mailprivacy_get_tmp_file(privacy,
      decrypted_filename,
      sizeof(decrypted_filename));
  if (decrypted_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_encrypted;
  }
  fclose(decrypted_f);

  /* description */
  
  description_f = mailprivacy_get_tmp_file(privacy, 
      description_filename,
      sizeof(description_filename));
  if (description_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_decrypted;
  }
  
  fprintf(description_f, PGP_DECRYPT_DESCRIPTION);
  
  /* get encryption key */
  
  * default_key = '\0';
  email = get_first_from_addr(mime);
  if (email != NULL)
    snprintf(default_key, sizeof(default_key),
        "--default-key %s", email);
  
  /* run the command */
  
  r = mail_quote_filename(quoted_encrypted_filename,
       sizeof(quoted_encrypted_filename), encrypted_filename);
  if (r < 0) {
    fclose(description_f);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  r = mail_quote_filename(quoted_decrypted_filename,
      sizeof(quoted_decrypted_filename), decrypted_filename);
  if (r < 0) {
    fclose(description_f);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  decrypt_ok = 0;
  snprintf(command, sizeof(command),
      "gpg -q --batch --yes --out %s %s --decrypt %s",
      quoted_decrypted_filename, default_key, quoted_encrypted_filename);
  
  r = get_pgp_output(description_f, command);
  switch (r) {
  case NO_ERROR_PGP:
    decrypt_ok = 1;
    break;
  case ERROR_PGP_CHECK:
    decrypt_ok = 0;
    break;
  case ERROR_PGP_COMMAND:
    fclose(description_f);
    res = MAIL_ERROR_COMMAND;
    goto unlink_description;
  case ERROR_PGP_FILE:
    fclose(description_f);
    res = MAIL_ERROR_FILE;
    goto unlink_description;
  }
  if (decrypt_ok)
    fprintf(description_f, PGP_DECRYPT_SUCCESS);
  else
    fprintf(description_f, PGP_DECRYPT_FAILED);
  fclose(description_f);
  
  /* building multipart */

  r = mailmime_new_with_content("multipart/x-decrypted", NULL, &multipart);
  if (r != MAILIMF_NO_ERROR) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  /* building the description part */
  
  description_mime = mailprivacy_new_file_part(privacy,
      description_filename,
      "text/plain", MAILMIME_MECHANISM_8BIT);
  if (description_mime == NULL) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  /* adds the description part */
  
  r = mailmime_smart_add_part(multipart, description_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(description_mime);
    mailmime_free(description_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  /* building the decrypted part */
  
  r = mailprivacy_get_part_from_file(privacy, 1,
      decrypted_filename, &decrypted_mime);
  if (r == MAIL_NO_ERROR) {
    /* adds the decrypted part */
    
    r = mailmime_smart_add_part(multipart, decrypted_mime);
    if (r != MAIL_NO_ERROR) {
      mailprivacy_mime_clear(decrypted_mime);
      mailmime_free(decrypted_mime);
      mailprivacy_mime_clear(multipart);
      mailmime_free(multipart);
      res = MAIL_ERROR_MEMORY;
      goto unlink_description;
    }
  }
  
  unlink(description_filename);
  unlink(decrypted_filename);
  unlink(encrypted_filename);
  
  * result = multipart;
  
  return MAIL_NO_ERROR;
  
 unlink_description:
  unlink(description_filename);
 unlink_decrypted:
  unlink(decrypted_filename);
 unlink_encrypted:
  unlink(encrypted_filename);
 err:
  return res;
}

#define PGP_VERIFY_DESCRIPTION "PGP verify signed message\r\n"
#define PGP_VERIFY_FAILED "PGP verification FAILED\r\n"
#define PGP_VERIFY_SUCCESS "PGP verification success\r\n"

static int
pgp_verify(struct mailprivacy * privacy,
    mailmessage * msg,
    struct mailmime * mime, struct mailmime ** result)
{
  struct mailmime * signed_mime;
  struct mailmime * signature_mime;
  char signed_filename[PATH_MAX];
  char signature_filename[PATH_MAX];
  int res;
  int r;
  clistiter * cur;
  char command[PATH_MAX];
  int sign_ok;
  struct mailmime * description_mime;
  FILE * description_f;
  char description_filename[PATH_MAX];
  char quoted_signed_filename[PATH_MAX];
  char quoted_signature_filename[PATH_MAX];
  struct mailmime * multipart;
  struct mailmime * signed_msg_mime;
  
  /* get the two parts of the PGP message */
  
  cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list);
  if (cur == NULL) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }
  
  signed_mime = clist_content(cur);
  cur = clist_next(cur);
  if (cur == NULL) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }
  
  signature_mime = clist_content(cur);
  
  /* fetch signed part and write it to a file */
  
  r = mailprivacy_fetch_mime_body_to_file(privacy,
      signed_filename, sizeof(signed_filename),
      msg, signed_mime);
  if (r != MAIL_NO_ERROR) {
    res = r;
    goto err;
  }
  
  /* fetch signed part and write it to a file */

  r = mailprivacy_fetch_decoded_to_file(privacy,
      signature_filename, sizeof(signature_filename),
      msg, signature_mime);
  if (r != MAIL_NO_ERROR) {
    res = r;
    goto unlink_signed;
  }
  
  /* description */
  
  description_f = mailprivacy_get_tmp_file(privacy,
      description_filename,
      sizeof(description_filename));
  if (description_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_signature;
  }
  
  fprintf(description_f, PGP_VERIFY_DESCRIPTION);
  
  /* run the command */

  r = mail_quote_filename(quoted_signature_filename,
      sizeof(quoted_signature_filename), signature_filename);
  if (r < 0) {
    fclose(description_f);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  r = mail_quote_filename(quoted_signed_filename,
      sizeof(quoted_signed_filename), signed_filename);
  if (r < 0) {
    fclose(description_f);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  sign_ok = 0;
  snprintf(command, sizeof(command), "gpg -q --batch --yes --verify %s %s",
      quoted_signature_filename, quoted_signed_filename);

  r = get_pgp_output(description_f, command);
  switch (r) {
  case NO_ERROR_PGP:
    sign_ok = 1;
    break;
  case ERROR_PGP_CHECK:
    sign_ok = 0;
    break;
  case ERROR_PGP_COMMAND:
    fclose(description_f);
    res = MAIL_ERROR_COMMAND;
    goto unlink_description;
  case ERROR_PGP_FILE:
    fclose(description_f);
    res = MAIL_ERROR_FILE;
    goto unlink_description;
  }
  
  if (sign_ok)
    fprintf(description_f, PGP_VERIFY_SUCCESS);
  else
    fprintf(description_f, PGP_VERIFY_FAILED);
  fclose(description_f);

  /* building multipart */

  r = mailmime_new_with_content("multipart/x-verified", NULL, &multipart);
  if (r != MAILIMF_NO_ERROR) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }

  /* building the description part */
  
  description_mime = mailprivacy_new_file_part(privacy,
      description_filename,
      "text/plain", MAILMIME_MECHANISM_8BIT);
  if (description_mime == NULL) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }

  /* adds the description part */
  
  r = mailmime_smart_add_part(multipart, description_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(description_mime);
    mailmime_free(description_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }

  r = mailprivacy_get_part_from_file(privacy, 1,
      signed_filename, &signed_msg_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  r = mailmime_smart_add_part(multipart, signed_msg_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(signed_msg_mime);
    mailmime_free(signed_msg_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  unlink(description_filename);
  unlink(signature_filename);
  unlink(signed_filename);

  * result = multipart;
  
  return MAIL_NO_ERROR;
  
 unlink_description:
  unlink(description_filename);
 unlink_signature:
  unlink(signature_filename);
 unlink_signed:
  unlink(signed_filename);
 err:
  return res;
}


#define PGP_CLEAR_VERIFY_DESCRIPTION "PGP verify clear signed message\r\n"
#define PGP_CLEAR_VERIFY_FAILED "PGP verification FAILED\r\n"
#define PGP_CLEAR_VERIFY_SUCCESS "PGP verification success\r\n"

static int pgp_verify_clearsigned(struct mailprivacy * privacy,
    mailmessage * msg,
    struct mailmime * mime,
    char * content, size_t content_len, struct mailmime ** result)
{
  int r;
  char command[PATH_MAX];
  int res;
  int sign_ok;
  size_t written;
  char signed_filename[PATH_MAX];
  FILE * signed_f;
  char stripped_filename[PATH_MAX];
  FILE * stripped_f;
  char description_filename[PATH_MAX];
  FILE * description_f;
  char quoted_signed_filename[PATH_MAX];
  char quoted_stripped_filename[PATH_MAX];
  struct mailmime * stripped_mime;
  struct mailmime * description_mime;
  struct mailmime * multipart;
  struct mailmime_content * content_type;
  
  if (mime->mm_parent == NULL) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }

  if (mime->mm_parent->mm_type == MAILMIME_SINGLE) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }
  
  signed_f = mailprivacy_get_tmp_file(privacy,
      signed_filename, sizeof(signed_filename));
  if (signed_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto err;
  }
  
  written = fwrite(content, 1, content_len, signed_f);
  if (written != content_len) {
    fclose(signed_f);
    unlink(signed_filename);
    res = MAIL_ERROR_FILE;
    goto err;
  }
  fclose(signed_f);

  /* XXX - prepare file for PGP, remove trailing WS */
  
  stripped_f = mailprivacy_get_tmp_file(privacy,
      stripped_filename, sizeof(stripped_filename));
  if (stripped_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_signed;
  }
  fclose(stripped_f);
  
  /* description */
  
  description_f = mailprivacy_get_tmp_file(privacy,
      description_filename,
      sizeof(description_filename));
  if (description_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_stripped;
  }
  
  fprintf(description_f, PGP_CLEAR_VERIFY_DESCRIPTION);
  
  r = mail_quote_filename(quoted_stripped_filename,
      sizeof(quoted_stripped_filename), stripped_filename);
  if (r < 0) {
    fclose(description_f);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  r = mail_quote_filename(quoted_signed_filename,
      sizeof(quoted_signed_filename), signed_filename);
  if (r < 0) {
    fclose(description_f);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  snprintf(command, sizeof(command),
      "gpg -q --batch --yes --out %s --decrypt %s",
      quoted_stripped_filename, quoted_signed_filename);
  
  sign_ok = 0;
  r = get_pgp_output(description_f, command);
  switch (r) {
  case NO_ERROR_PGP:
    sign_ok = 1;
    break;
  case ERROR_PGP_CHECK:
    sign_ok = 0;
    break;
  case ERROR_PGP_COMMAND:
    res = MAIL_ERROR_COMMAND;
    fclose(description_f);
    goto unlink_description;
  case ERROR_PGP_FILE:
    res = MAIL_ERROR_FILE;
    fclose(description_f);
    goto unlink_description;
  }
  if (sign_ok)
    fprintf(description_f, PGP_CLEAR_VERIFY_SUCCESS);
  else
    fprintf(description_f, PGP_CLEAR_VERIFY_FAILED);
  fclose(description_f);
  
  /* building multipart */

  r = mailmime_new_with_content("multipart/x-verified", NULL, &multipart);
  if (r != MAILIMF_NO_ERROR) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  /* building the description part */
  
  description_mime = mailprivacy_new_file_part(privacy,
      description_filename,
      "text/plain", MAILMIME_MECHANISM_8BIT);
  if (description_mime == NULL) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  /* adds the description part */
  
  r = mailmime_smart_add_part(multipart, description_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(description_mime);
    mailmime_free(description_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  /* building the signature stripped part */
  
  stripped_mime = mailprivacy_new_file_part(privacy,
      stripped_filename,
      "application/octet-stream",
      MAILMIME_MECHANISM_8BIT);
  if (stripped_mime == NULL) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  /* place original content type */
  
  content_type = mailmime_content_dup(mime->mm_content_type);
  if (content_type == NULL) {
    mailprivacy_mime_clear(stripped_mime);
    mailmime_free(stripped_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  mailmime_content_free(stripped_mime->mm_content_type);
  stripped_mime->mm_content_type = content_type;

  /* place original MIME fields */
  
  if (mime->mm_mime_fields != NULL) {
    struct mailmime_fields * mime_fields;
    clistiter * cur;

    mime_fields = mailprivacy_mime_fields_dup(privacy, mime->mm_mime_fields);
    if (mime_fields == NULL) {
      mailprivacy_mime_clear(stripped_mime);
      mailmime_free(stripped_mime);
      mailprivacy_mime_clear(multipart);
      mailmime_free(multipart);
      res = MAIL_ERROR_MEMORY;
      goto unlink_description;
    }
    for(cur = clist_begin(mime_fields->fld_list) ;
        cur != NULL ; cur = clist_next(cur)) {
      struct mailmime_field * field;
      
      field = clist_content(cur);
      if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) {
        mailmime_field_free(field);
        clist_delete(mime_fields->fld_list, cur);
        break;
      }
    }
    clist_concat(stripped_mime->mm_mime_fields->fld_list,
        mime_fields->fld_list);
    mailmime_fields_free(mime_fields);
  }
  
  /* adds the stripped part */
  
  r = mailmime_smart_add_part(multipart, stripped_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(stripped_mime);
    mailmime_free(stripped_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  unlink(description_filename);
  unlink(stripped_filename);
  unlink(signed_filename);
  
  * result = multipart;
  
  return MAIL_NO_ERROR;
  
 unlink_description:
  unlink(description_filename);
 unlink_stripped:
  unlink(stripped_filename);
 unlink_signed:
  unlink(signed_filename);
 err:
  return res;
}


#define PGP_DECRYPT_ARMOR_DESCRIPTION "PGP ASCII armor encrypted part\r\n"
#define PGP_DECRYPT_ARMOR_FAILED "PGP ASCII armor decryption FAILED\r\n"
#define PGP_DECRYPT_ARMOR_SUCCESS "PGP ASCII armor decryption success\r\n"

static int pgp_decrypt_armor(struct mailprivacy * privacy,
    mailmessage * msg,
    struct mailmime * mime,
    char * content, size_t content_len, struct mailmime ** result)
{
  char default_key[PATH_MAX];
  FILE * encrypted_f;
  char encrypted_filename[PATH_MAX];
  char description_filename[PATH_MAX];
  FILE * description_f;
  char decrypted_filename[PATH_MAX];
  FILE * decrypted_f;
  size_t written;
  char command[PATH_MAX];
  struct mailmime * description_mime;
  struct mailmime * decrypted_mime;
  struct mailmime * multipart;
  int r;
  int res;
  int sign_ok;
  char quoted_decrypted_filename[PATH_MAX];
  char quoted_encrypted_filename[PATH_MAX];
  char * email;

  if (mime->mm_parent == NULL) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }

  if (mime->mm_parent->mm_type == MAILMIME_SINGLE) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }
  
  encrypted_f = mailprivacy_get_tmp_file(privacy,
      encrypted_filename,
      sizeof(encrypted_filename));
  if (encrypted_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto err;
  }
  
  written = fwrite(content, 1, content_len, encrypted_f);
  if (written != content_len) {
    fclose(encrypted_f);
    unlink(encrypted_filename);
    res = MAIL_ERROR_FILE;
    goto err;
  }
  
  fclose(encrypted_f);
  
  /* we are in a safe directory */
  
  decrypted_f = mailprivacy_get_tmp_file(privacy,
      decrypted_filename,
      sizeof(decrypted_filename));
  if (decrypted_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_encrypted;
  }
  fclose(decrypted_f);

  /* description */
  
  description_f = mailprivacy_get_tmp_file(privacy,
      description_filename,
      sizeof(description_filename));
  if (description_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_decrypted;
  }
  
  fprintf(description_f, PGP_DECRYPT_ARMOR_DESCRIPTION);
  
  /* get encryption key */
  
  * default_key = '\0';
  email = get_first_from_addr(mime);
  if (email != NULL)
    snprintf(default_key, sizeof(default_key),
        "--default-key %s", email);
  
  /* run the command */
  
  r = mail_quote_filename(quoted_encrypted_filename,
       sizeof(quoted_encrypted_filename), encrypted_filename);
  if (r < 0) {
    fclose(description_f);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  r = mail_quote_filename(quoted_decrypted_filename,
      sizeof(quoted_decrypted_filename), decrypted_filename);
  if (r < 0) {
    fclose(description_f);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  sign_ok = 0;
  snprintf(command, sizeof(command),
      "gpg -q --batch --yes --out %s %s --decrypt %s",
      quoted_decrypted_filename, default_key, quoted_encrypted_filename);
  
  r = get_pgp_output(description_f, command);
  switch (r) {
  case NO_ERROR_PGP:
    sign_ok = 1;
    break;
  case ERROR_PGP_CHECK:
    sign_ok = 0;
    break;
  case ERROR_PGP_COMMAND:
    fclose(description_f);
    res = MAIL_ERROR_COMMAND;
    goto unlink_description;
  case ERROR_PGP_FILE:
    fclose(description_f);
    res = MAIL_ERROR_FILE;
    goto unlink_description;
  }
  if (sign_ok)
    fprintf(description_f, PGP_DECRYPT_ARMOR_SUCCESS);
  else
    fprintf(description_f, PGP_DECRYPT_ARMOR_FAILED);
  fclose(description_f);
  
  /* building multipart */
  
  r = mailmime_new_with_content("multipart/x-decrypted", NULL, &multipart);
  if (r != MAILIMF_NO_ERROR) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  /* building the description part */
  
  description_mime = mailprivacy_new_file_part(privacy,
      description_filename,
      "text/plain", MAILMIME_MECHANISM_8BIT);
  if (description_mime == NULL) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  /* adds the description part */
  
  r = mailmime_smart_add_part(multipart, description_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(description_mime);
    mailmime_free(description_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  /* building the decrypted part */
  
  r = mailprivacy_get_part_from_file(privacy, 1,
      decrypted_filename, &decrypted_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = r;
    goto unlink_description;
  }
  
  /* adds the decrypted part */
  
  r = mailmime_smart_add_part(multipart, decrypted_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(decrypted_mime);
    mailmime_free(decrypted_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_description;
  }
  
  unlink(description_filename);
  unlink(decrypted_filename);
  unlink(encrypted_filename);
  
  * result = multipart;
  
  return MAIL_NO_ERROR;
  
 unlink_description:
  unlink(description_filename);
 unlink_decrypted:
  unlink(decrypted_filename);
 unlink_encrypted:
  unlink(encrypted_filename);
 err:
  return res;
}


static int mime_is_text(struct mailmime * build_info)
{
  if (build_info->mm_type == MAILMIME_SINGLE) {
    if (build_info->mm_content_type != NULL) {
      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)
          return 1;
      }
    }
    else
      return 1;
  }

  return 0;
}


static int pgp_test_encrypted(struct mailprivacy * privacy,
    mailmessage * msg, struct mailmime * mime)
{
  int r;
  int res;

  switch (mime->mm_type) {
  case MAILMIME_MULTIPLE:
    return (pgp_is_encrypted(mime) || pgp_is_signed(mime));
    
  case MAILMIME_SINGLE:
    /* clear sign or ASCII armor encryption */
    if (mime_is_text(mime)) {
      char * content;
      size_t content_len;
      char * parsed_content;
      size_t parsed_content_len;
      size_t cur_token;
      int encoding;
      struct mailmime_single_fields single_fields;
      
      r = mailprivacy_msg_fetch_section(privacy, msg, mime,
          &content, &content_len);
      if (r != MAIL_NO_ERROR)
        return 0;
      
      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;
      
      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)
        return 0;
      
      res = 0;
      
      if (pgp_is_clearsigned(parsed_content, parsed_content_len))
        res = 1;
      else if (pgp_is_crypted_armor(parsed_content, parsed_content_len))
        res = 1;
      
      mmap_string_unref(parsed_content);
      
      return res;
    }
    break;
  }
  
  return 0;
}

static int pgp_handler(struct mailprivacy * privacy,
    mailmessage * msg,
    struct mailmime * mime, struct mailmime ** result)
{
  int r;
  struct mailmime * alternative_mime;
  
  alternative_mime = NULL;
  switch (mime->mm_type) {
  case MAILMIME_MULTIPLE:
    r = MAIL_ERROR_INVAL;
    if (pgp_is_encrypted(mime)) {
      r = pgp_decrypt(privacy, msg, mime, &alternative_mime);
    }
    else if (pgp_is_signed(mime)) {
      r = pgp_verify(privacy, msg, mime, &alternative_mime);
    }
    
    if (r != MAIL_NO_ERROR)
      return r;

    * result = alternative_mime;
    
    return MAIL_NO_ERROR;
    
  case MAILMIME_SINGLE:
    /* clear sign or ASCII armor encryption */
    if (mime_is_text(mime)) {
      char * content;
      size_t content_len;
      char * parsed_content;
      size_t parsed_content_len;
      size_t cur_token;
      int encoding;
      struct mailmime_single_fields single_fields;
      
      r = mailprivacy_msg_fetch_section(privacy, msg, mime,
          &content, &content_len);
      if (r != MAIL_NO_ERROR)
        return MAIL_ERROR_FETCH;
      
      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;
      
      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)
        return MAIL_ERROR_PARSE;
      
      r = MAIL_ERROR_INVAL;
      if (pgp_is_clearsigned(parsed_content,
              parsed_content_len)) {
        r = pgp_verify_clearsigned(privacy,
            msg, mime, parsed_content, parsed_content_len, &alternative_mime);
      }
      else if (pgp_is_crypted_armor(parsed_content,
                   parsed_content_len)) {
        r = pgp_decrypt_armor(privacy,
            msg, mime, parsed_content, parsed_content_len, &alternative_mime);
      }
      
      mmap_string_unref(parsed_content);
      
      if (r != MAIL_NO_ERROR)
        return r;

      * result = alternative_mime;
      
      return MAIL_NO_ERROR;
    }
    break;
  }
  
  return MAIL_ERROR_INVAL;
}


#if 0
static void prepare_mime_single(struct mailmime * mime)
{
  struct mailmime_single_fields single_fields;
  int encoding;
  int r;
  
  if (mime->mime_fields != NULL) {
    mailmime_single_fields_init(&single_fields, mime->mime_fields,
        mime->content_type);
    if (single_fields.encoding != NULL) {
      encoding = single_fields.encoding->type;
      switch (encoding) {
      case MAILMIME_MECHANISM_8BIT:
      case MAILMIME_MECHANISM_7BIT:
      case MAILMIME_MECHANISM_BINARY:
        single_fields.encoding->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->mime_fields->list, field);
      if (r < 0) {
        mailmime_field_free(field);
        return;
      }
    }
  }
      
  switch (mime->body->encoding) {
  case MAILMIME_MECHANISM_8BIT:
  case MAILMIME_MECHANISM_7BIT:
  case MAILMIME_MECHANISM_BINARY:
    mime->body->encoding = MAILMIME_MECHANISM_QUOTED_PRINTABLE;
    mime->body->encoded = 0;
    break;
  }
}

/*
  prepare_mime()
  
  we assume we built ourself the message.
*/

static void prepare_mime(struct mailmime * mime)
{
  clistiter * cur;
  
  switch (mime->type) {
  case MAILMIME_SINGLE:
    if (mime->body != NULL) {
      prepare_mime_single(mime);
    }
    break;
    
  case MAILMIME_MULTIPLE:
    for(cur = clist_begin(mime->list) ; cur != NULL ; cur = clist_next(cur)) {
      struct mailmime * child;
      
      child = cur->data;
      
      prepare_mime(child);
    }
    break;
    
  case MAILMIME_MESSAGE:
    if (mime->msg_mime) {
      prepare_mime(mime->msg_mime);
    }
    break;
  }
}
#endif

static int pgp_sign_mime(struct mailprivacy * privacy,
    struct mailmime * mime, struct mailmime ** result)
{
  char signed_filename[PATH_MAX];
  FILE * signed_f;
  int res;
  int r;
  int col;
  char signature_filename[PATH_MAX];
  FILE * signature_f;
  char command[PATH_MAX];
  char quoted_signature_filename[PATH_MAX];
  char quoted_signed_filename[PATH_MAX];
  char default_key[PATH_MAX];
  struct mailmime * signature_mime;
  struct mailmime * multipart;
  struct mailmime_content * content;
  struct mailmime_parameter * param;
  struct mailmime * signed_msg_mime;
  char * dup_signature_filename;
  char * email;
  
  /* get signing key */
  
  * default_key = '\0';
  email = get_first_from_addr(mime);
  if (email != NULL)
    snprintf(default_key, sizeof(default_key),
        "--default-key %s", email);
  
  /* part to sign */

  /* encode quoted printable all text parts */
  
  mailprivacy_prepare_mime(mime);
  
  signed_f = mailprivacy_get_tmp_file(privacy,
      signed_filename, sizeof(signed_filename));
  if (signed_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto err;
  }
  
  col = 0;
  r = mailmime_write(signed_f, &col, mime);
  if (r != MAILIMF_NO_ERROR) {
    fclose(signed_f);
    res = MAIL_ERROR_FILE;
    goto unlink_signed;
  }
  
  fclose(signed_f);
  
  /* prepare destination file for signature */
  
  signature_f = mailprivacy_get_tmp_file(privacy,
      signature_filename,
      sizeof(signature_filename));
  if (signature_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_signed;
  }
  fclose(signature_f);
  
  r = mail_quote_filename(quoted_signed_filename,
       sizeof(quoted_signed_filename), signed_filename);
  if (r < 0) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_signature;
  }
  
  r = mail_quote_filename(quoted_signature_filename,
      sizeof(quoted_signature_filename), signature_filename);
  if (r < 0) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_signature;
  }
  
  snprintf(command, sizeof(command),
      "gpg -q -a --batch --yes --digest-algo sha1 --out %s %s -b %s 2>/dev/null",
      quoted_signature_filename, default_key, quoted_signed_filename);
  
  r = system(command);
  if (WEXITSTATUS(r) != 0) {
    res = MAIL_ERROR_COMMAND;
    goto unlink_signature;
  }
  
  /* multipart */
  
  multipart = mailprivacy_new_file_part(privacy, NULL,
      "multipart/signed", -1);
  
  content = multipart->mm_content_type;
  
  param = mailmime_param_new_with_data("micalg", "pgp-sha1");
  if (param == NULL) {
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_signature;
  }
  
  r = clist_append(content->ct_parameters, param);
  if (r < 0) {
    mailmime_parameter_free(param);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_signature;
  }
  
  param = mailmime_param_new_with_data("protocol",
      "application/pgp-signature");
  if (param == NULL) {
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_signature;
  }

  r = clist_append(content->ct_parameters, param);
  if (r < 0) {
    mailmime_parameter_free(param);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_signature;
  }
  
  /* signed part */

  r = mailprivacy_get_part_from_file(privacy, 1,
      signed_filename, &signed_msg_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = r;
    goto unlink_signature;
  }
  
  mailprivacy_prepare_mime(signed_msg_mime);
  
  r = mailmime_smart_add_part(multipart, signed_msg_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(signed_msg_mime);
    mailmime_free(signed_msg_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_signature;
  }

  /* signature part */
  
  /* reencode the signature file with CRLF */
  dup_signature_filename = mailprivacy_dup_imf_file(privacy,
      signature_filename);
  if (dup_signature_filename == NULL) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_FILE;
    goto unlink_signature;
  }
  
  /* replace the signature file */
  unlink(signature_filename);
  strncpy(signature_filename,
      dup_signature_filename, sizeof(signature_filename));
  signature_filename[sizeof(signature_filename) - 1] = '\0';
  
  signature_mime = mailprivacy_new_file_part(privacy,
      signature_filename,
      "application/pgp-signature",
      MAILMIME_MECHANISM_8BIT);
  if (signature_mime == NULL) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_signature;
  }
  
  r = mailmime_smart_add_part(multipart, signature_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(signature_mime);
    mailmime_free(signature_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_signature;
  }

  unlink(signature_filename);
  unlink(signed_filename);
  
  * result = multipart;
  
  return MAIL_NO_ERROR;
  
 unlink_signature:
  unlink(signature_filename);
 unlink_signed:
  unlink(signed_filename);
 err:
  return res;
}


/* ********************************************************************* */
/* find GPG recipient */

static int recipient_add_mb(char * recipient, size_t * len,
    struct mailimf_mailbox * mb)
{
  char buffer[PATH_MAX];
  size_t buflen;

  if (mb->mb_addr_spec != NULL) {
    snprintf(buffer, sizeof(buffer), "-r %s ", mb->mb_addr_spec);
    buflen = strlen(buffer);
    if (buflen >= * len)
      return MAIL_ERROR_MEMORY;

    strncat(recipient, buffer, * len);
    (* len) -= buflen;
  }

  return MAIL_NO_ERROR;
}

static int recipient_add_mb_list(char * recipient, size_t * len,
    struct mailimf_mailbox_list * mb_list)
{
  clistiter * cur;
  int r;

  for(cur = clist_begin(mb_list->mb_list) ; cur != NULL ;
      cur = clist_next(cur)) {
    struct mailimf_mailbox * mb;

    mb = clist_content(cur);

    r = recipient_add_mb(recipient, len, mb);
    if (r != MAIL_NO_ERROR)
      return r;
  }

  return MAIL_NO_ERROR;
}

static int recipient_add_group(char * recipient, size_t * len,
    struct mailimf_group * group)
{
  return recipient_add_mb_list(recipient, len, group->grp_mb_list);
}

static int recipient_add_addr(char * recipient, size_t * len,
    struct mailimf_address * addr)
{
  int r;

  switch (addr->ad_type) {
  case MAILIMF_ADDRESS_MAILBOX:
    r = recipient_add_mb(recipient, len, addr->ad_data.ad_mailbox);
    break;
  case MAILIMF_ADDRESS_GROUP:
    r = recipient_add_group(recipient, len, addr->ad_data.ad_group);
    break;
  default:
    r = MAIL_ERROR_INVAL;
  }
  
  return r;
}

static int recipient_add_addr_list(char * recipient, size_t * len,
    struct mailimf_address_list * addr_list)
{
  clistiter * cur;
  int r;

  for(cur = clist_begin(addr_list->ad_list) ; cur != NULL ;
      cur = clist_next(cur)) {
    struct mailimf_address * addr;

    addr = clist_content(cur);

    r = recipient_add_addr(recipient, len, addr);
    if (r != MAIL_NO_ERROR)
      return r;
  }

  return MAIL_NO_ERROR;
}


static int collect_recipient(char * recipient, size_t size,
    struct mailimf_fields * fields)
{
  struct mailimf_single_fields single_fields;
  int r;
  size_t remaining;
  int res;

  * recipient = '\0';
  remaining = size;
  
  if (fields != NULL)
    mailimf_single_fields_init(&single_fields, fields);
  
  if (single_fields.fld_to != NULL) {
    r = recipient_add_addr_list(recipient, &remaining,
        single_fields.fld_to->to_addr_list);
    if (r != MAIL_NO_ERROR) {
      res = r;
      goto err;
    }
  }

  if (single_fields.fld_cc != NULL) {
    r = recipient_add_addr_list(recipient, &remaining,
        single_fields.fld_cc->cc_addr_list);
    if (r != MAIL_NO_ERROR) {
      res = r;
      goto err;
    }
  }

  if (single_fields.fld_bcc != NULL) {
    if (single_fields.fld_bcc->bcc_addr_list != NULL) {
      r = recipient_add_addr_list(recipient, &remaining,
          single_fields.fld_bcc->bcc_addr_list);
      if (r != MAIL_NO_ERROR) {
        res = r;
        goto err;
      }
    }
  }
  
  return MAIL_NO_ERROR;
  
 err:
  return res;
}


#define PGP_VERSION "Version: 1\r\n"

static int pgp_sign_encrypt_mime(struct mailprivacy * privacy,
    struct mailmime * mime, struct mailmime ** result)
{
  char original_filename[PATH_MAX];
  FILE * original_f;
  int res;
  int r;
  int col;
  char encrypted_filename[PATH_MAX];
  FILE * encrypted_f;
  char version_filename[PATH_MAX];
  FILE * version_f;
  char command[PATH_MAX];
  char quoted_original_filename[PATH_MAX];
  char quoted_encrypted_filename[PATH_MAX];
  char default_key[PATH_MAX];
  struct mailmime * version_mime;
  struct mailmime * multipart;
  struct mailmime_content * content;
  struct mailmime_parameter * param;
  struct mailmime * encrypted_mime;
  char recipient[PATH_MAX];
  struct mailimf_fields * fields;
  struct mailmime * root;
  size_t written;
  char * email;
  
  root = mime;
  while (root->mm_parent != NULL)
    root = root->mm_parent;
  
  fields = NULL;
  if (root->mm_type == MAILMIME_MESSAGE)
    fields = root->mm_data.mm_message.mm_fields;
  
  /* recipient */
  
  collect_recipient(recipient, sizeof(recipient), fields);
  
  /* get signing key */
  
  * default_key = '\0';
  email = get_first_from_addr(mime);
  if (email != NULL)
    snprintf(default_key, sizeof(default_key),
        "--default-key %s", email);
  
  /* part to encrypt */
  
  /* encode quoted printable all text parts */
  
  mailprivacy_prepare_mime(mime);
  
  original_f = mailprivacy_get_tmp_file(privacy, original_filename,
      sizeof(original_filename));
  if (original_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto err;
  }
  
  col = 0;
  r = mailmime_write(original_f, &col, mime);
  if (r != MAILIMF_NO_ERROR) {
    fclose(original_f);
    res = MAIL_ERROR_FILE;
    goto unlink_original;
  }
  
  fclose(original_f);
  
  /* prepare destination file for encryption */
  
  encrypted_f = mailprivacy_get_tmp_file(privacy,
      encrypted_filename,
      sizeof(encrypted_filename));
  if (encrypted_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_original;
  }
  fclose(encrypted_f);
  
  r = mail_quote_filename(quoted_original_filename,
       sizeof(quoted_original_filename), original_filename);
  if (r < 0) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }
  
  r = mail_quote_filename(quoted_encrypted_filename,
      sizeof(quoted_encrypted_filename), encrypted_filename);
  if (r < 0) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }
  
  snprintf(command, sizeof(command),
      "gpg -q %s -a --batch --yes --digest-algo sha1 --out %s -s %s -e %s 2>/dev/null",
      recipient, quoted_encrypted_filename, default_key,
      quoted_original_filename);
  
  r = system(command);
  if (WEXITSTATUS(r) != 0) {
    res = MAIL_ERROR_COMMAND;
    goto unlink_encrypted;
  }
  
  /* multipart */
  
  multipart = mailprivacy_new_file_part(privacy, NULL,
      "multipart/encrypted", -1);
  
  content = multipart->mm_content_type;
  
  param = mailmime_param_new_with_data("protocol",
      "application/pgp-encrypted");
  if (param == NULL) {
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }
  
  r = clist_append(content->ct_parameters, param);
  if (r < 0) {
    mailmime_parameter_free(param);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }

  /* version part */
  
  version_f = mailprivacy_get_tmp_file(privacy,
      version_filename,
      sizeof(version_filename));
  if (version_f == NULL) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_FILE;
    goto unlink_encrypted;
  }
  written = fwrite(PGP_VERSION, 1, sizeof(PGP_VERSION) - 1, version_f);
  if (written != sizeof(PGP_VERSION) - 1) {
    fclose(version_f);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_FILE;
    goto unlink_encrypted;
  }
  fclose(version_f);
  
  version_mime = mailprivacy_new_file_part(privacy,
      version_filename,
      "application/pgp-encrypted",
      MAILMIME_MECHANISM_8BIT);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = r;
    goto unlink_version;
  }

  r = mailmime_smart_add_part(multipart, version_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(version_mime);
    mailmime_free(version_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_version;
  }
  
  /* encrypted part */
  
  encrypted_mime = mailprivacy_new_file_part(privacy,
      encrypted_filename,
      "application/octet-stream",
      MAILMIME_MECHANISM_8BIT);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = r;
    goto unlink_version;
  }
  
  r = mailmime_smart_add_part(multipart, encrypted_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(encrypted_mime);
    mailmime_free(encrypted_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_version;
  }
  
  unlink(version_filename);
  unlink(encrypted_filename);
  unlink(original_filename);
  
  * result = multipart;
  
  return MAIL_NO_ERROR;
  
 unlink_version:
  unlink(version_filename);
 unlink_encrypted:
  unlink(encrypted_filename);
 unlink_original:
  unlink(original_filename);
 err:
  return res;
}


static int pgp_encrypt_mime(struct mailprivacy * privacy,
    struct mailmime * mime, struct mailmime ** result)
{
  char original_filename[PATH_MAX];
  FILE * original_f;
  int res;
  int r;
  int col;
  char encrypted_filename[PATH_MAX];
  FILE * encrypted_f;
  char version_filename[PATH_MAX];
  FILE * version_f;
  char command[PATH_MAX];
  char quoted_original_filename[PATH_MAX];
  char quoted_encrypted_filename[PATH_MAX];
  struct mailmime * version_mime;
  struct mailmime * multipart;
  struct mailmime_content * content;
  struct mailmime_parameter * param;
  struct mailmime * encrypted_mime;
  char recipient[PATH_MAX];
  struct mailimf_fields * fields;
  struct mailmime * root;
  size_t written;

  root = mime;
  while (root->mm_parent != NULL)
    root = root->mm_parent;
  
  fields = NULL;
  if (root->mm_type == MAILMIME_MESSAGE)
    fields = root->mm_data.mm_message.mm_fields;
  
  /* recipient */
  
  collect_recipient(recipient, sizeof(recipient), fields);
  
  /* part to encrypt */
  
  /* encode quoted printable all text parts */
  
  mailprivacy_prepare_mime(mime);
  
  original_f = mailprivacy_get_tmp_file(privacy,
      original_filename, sizeof(original_filename));
  if (original_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto err;
  }
  
  col = 0;
  r = mailmime_write(original_f, &col, mime);
  if (r != MAILIMF_NO_ERROR) {
    fclose(original_f);
    res = MAIL_ERROR_FILE;
    goto unlink_original;
  }
  
  fclose(original_f);
  
  /* prepare destination file for encryption */
  
  encrypted_f = mailprivacy_get_tmp_file(privacy,
      encrypted_filename,
      sizeof(encrypted_filename));
  if (encrypted_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_original;
  }
  fclose(encrypted_f);
  
  r = mail_quote_filename(quoted_original_filename,
       sizeof(quoted_original_filename), original_filename);
  if (r < 0) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }
  
  r = mail_quote_filename(quoted_encrypted_filename,
      sizeof(quoted_encrypted_filename), encrypted_filename);
  if (r < 0) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }
  
  snprintf(command, sizeof(command),
      "gpg -q %s -a --batch --yes --out %s -e %s 2>/dev/null",
      recipient, quoted_encrypted_filename, quoted_original_filename);
  
  r = system(command);
  if (WEXITSTATUS(r) != 0) {
    res = MAIL_ERROR_COMMAND;
    goto unlink_encrypted;
  }
  
  /* multipart */
  
  multipart = mailprivacy_new_file_part(privacy, NULL,
      "multipart/encrypted", -1);
  
  content = multipart->mm_content_type;
  
  param = mailmime_param_new_with_data("protocol",
      "application/pgp-encrypted");
  if (param == NULL) {
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }
  
  r = clist_append(content->ct_parameters, param);
  if (r < 0) {
    mailmime_parameter_free(param);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }

  /* version part */
  
  version_f = mailprivacy_get_tmp_file(privacy,
      version_filename, sizeof(version_filename));
  if (version_f == NULL) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_FILE;
    goto unlink_encrypted;
  }
  written = fwrite(PGP_VERSION, 1, sizeof(PGP_VERSION) - 1, version_f);
  if (written != sizeof(PGP_VERSION) - 1) {
    fclose(version_f);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_FILE;
    goto unlink_encrypted;
  }
  fclose(version_f);
  
  version_mime = mailprivacy_new_file_part(privacy,
      version_filename,
      "application/pgp-encrypted",
      MAILMIME_MECHANISM_8BIT);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = r;
    goto unlink_version;
  }

  r = mailmime_smart_add_part(multipart, version_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(version_mime);
    mailmime_free(version_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_version;
  }
  
  /* encrypted part */
  
  encrypted_mime = mailprivacy_new_file_part(privacy,
      encrypted_filename,
      "application/octet-stream",
      MAILMIME_MECHANISM_8BIT);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = r;
    goto unlink_version;
  }
  
  r = mailmime_smart_add_part(multipart, encrypted_mime);
  if (r != MAIL_NO_ERROR) {
    mailprivacy_mime_clear(encrypted_mime);
    mailmime_free(encrypted_mime);
    mailprivacy_mime_clear(multipart);
    mailmime_free(multipart);
    res = MAIL_ERROR_MEMORY;
    goto unlink_version;
  }
  
  unlink(version_filename);
  unlink(encrypted_filename);
  unlink(original_filename);
  
  * result = multipart;
  
  return MAIL_NO_ERROR;
  
 unlink_version:
  unlink(version_filename);
 unlink_encrypted:
  unlink(encrypted_filename);
 unlink_original:
  unlink(original_filename);
 err:
  return res;
}

static int pgp_clear_sign(struct mailprivacy * privacy,
    struct mailmime * mime, struct mailmime ** result)
{
  char default_key[PATH_MAX];
  char original_filename[PATH_MAX];
  FILE * original_f;
  char signed_filename[PATH_MAX];
  FILE * signed_f;
  char quoted_original_filename[PATH_MAX];
  char quoted_signed_filename[PATH_MAX];
  int col;
  struct mailmime * signed_mime;
  int res;
  int r;
  char command[PATH_MAX];
  struct mailmime_content * content_type;
  char * email;
  
  if (mime->mm_type != MAILMIME_SINGLE) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }
  
  if (mime->mm_data.mm_single == NULL) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }
  
  /* get signing key */
  
  * default_key = '\0';
  email = get_first_from_addr(mime);
  if (email != NULL)
    snprintf(default_key, sizeof(default_key),
        "--default-key %s", email);
  
  /* get part to sign */
  
  original_f = mailprivacy_get_tmp_file(privacy,
      original_filename,
      sizeof(original_filename));
  if (original_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto err;
  }
  
  col = 0;
  r = mailmime_data_write(original_f, &col, mime->mm_data.mm_single, 1);
  if (r != MAILIMF_NO_ERROR) {
    fclose(original_f);
    res = MAIL_ERROR_FILE;
    goto unlink_original;
  }
  fclose(original_f);

  signed_f = mailprivacy_get_tmp_file(privacy,
      signed_filename,
      sizeof(signed_filename));
  if (signed_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_original;
  }
  fclose(signed_f);

  r = mail_quote_filename(quoted_original_filename,
      sizeof(quoted_original_filename), original_filename);
  if (r < 0) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_signed;
  }

  r = mail_quote_filename(quoted_signed_filename,
      sizeof(quoted_signed_filename), signed_filename);
  if (r < 0) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_signed;
  }
  
  snprintf(command, sizeof(command),
      "gpg -q --batch --yes --digest-algo sha1 --out %s %s --clearsign %s 2>/dev/null",
      quoted_signed_filename, default_key, quoted_original_filename);
  
  r = system(command);
  if (WEXITSTATUS(r) != 0) {
    res = MAIL_ERROR_COMMAND;
    goto unlink_signed;
  }

  /* building the signed part */
  
  signed_mime = mailprivacy_new_file_part(privacy, signed_filename,
      NULL, MAILMIME_MECHANISM_8BIT);
  if (signed_mime == NULL) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_signed;
  }
  
  /* place original content type */
  
  content_type = mailmime_content_dup(mime->mm_content_type);
  if (content_type == NULL) {
    mailprivacy_mime_clear(signed_mime);
    mailmime_free(signed_mime);
    res = MAIL_ERROR_MEMORY;
    goto unlink_signed;
  }
  
  mailmime_content_free(signed_mime->mm_content_type);
  signed_mime->mm_content_type = content_type;

  /* place original MIME fields */
  
  if (mime->mm_mime_fields != NULL) {
    struct mailmime_fields * mime_fields;
    clistiter * cur;
    
    mime_fields = mailprivacy_mime_fields_dup(privacy,
        mime->mm_mime_fields);
    if (mime_fields == NULL) {
      mailprivacy_mime_clear(signed_mime);
      mailmime_free(signed_mime);
      res = MAIL_ERROR_MEMORY;
      goto unlink_signed;
    }
    for(cur = clist_begin(mime_fields->fld_list) ;
        cur != NULL ; cur = clist_next(cur)) {
      struct mailmime_field * field;
      
      field = clist_content(cur);
      if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) {
        mailmime_field_free(field);
        clist_delete(mime_fields->fld_list, cur);
        break;
      }
    }
    clist_concat(signed_mime->mm_mime_fields->fld_list,
        mime_fields->fld_list);
    mailmime_fields_free(mime_fields);
  }
  
  unlink(signed_filename);
  unlink(original_filename);
  
  * result = signed_mime;
  
  return MAIL_NO_ERROR;
  
 unlink_signed:
  unlink(signed_filename);
 unlink_original:
  unlink(original_filename);
 err:
  return res;
}


static int pgp_armor_encrypt(struct mailprivacy * privacy,
    struct mailmime * mime, struct mailmime ** result)
{
  char original_filename[PATH_MAX];
  FILE * original_f;
  char encrypted_filename[PATH_MAX];
  FILE * encrypted_f;
  char quoted_original_filename[PATH_MAX];
  char quoted_encrypted_filename[PATH_MAX];
  int col;
  struct mailmime * encrypted_mime;
  int res;
  int r;
  char command[PATH_MAX];
  struct mailmime_content * content_type;
  struct mailmime * root;
  struct mailimf_fields * fields;
  char recipient[PATH_MAX];

  if (mime->mm_type != MAILMIME_SINGLE) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }
  
  if (mime->mm_data.mm_single == NULL) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }
  
  root = mime;
  while (root->mm_parent != NULL)
    root = root->mm_parent;
  
  fields = NULL;
  if (root->mm_type == MAILMIME_MESSAGE)
    fields = root->mm_data.mm_message.mm_fields;
  
  /* recipient */
  
  collect_recipient(recipient, sizeof(recipient), fields);
  
  /* get part to encrypt */
  
  original_f = mailprivacy_get_tmp_file(privacy, original_filename,
      sizeof(original_filename));
  if (original_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto err;
  }
  
  col = 0;
  r = mailmime_data_write(original_f, &col, mime->mm_data.mm_single, 1);
  if (r != MAILIMF_NO_ERROR) {
    fclose(original_f);
    res = MAIL_ERROR_FILE;
    goto unlink_original;
  }
  fclose(original_f);

  encrypted_f = mailprivacy_get_tmp_file(privacy,
      encrypted_filename,
      sizeof(encrypted_filename));
  if (encrypted_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_original;
  }
  fclose(encrypted_f);

  r = mail_quote_filename(quoted_original_filename,
      sizeof(quoted_original_filename), original_filename);
  if (r < 0) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }

  r = mail_quote_filename(quoted_encrypted_filename,
      sizeof(quoted_encrypted_filename), encrypted_filename);
  if (r < 0) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }
  
  snprintf(command, sizeof(command),
      "gpg -q %s --batch --yes --out %s -e --armor %s 2>/dev/null",
      recipient, quoted_encrypted_filename, quoted_original_filename);
  
  r = system(command);
  if (WEXITSTATUS(r) != 0) {
    res = MAIL_ERROR_COMMAND;
    goto unlink_encrypted;
  }
  
  /* building the encrypted part */
  
  encrypted_mime = mailprivacy_new_file_part(privacy,
      encrypted_filename,
      "application/octet-stream",
      MAILMIME_MECHANISM_8BIT);
  if (encrypted_mime == NULL) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }
  
  /* place original content type */
  
  content_type = mailmime_content_dup(mime->mm_content_type);
  if (content_type == NULL) {
    mailprivacy_mime_clear(encrypted_mime);
    mailmime_free(encrypted_mime);
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }
  
  mailmime_content_free(encrypted_mime->mm_content_type);
  encrypted_mime->mm_content_type = content_type;

  /* place original MIME fields */
  
  if (mime->mm_mime_fields != NULL) {
    struct mailmime_fields * mime_fields;
    clistiter * cur;
    
    mime_fields = mailprivacy_mime_fields_dup(privacy, mime->mm_mime_fields);
    if (mime_fields == NULL) {
      mailprivacy_mime_clear(encrypted_mime);
      mailmime_free(encrypted_mime);
      res = MAIL_ERROR_MEMORY;
      goto unlink_encrypted;
    }
    for(cur = clist_begin(mime_fields->fld_list) ;
        cur != NULL ; cur = clist_next(cur)) {
      struct mailmime_field * field;
      
      field = clist_content(cur);
      if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) {
        mailmime_field_free(field);
        clist_delete(mime_fields->fld_list, cur);
        break;
      }
    }
    clist_concat(encrypted_mime->mm_mime_fields->fld_list,
        mime_fields->fld_list);
    mailmime_fields_free(mime_fields);
  }

  unlink(encrypted_filename);
  unlink(original_filename);
  
  * result = encrypted_mime;
  
  return MAIL_NO_ERROR;
  
 unlink_encrypted:
  unlink(encrypted_filename);
 unlink_original:
  unlink(original_filename);
 err:
  return res;
}


static int pgp_armor_sign_encrypt(struct mailprivacy * privacy,
    struct mailmime * mime, struct mailmime ** result)
{
  char default_key[PATH_MAX];
  char original_filename[PATH_MAX];
  FILE * original_f;
  char encrypted_filename[PATH_MAX];
  FILE * encrypted_f;
  char quoted_original_filename[PATH_MAX];
  char quoted_encrypted_filename[PATH_MAX];
  int col;
  struct mailmime * encrypted_mime;
  int res;
  int r;
  char command[PATH_MAX];
  struct mailmime_content * content_type;
  struct mailmime * root;
  struct mailimf_fields * fields;
  char recipient[PATH_MAX];
  char * email;
  
  if (mime->mm_type != MAILMIME_SINGLE) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }
  
  if (mime->mm_data.mm_single == NULL) {
    res = MAIL_ERROR_INVAL;
    goto err;
  }
  
  /* get signing key */
  
  * default_key = '\0';
  email = get_first_from_addr(mime);
  if (email != NULL)
    snprintf(default_key, sizeof(default_key),
        "--default-key %s", email);
  
  root = mime;
  while (root->mm_parent != NULL)
    root = root->mm_parent;
  
  fields = NULL;
  if (root->mm_type == MAILMIME_MESSAGE)
    fields = root->mm_data.mm_message.mm_fields;
  
  /* recipient */
  
  collect_recipient(recipient, sizeof(recipient), fields);
  
  /* get part to encrypt */
  
  original_f = mailprivacy_get_tmp_file(privacy,
      original_filename,
      sizeof(original_filename));
  if (original_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto err;
  }
  
  col = 0;
  r = mailmime_data_write(original_f, &col, mime->mm_data.mm_single, 1);
  if (r != MAILIMF_NO_ERROR) {
    fclose(original_f);
    res = MAIL_ERROR_FILE;
    goto unlink_original;
  }
  fclose(original_f);

  encrypted_f = mailprivacy_get_tmp_file(privacy,
      encrypted_filename,
      sizeof(encrypted_filename));
  if (encrypted_f == NULL) {
    res = MAIL_ERROR_FILE;
    goto unlink_original;
  }
  fclose(encrypted_f);

  r = mail_quote_filename(quoted_original_filename,
      sizeof(quoted_original_filename), original_filename);
  if (r < 0) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }

  r = mail_quote_filename(quoted_encrypted_filename,
      sizeof(quoted_encrypted_filename), encrypted_filename);
  if (r < 0) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }
  
  snprintf(command, sizeof(command),
      "gpg -q %s --batch --yes --digest-algo sha1 "
      "--out %s %s -e -s -a %s 2>/dev/null",
      recipient, encrypted_filename, default_key, original_filename);
  
  r = system(command);
  if (WEXITSTATUS(r) != 0) {
    res = MAIL_ERROR_COMMAND;
    goto unlink_encrypted;
  }
  
  /* building the encrypted part */
  
  encrypted_mime = mailprivacy_new_file_part(privacy,
      encrypted_filename,
      "application/octet-stream",
      MAILMIME_MECHANISM_8BIT);
  if (encrypted_mime == NULL) {
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }
  
  /* place original content type */
  
  content_type = mailmime_content_dup(mime->mm_content_type);
  if (content_type == NULL) {
    mailprivacy_mime_clear(encrypted_mime);
    mailmime_free(encrypted_mime);
    res = MAIL_ERROR_MEMORY;
    goto unlink_encrypted;
  }
  
  mailmime_content_free(encrypted_mime->mm_content_type);
  encrypted_mime->mm_content_type = content_type;

  /* place original MIME fields */
  
  if (mime->mm_mime_fields != NULL) {
    struct mailmime_fields * mime_fields;
    clistiter * cur;
    
    mime_fields = mailprivacy_mime_fields_dup(privacy, mime->mm_mime_fields);
    if (mime_fields == NULL) {
      mailprivacy_mime_clear(encrypted_mime);
      mailmime_free(encrypted_mime);
      res = MAIL_ERROR_MEMORY;
      goto unlink_encrypted;
    }
    for(cur = clist_begin(mime_fields->fld_list) ;
        cur != NULL ; cur = clist_next(cur)) {
      struct mailmime_field * field;
      
      field = clist_content(cur);
      if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) {
        mailmime_field_free(field);
        clist_delete(mime_fields->fld_list, cur);
        break;
      }
    }
    clist_concat(encrypted_mime->mm_mime_fields->fld_list,
        mime_fields->fld_list);
    mailmime_fields_free(mime_fields);
  }

  unlink(encrypted_filename);
  unlink(original_filename);
  
  * result = encrypted_mime;
  
  return MAIL_NO_ERROR;
  
 unlink_encrypted:
  unlink(encrypted_filename);
 unlink_original:
  unlink(original_filename);
 err:
  return res;
}


static struct mailprivacy_encryption pgp_encryption_tab[] = {
  /* PGP signed part */
  {
    .name = "signed",
    .description = "PGP signed part",
    .encrypt = pgp_sign_mime,
  },
  
  /* PGP encrypted part */
  
  {
    .name = "encrypted",
    .description = "PGP encrypted part",
    .encrypt = pgp_encrypt_mime,
  },
  
  /* PGP signed & encrypted part */
  
  {
    .name = "signed-encrypted",
    .description = "PGP signed & encrypted part",
    .encrypt = pgp_sign_encrypt_mime,
  },

  /* PGP clear signed part */
  
  {
    .name = "clear-signed",
    .description = "PGP clear signed part",
    .encrypt = pgp_clear_sign,
  },

  /* PGP armor encrypted part */
  
  {
    .name = "encrypted-armor",
    .description = "PGP ASCII armor encrypted part",
    .encrypt = pgp_armor_encrypt,
  },

  /* PGP armor signed & encrypted part */
  
  {
    .name = "signed-encrypted-armor",
    .description = "PGP ASCII armor signed & encrypted part",
    .encrypt = pgp_armor_sign_encrypt,
  },
};

static struct mailprivacy_protocol pgp_protocol = {
  .name = "pgp",
  .description = "OpenPGP",
  
  .is_encrypted = pgp_test_encrypted,
  .decrypt = pgp_handler,
  
  .encryption_count =
  (sizeof(pgp_encryption_tab) / sizeof(pgp_encryption_tab[0])),
  
  .encryption_tab = pgp_encryption_tab,
};

int mailprivacy_gnupg_init(struct mailprivacy * privacy)
{
  return mailprivacy_register(privacy, &pgp_protocol);
}

void mailprivacy_gnupg_done(struct mailprivacy * privacy)
{
  mailprivacy_unregister(privacy, &pgp_protocol);
}