/*
 * Copyright notice from original mutt:
 * Copyright (C) 2001,2002 Oliver Ehli <elmy@acm.org>
 * Copyright (C) 2002 Mike Schiraldi <raldi@research.netsol.com>
 * Copyright (C) 2004 g10 Code GmbH
 *
 * This file is part of mutt-ng, see http://www.muttng.org/.
 * It's licensed under the GNU General Public License,
 * please see the file GPL in the top level source directory.
 */

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "mutt.h"
#include "enter.h"
#include "handler.h"
#include "mutt_curses.h"
#include "mutt_menu.h"
#include "smime.h"
#include "mime.h"
#include "copy.h"

#include "lib/mem.h"
#include "lib/intl.h"
#include "lib/str.h"
#include "lib/debug.h"

#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <ctype.h>

#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif

#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif

#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif

#ifdef CRYPT_BACKEND_CLASSIC_SMIME

#include "mutt_crypt.h"

struct smime_command_context {
  const char *key;              /* %k */
  const char *cryptalg;         /* %a */
  const char *fname;            /* %f */
  const char *sig_fname;        /* %s */
  const char *certificates;     /* %c */
  const char *intermediates;    /* %i */
};


typedef struct {
  unsigned int hash;
  char suffix;
  char email[256];
  char nick[256];
  char trust;                   /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
  short public;                 /* 1=public 0=private */
} smime_id;


char SmimePass[STRING];
time_t SmimeExptime = 0;        /* when does the cached passphrase expire? */


static char SmimeKeyToUse[_POSIX_PATH_MAX] = { 0 };
static char SmimeCertToUse[_POSIX_PATH_MAX];
static char SmimeIntermediateToUse[_POSIX_PATH_MAX];


/*
 *     Queries and passphrase handling.
 */




/* these are copies from pgp.c */


void smime_void_passphrase (void)
{
  memset (SmimePass, 0, sizeof (SmimePass));
  SmimeExptime = 0;
}

int smime_valid_passphrase (void)
{
  time_t now = time (NULL);

  if (now < SmimeExptime)
    /* Use cached copy.  */
    return 1;

  smime_void_passphrase ();

  if (mutt_get_field_unbuffered (_("Enter S/MIME passphrase:"), SmimePass,
                                 sizeof (SmimePass), M_PASS) == 0) {
    SmimeExptime = time (NULL) + SmimeTimeout;
    return (1);
  }
  else
    SmimeExptime = 0;

  return 0;
}


/*
 *     The OpenSSL interface
 */

/* This is almost identical to ppgp's invoking interface. */

static const char *_mutt_fmt_smime_command (char *dest,
                                            size_t destlen,
                                            char op,
                                            const char *src,
                                            const char *prefix,
                                            const char *ifstring,
                                            const char *elsestring,
                                            unsigned long data,
                                            format_flag flags)
{
  char fmt[16];
  struct smime_command_context *cctx = (struct smime_command_context *) data;
  int optional = (flags & M_FORMAT_OPTIONAL);

  switch (op) {
  case 'C':
    {
      if (!optional) {
        char path[_POSIX_PATH_MAX];
        char buf1[LONG_STRING], buf2[LONG_STRING];
        struct stat sb;

        strfcpy (path, NONULL (SmimeCALocation), sizeof (path));
        mutt_expand_path (path, sizeof (path));
        mutt_quote_filename (buf1, sizeof (buf1), path);

        if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
          snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
        else
          snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);

        snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
        snprintf (dest, destlen, fmt, buf2);
      }
      else if (!SmimeCALocation)
        optional = 0;
      break;
    }

  case 'c':
    {                           /* certificate (list) */
      if (!optional) {
        snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
        snprintf (dest, destlen, fmt, NONULL (cctx->certificates));
      }
      else if (!cctx->certificates)
        optional = 0;
      break;
    }

  case 'i':
    {                           /* intermediate certificates  */
      if (!optional) {
        snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
        snprintf (dest, destlen, fmt, NONULL (cctx->intermediates));
      }
      else if (!cctx->intermediates)
        optional = 0;
      break;
    }

  case 's':
    {                           /* detached signature */
      if (!optional) {
        snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
        snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
      }
      else if (!cctx->sig_fname)
        optional = 0;
      break;
    }

  case 'k':
    {                           /* private key */
      if (!optional) {
        snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
        snprintf (dest, destlen, fmt, NONULL (cctx->key));
      }
      else if (!cctx->key)
        optional = 0;
      break;
    }

  case 'a':
    {                           /* algorithm for encryption */
      if (!optional) {
        snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
        snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
      }
      else if (!cctx->key)
        optional = 0;
      break;
    }

  case 'f':
    {                           /* file to process */
      if (!optional) {
        snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
        snprintf (dest, destlen, fmt, NONULL (cctx->fname));
      }
      else if (!cctx->fname)
        optional = 0;
      break;
    }

  default:
    *dest = '\0';
    break;
  }

  if (optional)
    mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
                       data, 0);
  else if (flags & M_FORMAT_OPTIONAL)
    mutt_FormatString (dest, destlen, elsestring, _mutt_fmt_smime_command,
                       data, 0);

  return (src);
}



static void mutt_smime_command (char *d, size_t dlen,
                                struct smime_command_context *cctx,
                                const char *fmt)
{
  mutt_FormatString (d, dlen, NONULL (fmt), _mutt_fmt_smime_command,
                     (unsigned long) cctx, 0);
  debug_print (2, ("%s\n", d));
}

static pid_t smime_invoke (FILE ** smimein, FILE ** smimeout,
                           FILE ** smimeerr, int smimeinfd, int smimeoutfd,
                           int smimeerrfd, const char *fname,
                           const char *sig_fname, const char *cryptalg,
                           const char *key, const char *certificates,
                           const char *intermediates, const char *format)
{
  struct smime_command_context cctx;
  char cmd[HUGE_STRING];

  memset (&cctx, 0, sizeof (cctx));

  if (!format || !*format)
    return (pid_t) - 1;

  cctx.fname = fname;
  cctx.sig_fname = sig_fname;
  cctx.key = key;
  cctx.cryptalg = cryptalg;
  cctx.certificates = certificates;
  cctx.intermediates = intermediates;

  mutt_smime_command (cmd, sizeof (cmd), &cctx, format);

  return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
                                smimeinfd, smimeoutfd, smimeerrfd);
}






/*
 *    Key and certificate handling.
 */



/* 
   Search the certificate index for given mailbox.
   return certificate file name.
*/

static void smime_entry (char *s, size_t l, MUTTMENU * menu, int num)
{
  smime_id *Table = (smime_id *) menu->data;
  smime_id this = Table[num];
  char *truststate;

  switch (this.trust) {
  case 't':
    truststate = N_("Trusted   ");
    break;
  case 'v':
    truststate = N_("Verified  ");
    break;
  case 'u':
    truststate = N_("Unverified");
    break;
  case 'e':
    truststate = N_("Expired   ");
    break;
  case 'r':
    truststate = N_("Revoked   ");
    break;
  case 'i':
    truststate = N_("Invalid   ");
    break;
  default:
    truststate = N_("Unknown   ");
  }
  if (this.public)
    snprintf (s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix,
              truststate, this.email, this.nick);
  else
    snprintf (s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix,
              this.email, this.nick);
}





char *smime_ask_for_key (char *prompt, char *mailbox, short public)
{
  char *fname;
  smime_id *Table;
  long cert_num;                /* Will contain the number of certificates.
                                 * To be able to get it, the .index file will be read twice... */
  char index_file[_POSIX_PATH_MAX];
  FILE *index;
  char buf[LONG_STRING];
  char fields[5][STRING];
  int numFields, hash_suffix, done, cur;        /* The current entry */
  MUTTMENU *menu;
  unsigned int hash;
  char helpstr[HUGE_STRING * 3];
  char qry[256];
  char title[256];

  if (!prompt)
    prompt = _("Enter keyID: ");
  snprintf (index_file, sizeof (index_file), "%s/.index",
            public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys));

  index = fopen (index_file, "r");
  if (index == NULL) {
    mutt_perror (index_file);
    return NULL;
  }
  /* Count Lines */
  cert_num = 0;
  while (!feof (index)) {
    if (fgets (buf, sizeof (buf), index))
      cert_num++;
  }
  fclose (index);

  FOREVER {
    *qry = 0;
    if (mutt_get_field (prompt, qry, sizeof (qry), 0))
      return NULL;
    snprintf (title, sizeof (title),
              _("S/MIME certificates matching \"%s\"."), qry);


    index = fopen (index_file, "r");
    if (index == NULL) {
      mutt_perror (index_file);
      return NULL;
    }
    /* Read Entries */
    cur = 0;
    Table = mem_calloc (cert_num, sizeof (smime_id));
    while (!feof (index)) {
      numFields =
        fscanf (index, MUTT_FORMAT (STRING) " %x.%i " MUTT_FORMAT (STRING),
                fields[0], &hash, &hash_suffix, fields[2]);
      if (public)
        fscanf (index, MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) "\n",
                fields[3], fields[4]);

      /* 0=email 1=name 2=nick 3=intermediate 4=trust */
      if (numFields < 2)
        continue;

      /* Check if query matches this certificate */
      if (!str_isstr (fields[0], qry) && !str_isstr (fields[2], qry))
        continue;

      Table[cur].hash = hash;
      Table[cur].suffix = hash_suffix;
      strncpy (Table[cur].email, fields[0], sizeof (Table[cur].email));
      strncpy (Table[cur].nick, fields[2], sizeof (Table[cur].nick));
      Table[cur].trust = *fields[4];
      Table[cur].public = public;

      cur++;
    }
    fclose (index);

    /* Make Helpstring */
    helpstr[0] = 0;
    mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_SMIME, OP_EXIT);
    strcat (helpstr, buf);      /* __STRCAT_CHECKED__ */
    mutt_make_help (buf, sizeof (buf), _("Select  "), MENU_SMIME,
                    OP_GENERIC_SELECT_ENTRY);
    strcat (helpstr, buf);      /* __STRCAT_CHECKED__ */
    mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
    strcat (helpstr, buf);      /* __STRCAT_CHECKED__ */

    /* Create the menu */
    menu = mutt_new_menu ();
    menu->max = cur;
    menu->make_entry = smime_entry;
    menu->menu = MENU_SMIME;
    menu->help = helpstr;
    menu->data = Table;
    menu->title = title;
    /* sorting keys might be done later - TODO */

    mutt_clear_error ();

    done = 0;
    hash = 0;
    while (!done) {
      switch (mutt_menuLoop (menu)) {
      case OP_GENERIC_SELECT_ENTRY:
        cur = menu->current;
        hash = 1;
        done = 1;
        break;
      case OP_EXIT:
        hash = 0;
        done = 1;
        break;
      }
    }
    if (hash) {
      fname = mem_malloc (13); /* Hash + '.' + Suffix + \0 */
      sprintf (fname, "%.8x.%i", Table[cur].hash, Table[cur].suffix);
    }
    else
      fname = NULL;

    mutt_menuDestroy (&menu);
    mem_free (&Table);
    set_option (OPTNEEDREDRAW);

    if (fname)
      return fname;
  }
}



char *smime_get_field_from_db (char *mailbox, char *query, short public,
                               short may_ask)
{
  int addr_len, query_len, found = 0, ask = 0, choice = 0;
  char cert_path[_POSIX_PATH_MAX];
  char buf[LONG_STRING], prompt[STRING];
  char fields[5][STRING];
  char key[STRING];
  int numFields;
  struct stat info;
  char key_trust_level = 0;
  FILE *fp;

  if (!mailbox && !query)
    return (NULL);

  addr_len = mailbox ? str_len (mailbox) : 0;
  query_len = query ? str_len (query) : 0;

  *key = '\0';

  /* index-file format:
     mailbox certfile label issuer_certfile trust_flags\n

     certfile is a hash value generated by openssl.
     Note that this was done according to the OpenSSL
     specs on their CA-directory.

   */
  snprintf (cert_path, sizeof (cert_path), "%s/.index",
            (public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys)));

  if (!stat (cert_path, &info)) {
    if ((fp = safe_fopen (cert_path, "r")) == NULL) {
      mutt_perror (cert_path);
      return (NULL);
    }

    while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
      if (mailbox && !(str_ncasecmp (mailbox, buf, addr_len))) {
        numFields = sscanf (buf,
                            MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
                            MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
                            MUTT_FORMAT (STRING) "\n",
                            fields[0], fields[1],
                            fields[2], fields[3], fields[4]);
        if (numFields < 2)
          continue;
        if (mailbox && public &&
            (!fields[4] ||
             *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
          continue;

        if (found) {
          if (public && *fields[4] == 'u')
            snprintf (prompt, sizeof (prompt),
                      _
                      ("ID %s is unverified. Do you want to use it for %s ?"),
                      fields[1], mailbox);
          else if (public && *fields[4] == 'v')
            snprintf (prompt, sizeof (prompt),
                      _("Use (untrusted!) ID %s for %s ?"),
                      fields[1], mailbox);
          else
            snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
                      fields[1], mailbox);
          if (may_ask == 0)
            choice = M_YES;
          if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1) {
            found = 0;
            ask = 0;
            *key = '\0';
            break;
          }
          else if (choice == M_NO) {
            ask = 1;
            continue;
          }
          else if (choice == M_YES) {
            strfcpy (key, fields[1], sizeof (key));
            ask = 0;
            break;
          }
        }
        else {
          if (public)
            key_trust_level = *fields[4];
          strfcpy (key, fields[1], sizeof (key));
        }
        found = 1;
      }
      else if (query) {
        numFields = sscanf (buf,
                            MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
                            MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
                            MUTT_FORMAT (STRING) "\n",
                            fields[0], fields[1],
                            fields[2], fields[3], fields[4]);

        /* query = label: return certificate. */
        if (numFields >= 3 &&
            !(str_ncasecmp (query, fields[2], query_len))) {
          ask = 0;
          strfcpy (key, fields[1], sizeof (key));
        }
        /* query = certificate: return intermediate certificate. */
        else if (numFields >= 4 &&
                 !(str_ncasecmp (query, fields[1], query_len))) {
          ask = 0;
          strfcpy (key, fields[3], sizeof (key));
        }
      }

    safe_fclose (&fp);

    if (ask) {
      if (public && *fields[4] == 'u')
        snprintf (prompt, sizeof (prompt),
                  _("ID %s is unverified. Do you want to use it for %s ?"),
                  fields[1], mailbox);
      else if (public && *fields[4] == 'v')
        snprintf (prompt, sizeof (prompt),
                  _("Use (untrusted!) ID %s for %s ?"), fields[1], mailbox);
      else
        snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"), key,
                  mailbox);
      choice = mutt_yesorno (prompt, M_NO);
      if (choice == -1 || choice == M_NO)
        *key = '\0';
    }
    else if (key_trust_level && may_ask) {
      if (key_trust_level == 'u') {
        snprintf (prompt, sizeof (prompt),
                  _("ID %s is unverified. Do you want to use it for %s ?"),
                  key, mailbox);
        choice = mutt_yesorno (prompt, M_NO);
        if (choice != M_YES)
          *key = '\0';
      }
      else if (key_trust_level == 'v') {
        mutt_error (_
                    ("Warning: You have not yet decided to trust ID %s. (any key to continue)"),
                    key);
        mutt_sleep (5);
      }
    }

  }

  /* Note: str_dup ("") returns NULL. */
  return str_dup (key);
}




/* 
   This sets the '*ToUse' variables for an upcoming decryption, where
   the reuquired key is different from SmimeDefaultKey.
*/

void _smime_getkeys (char *mailbox)
{
  char *k = NULL;
  char buf[STRING];

  k = smime_get_field_from_db (mailbox, NULL, 0, 1);

  if (!k) {
    snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), mailbox);
    k = smime_ask_for_key (buf, mailbox, 0);
  }

  if (k) {
    /* the key used last time. */
    if (*SmimeKeyToUse &&
        !str_casecmp (k, SmimeKeyToUse + str_len (SmimeKeys) + 1)) {
      mem_free (&k);
      return;
    }
    else
      smime_void_passphrase ();

    snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
              NONULL (SmimeKeys), k);

    snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
              NONULL (SmimeCertificates), k);

    if (str_casecmp (k, SmimeDefaultKey))
      smime_void_passphrase ();

    mem_free (&k);
    return;
  }

  if (*SmimeKeyToUse) {
    if (!str_casecmp (SmimeDefaultKey,
                          SmimeKeyToUse + str_len (SmimeKeys) + 1))
      return;

    smime_void_passphrase ();
  }

  snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
            NONULL (SmimeKeys), NONULL (SmimeDefaultKey));

  snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
            NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
}

void smime_getkeys (ENVELOPE * env)
{
  ADDRESS *t;
  int found = 0;

  if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey) {
    snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
              NONULL (SmimeKeys), SmimeDefaultKey);

    snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
              NONULL (SmimeCertificates), SmimeDefaultKey);

    return;
  }

  for (t = env->to; !found && t; t = t->next)
    if (mutt_addr_is_user (t)) {
      found = 1;
      _smime_getkeys (t->mailbox);
    }
  for (t = env->cc; !found && t; t = t->next)
    if (mutt_addr_is_user (t)) {
      found = 1;
      _smime_getkeys (t->mailbox);
    }
  if (!found && (t = mutt_default_from ())) {
    _smime_getkeys (t->mailbox);
    rfc822_free_address (&t);
  }
}

/* This routine attempts to find the keyids of the recipients of a message.
 * It returns NULL if any of the keys can not be found.
 */

char *smime_findKeys (ADDRESS * to, ADDRESS * cc, ADDRESS * bcc)
{
  char *keyID, *keylist = NULL;
  size_t keylist_size = 0;
  size_t keylist_used = 0;
  ADDRESS *tmp = NULL, *addr = NULL;
  ADDRESS **last = &tmp;
  ADDRESS *p, *q;
  int i;

  const char *fqdn = mutt_fqdn (1);

  for (i = 0; i < 3; i++) {
    switch (i) {
    case 0:
      p = to;
      break;
    case 1:
      p = cc;
      break;
    case 2:
      p = bcc;
      break;
    default:
      abort ();
    }

    *last = rfc822_cpy_adr (p);
    while (*last)
      last = &((*last)->next);
  }

  if (fqdn)
    rfc822_qualify (tmp, fqdn);

  tmp = mutt_remove_duplicates (tmp);

  for (p = tmp; p; p = p->next) {
    char buf[LONG_STRING];

    q = p;

    if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
      snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
      keyID = smime_ask_for_key (buf, q->mailbox, 1);
    }
    if (!keyID) {
      mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
      mem_free (&keylist);
      rfc822_free_address (&tmp);
      rfc822_free_address (&addr);
      return NULL;
    }

    keylist_size += str_len (keyID) + 2;
    mem_realloc (&keylist, keylist_size);
    sprintf (keylist + keylist_used, "%s\n", keyID);    /* __SPRINTF_CHECKED__ */
    keylist_used = str_len (keylist);

    rfc822_free_address (&addr);

  }
  rfc822_free_address (&tmp);
  return (keylist);
}






static int smime_handle_cert_email (char *certificate, char *mailbox,
                                    int copy, char ***buffer, int *num)
{
  FILE *fpout = NULL, *fperr = NULL;
  char tmpfname[_POSIX_PATH_MAX];
  char email[STRING];
  int ret = -1, count = 0;
  pid_t thepid;

  mutt_mktemp (tmpfname);
  if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
    mutt_perror (tmpfname);
    return 1;
  }
  mutt_unlink (tmpfname);

  mutt_mktemp (tmpfname);
  if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
    fclose (fperr);
    mutt_perror (tmpfname);
    return 1;
  }
  mutt_unlink (tmpfname);

  if ((thepid = smime_invoke (NULL, NULL, NULL,
                              -1, fileno (fpout), fileno (fperr),
                              certificate, NULL, NULL, NULL, NULL, NULL,
                              SmimeGetCertEmailCommand)) == -1) {
    mutt_message (_("Error: unable to create OpenSSL subprocess!"));
    fclose (fperr);
    fclose (fpout);
    return 1;
  }

  mutt_wait_filter (thepid);

  fflush (fpout);
  rewind (fpout);
  rewind (fperr);
  fflush (fperr);


  while ((fgets (email, sizeof (email), fpout))) {
    *(email + str_len (email) - 1) = '\0';
    if (str_ncasecmp (email, mailbox, str_len (mailbox)) == 0)
      ret = 1;

    ret = ret < 0 ? 0 : ret;
    count++;
  }

  if (ret == -1) {
    mutt_endwin (NULL);
    mutt_copy_stream (fperr, stdout);
    mutt_any_key_to_continue (_
                              ("Error: unable to create OpenSSL subprocess!"));
    ret = 1;
  }
  else if (!ret)
    ret = 1;
  else
    ret = 0;

  if (copy && buffer && num) {
    (*num) = count;
    *buffer = mem_calloc (sizeof (char *), count);
    count = 0;

    rewind (fpout);
    while ((fgets (email, sizeof (email), fpout))) {
      *(email + str_len (email) - 1) = '\0';
      (*buffer)[count] = mem_calloc (1, str_len (email) + 1);
      strncpy ((*buffer)[count], email, str_len (email));
      count++;
    }
  }
  else if (copy)
    ret = 2;

  fclose (fpout);
  fclose (fperr);

  return ret;
}



static char *smime_extract_certificate (char *infile)
{
  FILE *fpout = NULL, *fperr = NULL;
  char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
  char tmpfname[_POSIX_PATH_MAX];
  pid_t thepid;
  int empty;


  mutt_mktemp (tmpfname);
  if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
    mutt_perror (tmpfname);
    return NULL;
  }
  mutt_unlink (tmpfname);

  mutt_mktemp (pk7out);
  if ((fpout = safe_fopen (pk7out, "w+")) == NULL) {
    fclose (fperr);
    mutt_perror (pk7out);
    return NULL;
  }

  /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
     extract the full set of certificates directly.
   */
  if ((thepid = smime_invoke (NULL, NULL, NULL,
                              -1, fileno (fpout), fileno (fperr),
                              infile, NULL, NULL, NULL, NULL, NULL,
                              SmimePk7outCommand)) == -1) {
    mutt_any_key_to_continue (_
                              ("Error: unable to create OpenSSL subprocess!"));
    fclose (fperr);
    fclose (fpout);
    mutt_unlink (pk7out);
    return NULL;
  }

  mutt_wait_filter (thepid);


  fflush (fpout);
  rewind (fpout);
  rewind (fperr);
  fflush (fperr);
  empty = (fgetc (fpout) == EOF);
  if (empty) {
    mutt_perror (pk7out);
    mutt_copy_stream (fperr, stdout);
    fclose (fpout);
    fclose (fperr);
    mutt_unlink (pk7out);
    return NULL;

  }


  fclose (fpout);
  mutt_mktemp (certfile);
  if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
    fclose (fperr);
    mutt_unlink (pk7out);
    mutt_perror (certfile);
    return NULL;
  }

  /* Step 2: Extract the certificates from a PKCS#7 structure.
   */
  if ((thepid = smime_invoke (NULL, NULL, NULL,
                              -1, fileno (fpout), fileno (fperr),
                              pk7out, NULL, NULL, NULL, NULL, NULL,
                              SmimeGetCertCommand)) == -1) {
    mutt_any_key_to_continue (_
                              ("Error: unable to create OpenSSL subprocess!"));
    fclose (fperr);
    fclose (fpout);
    mutt_unlink (pk7out);
    mutt_unlink (certfile);
    return NULL;
  }

  mutt_wait_filter (thepid);

  mutt_unlink (pk7out);

  fflush (fpout);
  rewind (fpout);
  rewind (fperr);
  fflush (fperr);
  empty = (fgetc (fpout) == EOF);
  if (empty) {
    mutt_copy_stream (fperr, stdout);
    fclose (fpout);
    fclose (fperr);
    mutt_unlink (certfile);
    return NULL;
  }

  fclose (fpout);
  fclose (fperr);

  return str_dup (certfile);
}

static char *smime_extract_signer_certificate (char *infile)
{
  FILE *fpout = NULL, *fperr = NULL;
  char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
  char tmpfname[_POSIX_PATH_MAX];
  pid_t thepid;
  int empty;


  mutt_mktemp (tmpfname);
  if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
    mutt_perror (tmpfname);
    return NULL;
  }
  mutt_unlink (tmpfname);


  mutt_mktemp (certfile);
  if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
    fclose (fperr);
    mutt_perror (certfile);
    return NULL;
  }

  /* Extract signer's certificate
   */
  if ((thepid = smime_invoke (NULL, NULL, NULL,
                              -1, -1, fileno (fperr),
                              infile, NULL, NULL, NULL, certfile, NULL,
                              SmimeGetSignerCertCommand)) == -1) {
    mutt_any_key_to_continue (_
                              ("Error: unable to create OpenSSL subprocess!"));
    fclose (fperr);
    fclose (fpout);
    mutt_unlink (pk7out);
    mutt_unlink (certfile);
    return NULL;
  }

  mutt_wait_filter (thepid);

  fflush (fpout);
  rewind (fpout);
  rewind (fperr);
  fflush (fperr);
  empty = (fgetc (fpout) == EOF);
  if (empty) {
    mutt_endwin (NULL);
    mutt_copy_stream (fperr, stdout);
    mutt_any_key_to_continue (NULL);
    fclose (fpout);
    fclose (fperr);
    mutt_unlink (certfile);
    return NULL;
  }

  fclose (fpout);
  fclose (fperr);

  return str_dup (certfile);
}




/* Add a certificate and update index file (externally). */

void smime_invoke_import (char *infile, char *mailbox)
{
  char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
  FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
  pid_t thepid = -1;

  mutt_mktemp (tmpfname);
  if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
    mutt_perror (tmpfname);
    return;
  }
  mutt_unlink (tmpfname);

  mutt_mktemp (tmpfname);
  if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
    fclose (fperr);
    mutt_perror (tmpfname);
    return;
  }
  mutt_unlink (tmpfname);


  buf[0] = '\0';
  if (option (OPTASKCERTLABEL))
    mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);

  mutt_endwin (NULL);
  if ((certfile = smime_extract_certificate (infile))) {
    mutt_endwin (NULL);

    if ((thepid = smime_invoke (&smimein, NULL, NULL,
                                -1, fileno (fpout), fileno (fperr),
                                certfile, NULL, NULL, NULL, NULL, NULL,
                                SmimeImportCertCommand)) == -1) {
      mutt_message (_("Error: unable to create OpenSSL subprocess!"));
      return;
    }
    fputs (buf, smimein);
    fputc ('\n', smimein);
    fclose (smimein);

    mutt_wait_filter (thepid);

    mutt_unlink (certfile);
    mem_free (&certfile);
  }

  fflush (fpout);
  rewind (fpout);
  fflush (fperr);
  rewind (fperr);

  mutt_copy_stream (fpout, stdout);
  mutt_copy_stream (fperr, stdout);

  fclose (fpout);
  fclose (fperr);

}



int smime_verify_sender (HEADER * h)
{
  char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
  FILE *fpout;
  int retval = 1;

  mutt_mktemp (tempfname);
  if (!(fpout = safe_fopen (tempfname, "w"))) {
    mutt_perror (tempfname);
    return 1;
  }

  if (h->security & ENCRYPT)
    mutt_copy_message (fpout, Context, h,
                       M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
                       CH_MIME | CH_WEED | CH_NONEWLINE);
  else
    mutt_copy_message (fpout, Context, h, 0, 0);

  fflush (fpout);
  fclose (fpout);

  if (h->env->from) {
    h->env->from = mutt_expand_aliases (h->env->from);
    mbox = h->env->from->mailbox;
  }
  else if (h->env->sender) {
    h->env->sender = mutt_expand_aliases (h->env->sender);
    mbox = h->env->sender->mailbox;
  }

  if (mbox) {
    if ((certfile = smime_extract_signer_certificate (tempfname))) {
      mutt_unlink (tempfname);
      if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
        if (isendwin ())
          mutt_any_key_to_continue (NULL);
      }
      else
        retval = 0;
      mutt_unlink (certfile);
      mem_free (&certfile);
    }
    else
      mutt_any_key_to_continue (_("no certfile"));
  }
  else
    mutt_any_key_to_continue (_("no mbox"));

  mutt_unlink (tempfname);
  return retval;
}









/*
 *    Creating S/MIME - bodies.
 */




static
pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
                            FILE ** smimeerr, int smimeinfd, int smimeoutfd,
                            int smimeerrfd, const char *fname,
                            const char *uids)
{
  return smime_invoke (smimein, smimeout, smimeerr,
                       smimeinfd, smimeoutfd, smimeerrfd,
                       fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
                       SmimeEncryptCommand);
}


static
pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
                         int smimeinfd, int smimeoutfd, int smimeerrfd,
                         const char *fname)
{
  return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
                       smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
                       SmimeCertToUse, SmimeIntermediateToUse,
                       SmimeSignCommand);
}




BODY *smime_build_smime_entity (BODY * a, char *certlist)
{
  char buf[LONG_STRING], certfile[LONG_STRING];
  char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
  char smimeinfile[_POSIX_PATH_MAX];
  char *cert_start = certlist, *cert_end = certlist;
  FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
  BODY *t;
  int err = 0, empty;
  pid_t thepid;

  mutt_mktemp (tempfile);
  if ((fpout = safe_fopen (tempfile, "w+")) == NULL) {
    mutt_perror (tempfile);
    return (NULL);
  }

  mutt_mktemp (smimeerrfile);
  if ((smimeerr = safe_fopen (smimeerrfile, "w+")) == NULL) {
    mutt_perror (smimeerrfile);
    fclose (fpout);
    mutt_unlink (tempfile);
    return NULL;
  }
  mutt_unlink (smimeerrfile);

  mutt_mktemp (smimeinfile);
  if ((fptmp = safe_fopen (smimeinfile, "w+")) == NULL) {
    mutt_perror (smimeinfile);
    mutt_unlink (tempfile);
    fclose (fpout);
    fclose (smimeerr);
    return NULL;
  }

  *certfile = '\0';
  while (1) {
    int off = str_len (certfile);

    while (*++cert_end && *cert_end != '\n');
    if (!*cert_end)
      break;
    *cert_end = '\0';
    snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
              NONULL (SmimeCertificates), cert_start);
    *cert_end = '\n';
    cert_start = cert_end;
    cert_start++;
  }

  /* write a MIME entity */
  mutt_write_mime_header (a, fptmp);
  fputc ('\n', fptmp);
  mutt_write_mime_body (a, fptmp);
  fclose (fptmp);

  if ((thepid =
       smime_invoke_encrypt (&smimein, NULL, NULL, -1,
                             fileno (fpout), fileno (smimeerr),
                             smimeinfile, certfile)) == -1) {
    fclose (smimeerr);
    mutt_unlink (smimeinfile);
    mutt_unlink (certfile);
    return (NULL);
  }

  fclose (smimein);

  mutt_wait_filter (thepid);
  mutt_unlink (smimeinfile);
  mutt_unlink (certfile);

  fflush (fpout);
  rewind (fpout);
  empty = (fgetc (fpout) == EOF);
  fclose (fpout);

  fflush (smimeerr);
  rewind (smimeerr);
  while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
    err = 1;
    fputs (buf, stdout);
  }
  fclose (smimeerr);

  /* pause if there is any error output from SMIME */
  if (err)
    mutt_any_key_to_continue (NULL);

  if (empty) {
    /* fatal error while trying to encrypt message */
    if (!err)
      mutt_any_key_to_continue _("No output from OpenSSL..");

    mutt_unlink (tempfile);
    return (NULL);
  }

  t = mutt_new_body ();
  t->type = TYPEAPPLICATION;
  t->subtype = str_dup ("x-pkcs7-mime");
  mutt_set_parameter ("name", "smime.p7m", &t->parameter);
  mutt_set_parameter ("smime-type", "enveloped-data", &t->parameter);
  t->encoding = ENCBASE64;      /* The output of OpenSSL SHOULD be binary */
  t->use_disp = 1;
  t->disposition = DISPATTACH;
  t->d_filename = str_dup ("smime.p7m");
  t->filename = str_dup (tempfile);
  t->unlink = 1;                /*delete after sending the message */
  t->parts = 0;
  t->next = 0;

  return (t);
}




BODY *smime_sign_message (BODY * a)
{
  BODY *t;
  char buffer[LONG_STRING];
  char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
  FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
  int err = 0;
  int empty = 0;
  pid_t thepid;
  char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);

  if (!intermediates) {
    mutt_message (_("Warning: Intermediate certificate not found."));
    intermediates = SmimeDefaultKey;    /* so openssl won't complain in any case */
  }

  convert_to_7bit (a);          /* Signed data _must_ be in 7-bit format. */

  mutt_mktemp (filetosign);
  if ((sfp = safe_fopen (filetosign, "w+")) == NULL) {
    mutt_perror (filetosign);
    return NULL;
  }

  mutt_mktemp (signedfile);
  if ((smimeout = safe_fopen (signedfile, "w+")) == NULL) {
    mutt_perror (signedfile);
    fclose (sfp);
    mutt_unlink (filetosign);
    return NULL;
  }

  mutt_write_mime_header (a, sfp);
  fputc ('\n', sfp);
  mutt_write_mime_body (a, sfp);
  fclose (sfp);



  snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
            NONULL (SmimeKeys), SmimeDefaultKey);

  snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
            NONULL (SmimeCertificates), SmimeDefaultKey);

  snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
            NONULL (SmimeCertificates), intermediates);



  if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
                                   -1, fileno (smimeout), -1,
                                   filetosign)) == -1) {
    mutt_perror (_("Can't open OpenSSL subprocess!"));

    fclose (smimeout);
    mutt_unlink (signedfile);
    mutt_unlink (filetosign);
    return NULL;
  }
  fputs (SmimePass, smimein);
  fputc ('\n', smimein);
  fclose (smimein);


  mutt_wait_filter (thepid);

  /* check for errors from OpenSSL */
  err = 0;
  fflush (smimeerr);
  rewind (smimeerr);
  while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
    err = 1;
    fputs (buffer, stdout);
  }
  fclose (smimeerr);


  fflush (smimeout);
  rewind (smimeout);
  empty = (fgetc (smimeout) == EOF);
  fclose (smimeout);

  mutt_unlink (filetosign);


  if (err)
    mutt_any_key_to_continue (NULL);

  if (empty) {
    mutt_any_key_to_continue _("No output from OpenSSL...");

    mutt_unlink (signedfile);
    return (NULL);              /* fatal error while signing */
  }

  t = mutt_new_body ();
  t->type = TYPEMULTIPART;
  t->subtype = str_dup ("signed");
  t->encoding = ENC7BIT;
  t->use_disp = 0;
  t->disposition = DISPINLINE;

  mutt_generate_boundary (&t->parameter);
  /* check if this can be extracted from private key somehow.... */
  mutt_set_parameter ("micalg", "sha1", &t->parameter);
  mutt_set_parameter ("protocol", "application/x-pkcs7-signature",
                      &t->parameter);

  t->parts = a;
  a = t;

  t->parts->next = mutt_new_body ();
  t = t->parts->next;
  t->type = TYPEAPPLICATION;
  t->subtype = str_dup ("x-pkcs7-signature");
  t->filename = str_dup (signedfile);
  t->d_filename = str_dup ("smime.p7s");
  t->use_disp = 1;
  t->disposition = DISPATTACH;
  t->encoding = ENCBASE64;
  t->unlink = 1;                /* ok to remove this file after sending. */

  return (a);

}






/*
 *    Handling S/MIME - bodies.
 */






static
pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
                           FILE ** smimeerr, int smimeinfd, int smimeoutfd,
                           int smimeerrfd, const char *fname,
                           const char *sig_fname, int opaque)
{
  return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
                       smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
                       (opaque ? SmimeVerifyOpaqueCommand :
                        SmimeVerifyCommand));
}


static
pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
                            FILE ** smimeerr, int smimeinfd, int smimeoutfd,
                            int smimeerrfd, const char *fname)
{
  return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
                       smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
                       SmimeCertToUse, NULL, SmimeDecryptCommand);
}



int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
{
  char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
  FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
  pid_t thepid;
  int badsig = -1;

  long tmpoffset = 0;
  size_t tmplength = 0;
  int origType = sigbdy->type;
  char *savePrefix = NULL;


  snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);

  /* decode to a tempfile, saving the original destination */
  fp = s->fpout;
  if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
    mutt_perror (signedfile);
    return -1;
  }
  /* decoding the attachment changes the size and offset, so save a copy
   * of the "real" values now, and restore them after processing
   */
  tmplength = sigbdy->length;
  tmpoffset = sigbdy->offset;

  /* if we are decoding binary bodies, we don't want to prefix each
   * line with the prefix or else the data will get corrupted.
   */
  savePrefix = s->prefix;
  s->prefix = NULL;

  mutt_decode_attachment (sigbdy, s);

  sigbdy->length = ftello (s->fpout);
  sigbdy->offset = 0;
  fclose (s->fpout);

  /* restore final destination and substitute the tempfile for input */
  s->fpout = fp;
  fp = s->fpin;
  s->fpin = fopen (signedfile, "r");

  /* restore the prefix */
  s->prefix = savePrefix;

  sigbdy->type = origType;


  mutt_mktemp (smimeerrfile);
  if (!(smimeerr = safe_fopen (smimeerrfile, "w+"))) {
    mutt_perror (smimeerrfile);
    mutt_unlink (signedfile);
    return -1;
  }

  crypt_current_time (s, "OpenSSL");

  if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
                                     -1, -1, fileno (smimeerr),
                                     tempfile, signedfile, 0)) != -1) {
    fflush (smimeout);
    fclose (smimeout);

    if (mutt_wait_filter (thepid))
      badsig = -1;
    else {
      char *line = NULL;
      int lineno = 0;
      size_t linelen;

      fflush (smimeerr);
      rewind (smimeerr);

      line = mutt_read_line (line, &linelen, smimeerr, &lineno);
      if (linelen && !str_casecmp (line, "verification successful"))
        badsig = 0;

      mem_free (&line);
    }
  }

  fflush (smimeerr);
  rewind (smimeerr);
  mutt_copy_stream (smimeerr, s->fpout);
  fclose (smimeerr);

  state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);

  mutt_unlink (signedfile);
  mutt_unlink (smimeerrfile);

  sigbdy->length = tmplength;
  sigbdy->offset = tmpoffset;

  /* restore the original source stream */
  fclose (s->fpin);
  s->fpin = fp;


  return badsig;
}





/*
  This handles application/pkcs7-mime which can either be a signed
  or an encrypted message.
*/

static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
{
  int len = 0;
  int c;
  long last_pos;
  char buf[HUGE_STRING];
  char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
  char tmpfname[_POSIX_PATH_MAX];
  char tmptmpfname[_POSIX_PATH_MAX];
  FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
  FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
  struct stat info;
  BODY *p = NULL;
  pid_t thepid = -1;
  unsigned int type = mutt_is_application_smime (m);

  if (!(type & APPLICATION_SMIME))
    return NULL;

  mutt_mktemp (outfile);
  if ((smimeout = safe_fopen (outfile, "w+")) == NULL) {
    mutt_perror (outfile);
    return NULL;
  }

  mutt_mktemp (errfile);
  if ((smimeerr = safe_fopen (errfile, "w+")) == NULL) {
    mutt_perror (errfile);
    fclose (smimeout);
    smimeout = NULL;
    return NULL;
  }
  mutt_unlink (errfile);


  mutt_mktemp (tmpfname);
  if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL) {
    mutt_perror (tmpfname);
    fclose (smimeout);
    smimeout = NULL;
    fclose (smimeerr);
    smimeerr = NULL;
    return NULL;
  }

  fseeko (s->fpin, m->offset, 0);
  last_pos = m->offset;

  mutt_copy_bytes (s->fpin, tmpfp, m->length);

  fflush (tmpfp);
  fclose (tmpfp);

  if ((type & ENCRYPT) &&
      (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
                                      fileno (smimeout), fileno (smimeerr),
                                      tmpfname)) == -1) {
    fclose (smimeout);
    smimeout = NULL;
    mutt_unlink (tmpfname);
    if (s->flags & M_DISPLAY)
      state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
    return NULL;
  }
  else if ((type & SIGNOPAQUE) &&
           (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
                                          fileno (smimeout),
                                          fileno (smimeerr), NULL, tmpfname,
                                          SIGNOPAQUE)) == -1) {
    fclose (smimeout);
    smimeout = NULL;
    mutt_unlink (tmpfname);
    if (s->flags & M_DISPLAY)
      state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
    return NULL;
  }


  if (type & ENCRYPT) {
    if (!smime_valid_passphrase ())
      smime_void_passphrase ();
    fputs (SmimePass, smimein);
    fputc ('\n', smimein);
  }

  fclose (smimein);

  mutt_wait_filter (thepid);
  mutt_unlink (tmpfname);


  if (s->flags & M_DISPLAY) {
    rewind (smimeerr);

    if ((c = fgetc (smimeerr)) != EOF) {
      ungetc (c, smimeerr);

      crypt_current_time (s, "OpenSSL");
      mutt_copy_stream (smimeerr, s->fpout);
      state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
    }

    if (type & ENCRYPT)
      state_attach_puts (_("[-- The following data is S/MIME"
                           " encrypted --]\n"), s);
    else
      state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
                         s);
  }

  if (smimeout) {
    fflush (smimeout);
    rewind (smimeout);

    if (outFile)
      fpout = outFile;
    else {
      mutt_mktemp (tmptmpfname);
      if ((fpout = safe_fopen (tmptmpfname, "w+")) == NULL) {
        mutt_perror (tmptmpfname);
        fclose (smimeout);
        smimeout = NULL;
        return NULL;
      }
    }
    while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
      len = str_len (buf);
      if (len > 1 && buf[len - 2] == '\r') {
        buf[len - 2] = '\n';
        buf[len - 1] = '\0';
      }
      fputs (buf, fpout);
    }
    fflush (fpout);
    rewind (fpout);


    if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
      fstat (fileno (fpout), &info);
      p->length = info.st_size - p->offset;

      mutt_parse_part (fpout, p);
      if (s->fpout) {
        rewind (fpout);
        tmpfp_buffer = s->fpin;
        s->fpin = fpout;
        mutt_body_handler (p, s);
        s->fpin = tmpfp_buffer;
      }

    }
    fclose (smimeout);
    smimeout = NULL;
    mutt_unlink (outfile);

    if (!outFile) {
      fclose (fpout);
      mutt_unlink (tmptmpfname);
    }
    fpout = NULL;
  }

  if (s->flags & M_DISPLAY) {
    if (type & ENCRYPT)
      state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
    else
      state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
  }

  if (type & SIGNOPAQUE) {
    char *line = NULL;
    int lineno = 0;
    size_t linelen;

    rewind (smimeerr);

    line = mutt_read_line (line, &linelen, smimeerr, &lineno);
    if (linelen && !str_casecmp (line, "verification successful"))
      m->goodsig = 1;
    mem_free (&line);
  }
  else {
    m->goodsig = p->goodsig;
    m->badsig = p->badsig;
  }
  fclose (smimeerr);

  return (p);
}





int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
{


  char tempfile[_POSIX_PATH_MAX];
  STATE s;
  long tmpoffset = b->offset;
  size_t tmplength = b->length;
  int origType = b->type;
  FILE *tmpfp = NULL;
  int rv = 0;

  if (!mutt_is_application_smime (b))
    return -1;

  if (b->parts)
    return -1;

  memset (&s, 0, sizeof (s));
  s.fpin = fpin;
  fseeko (s.fpin, b->offset, 0);

  mutt_mktemp (tempfile);
  if ((tmpfp = safe_fopen (tempfile, "w+")) == NULL) {
    mutt_perror (tempfile);
    return (-1);
  }

  mutt_unlink (tempfile);
  s.fpout = tmpfp;
  mutt_decode_attachment (b, &s);
  fflush (tmpfp);
  b->length = ftello (s.fpout);
  b->offset = 0;
  rewind (tmpfp);
  s.fpin = tmpfp;
  s.fpout = 0;

  mutt_mktemp (tempfile);
  if ((*fpout = safe_fopen (tempfile, "w+")) == NULL) {
    mutt_perror (tempfile);
    rv = -1;
    goto bail;
  }
  mutt_unlink (tempfile);

  if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
    rv = -1;
    goto bail;
  }

  (*cur)->goodsig = b->goodsig;
  (*cur)->badsig  = b->badsig;

bail:
  b->type = origType;
  b->length = tmplength;
  b->offset = tmpoffset;

  safe_fclose (&tmpfp);
  if (*fpout)
    rewind (*fpout);
  return (rv);
}


int smime_application_smime_handler (BODY * m, STATE * s)
{
  return smime_handle_entity (m, s, NULL) ? 0 : -1;
}

int smime_send_menu (HEADER * msg, int *redraw)
{
  char *p;

  if (!(WithCrypto & APPLICATION_SMIME))
    return msg->security;

  switch (mutt_multi_choice
          (_
           ("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
           _("eswabfc"))) {
  case 1:                      /* (e)ncrypt */
    msg->security |= ENCRYPT;
    msg->security &= ~SIGN;
    break;

  case 3:                      /* encrypt (w)ith */
    {
      int choice = 0;
      msg->security |= ENCRYPT;

      do {
        /* I use "dra" because "123" is recognized anyway */
        switch (mutt_multi_choice (_("Choose algorithm family:"
                                     " 1: DES, 2: RC2, 3: AES,"
                                     " or (c)lear? "), _("drac"))) {
          case 1:
            switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "),
                                                _("dt"))) {
              case 1:
                str_replace (&SmimeCryptAlg, "des");
                break;
              case 2:
                str_replace (&SmimeCryptAlg, "des3");
                break;
            }
            break;

          case 2:
            switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "),
                                                _("468"))) {
              case 1:
                str_replace (&SmimeCryptAlg, "rc2-40");
                break;
              case 2:
                str_replace (&SmimeCryptAlg, "rc2-64");
                break;
              case 3:
                str_replace (&SmimeCryptAlg, "rc2-128");
                break;
            }
            break;

          case 3:
            switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "),
                                                _("895"))) {
              case 1:
                str_replace (&SmimeCryptAlg, "aes128");
                break;
              case 2:
                str_replace (&SmimeCryptAlg, "aes192");
                break;
              case 3:
                str_replace (&SmimeCryptAlg, "aes256");
                break;
            }
            break;

          case 4: /* (c)lear */
            mem_free (&SmimeCryptAlg);
            /* fallback */
          case -1: /* Ctrl-G or Enter */
            choice = 0;
            break;
        }
      } while (choice == -1);
    }
    break;

  case 2:                      /* (s)ign */

    if (!SmimeDefaultKey)
      mutt_message (_("Can't sign: No key specified. Use Sign As."));

    else {
      msg->security |= SIGN;
      msg->security &= ~ENCRYPT;
    }
    break;

  case 4:                      /* sign (a)s */

    if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
      str_replace (&SmimeDefaultKey, p);

      msg->security |= SIGN;

      /* probably need a different passphrase */
      crypt_smime_void_passphrase ();
    }
#if 0
    else
      msg->security &= ~SIGN;
#endif

    *redraw = REDRAW_FULL;
    break;

  case 5:                      /* (b)oth */
    msg->security |= (ENCRYPT | SIGN);
    break;

  case 6:                      /* (f)orget it */
  case 7:                      /* (c)lear */
    msg->security = 0;
    break;
  }

  if (msg->security && msg->security != APPLICATION_SMIME)
    msg->security |= APPLICATION_SMIME;
  else
    msg->security = 0;

  return (msg->security);
}


#endif /* CRYPT_BACKEND_CLASSIC_SMIME */


syntax highlighted by Code2HTML, v. 0.9.1