summaryrefslogtreecommitdiffstats
path: root/pgp.c
diff options
context:
space:
mode:
Diffstat (limited to 'pgp.c')
-rw-r--r--pgp.c1278
1 files changed, 1278 insertions, 0 deletions
diff --git a/pgp.c b/pgp.c
new file mode 100644
index 00000000..881f325a
--- /dev/null
+++ b/pgp.c
@@ -0,0 +1,1278 @@
+/*
+ * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file contains all of the PGP routines necessary to sign, encrypt,
+ * verify and decrypt PGP messages in either the new PGP/MIME format, or
+ * in the older Application/Pgp format. It also contains some code to
+ * cache the user's passphrase for repeat use when decrypting or signing
+ * a message.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "pgp.h"
+#include "mime.h"
+#include "parse.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 _PGPPATH
+
+
+char PgpPass[STRING];
+static time_t PgpExptime = 0; /* when does the cached passphrase expire? */
+
+static struct pgp_vinfo pgp_vinfo[] =
+{
+ { PGP_V2, "pgp2", &PgpV2, &PgpV2Pubring, &PgpV2Secring, &PgpV2Language },
+ { PGP_V3, "pgp3", &PgpV3, &PgpV3Pubring, &PgpV3Secring, &PgpV3Language },
+ { PGP_V3, "pgp5", &PgpV3, &PgpV3Pubring, &PgpV3Secring, &PgpV3Language },
+ { PGP_UNKNOWN, NULL, NULL, NULL, NULL, NULL}
+};
+
+static struct
+{
+ enum pgp_ops op;
+ char **str;
+}
+pgp_opvers[] =
+{
+ { PGP_DECODE, &PgpReceiveVersion },
+ { PGP_VERIFY, &PgpReceiveVersion },
+ { PGP_DECRYPT, &PgpReceiveVersion },
+ { PGP_SIGN, &PgpSendVersion },
+ { PGP_ENCRYPT, &PgpSendVersion },
+ { PGP_VERIFY_KEY, &PgpSendVersion },
+ { PGP_EXTRACT, &PgpKeyVersion },
+ { PGP_EXTRACT_KEY, &PgpKeyVersion },
+ { PGP_LAST_OP, NULL }
+};
+
+
+
+void pgp_void_passphrase (void)
+{
+ memset (PgpPass, 0, sizeof (PgpPass));
+ PgpExptime = 0;
+}
+
+int pgp_valid_passphrase (void)
+{
+ time_t now = time (NULL);
+
+ if (now < PgpExptime) return 1; /* just use the cached copy. */
+ pgp_void_passphrase ();
+
+ if (mutt_get_password ("Enter PGP passphrase:", PgpPass, sizeof (PgpPass)) == 0)
+ {
+ PgpExptime = time (NULL) + PgpTimeout;
+ return (1);
+ }
+ else
+ {
+ PgpExptime = 0;
+ return (0);
+ }
+ /* not reached */
+}
+
+void mutt_forget_passphrase (void)
+{
+ pgp_void_passphrase ();
+ mutt_message ("PGP passphrase forgotten.");
+}
+
+
+struct pgp_vinfo *pgp_get_vinfo(enum pgp_ops op)
+{
+ int i;
+ char *version = "default";
+
+ for(i = 0; pgp_opvers[i].op != PGP_LAST_OP; i++)
+ {
+ if(pgp_opvers[i].op == op)
+ {
+ version = *pgp_opvers[i].str;
+ break;
+ }
+ }
+
+ if (!strcasecmp(version, "default"))
+ version = PgpDefaultVersion;
+
+ for(i = 0; pgp_vinfo[i].name; i++)
+ {
+ if(!strcasecmp(pgp_vinfo[i].name, version))
+ return &pgp_vinfo[i];
+ }
+
+ return NULL;
+}
+
+enum pgp_version pgp_version(enum pgp_ops op)
+{
+ struct pgp_vinfo *info = pgp_get_vinfo(op);
+ return info ? info->v : PGP_UNKNOWN;
+}
+
+
+char *pgp_getring (enum pgp_ops op, int pub)
+{
+ struct pgp_vinfo *info = pgp_get_vinfo(op);
+ if(!info) return NULL;
+ return pub ? *info->pubring : *info->secring;
+}
+
+char *pgp_binary(enum pgp_ops op)
+{
+ struct pgp_vinfo *info = pgp_get_vinfo(op);
+ return info ? *info->binary : NULL;
+}
+
+
+char *pgp_keyid(KEYINFO *k)
+{
+ if((k->flags & KEYFLAG_SUBKEY) && k->mainkey)
+ k = k->mainkey;
+
+ return _pgp_keyid(k);
+}
+
+char *_pgp_keyid(KEYINFO *k)
+{
+ if(option(OPTPGPLONGIDS))
+ return k->keyid;
+ else
+ return (k->keyid + 8);
+}
+
+/* ----------------------------------------------------------------------------
+ * Routines for handing PGP input.
+ */
+
+/* print the current time to avoid spoofing of the signature output */
+static void pgp_current_time (STATE *s)
+{
+ time_t t;
+ char p[STRING];
+
+ state_puts ("[-- PGP output follows (current time: ", s);
+
+ t = time (NULL);
+ strfcpy (p, asctime (localtime (&t)), sizeof (p));
+ p[strlen (p) - 1] = 0; /* kill the newline */
+ state_puts (p, s);
+
+ state_puts (") --]\n", s);
+}
+
+
+/* Support for the Application/PGP Content Type. */
+
+void application_pgp_handler (BODY *m, STATE *s)
+{
+ int needpass = -1, pgp_keyblock = 0;
+ int clearsign = 0;
+ long start_pos = 0;
+ long bytes, last_pos, offset;
+ char buf[HUGE_STRING];
+ char outfile[_POSIX_PATH_MAX];
+ char tmpfname[_POSIX_PATH_MAX];
+ FILE *pgpout = NULL, *pgpin, *pgperr;
+ FILE *tmpfp;
+ pid_t thepid;
+
+
+ fseek (s->fpin, m->offset, 0);
+ last_pos = m->offset;
+
+ for (bytes = m->length; bytes > 0;)
+ {
+ if (fgets (buf, sizeof (buf) - 1, s->fpin) == NULL)
+ break;
+ offset = ftell (s->fpin);
+ bytes -= (offset - last_pos); /* don't rely on strlen(buf) */
+ last_pos = offset;
+
+ if (strncmp ("-----BEGIN PGP ", buf, 15) == 0)
+ {
+ clearsign = 0;
+ start_pos = last_pos;
+
+ if (strcmp ("MESSAGE-----\n", buf + 15) == 0)
+ needpass = 1;
+ else if (strcmp ("SIGNED MESSAGE-----\n", buf + 15) == 0)
+ {
+ clearsign = 1;
+ needpass = 0;
+ }
+ else if (!option(OPTDONTHANDLEPGPKEYS) &&
+ strcmp ("PUBLIC KEY BLOCK-----\n", buf + 15) == 0)
+ {
+ needpass = 0;
+ pgp_keyblock =1;
+ }
+ else
+ {
+ if (s->prefix)
+ state_puts (s->prefix, s);
+ state_puts (buf, s);
+ continue;
+ }
+
+ if(!clearsign || s->flags & M_VERIFY)
+ {
+
+ /* invoke PGP */
+
+ mutt_mktemp (outfile);
+ if ((pgpout = safe_fopen (outfile, "w+")) == NULL)
+ {
+ mutt_perror (outfile);
+ return;
+ }
+
+ mutt_mktemp (tmpfname);
+ if ((tmpfp = safe_fopen(tmpfname, "w+")) == NULL)
+ {
+ mutt_perror(tmpfname);
+ fclose(pgpout); pgpout = NULL;
+ return;
+ }
+
+ fputs (buf, tmpfp);
+ while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL)
+ {
+ offset = ftell (s->fpin);
+ bytes -= (offset - last_pos); /* don't rely on strlen(buf) */
+ last_pos = offset;
+
+ fputs (buf, tmpfp);
+ if ((needpass && strcmp ("-----END PGP MESSAGE-----\n", buf) == 0) ||
+ (!needpass
+ && (strcmp ("-----END PGP SIGNATURE-----\n", buf) == 0
+ || strcmp ("-----END PGP PUBLIC KEY BLOCK-----\n",buf) == 0)))
+ break;
+ }
+
+ fclose(tmpfp);
+
+ if ((thepid = pgp_invoke_decode (&pgpin, NULL, &pgperr, -1,
+ fileno (pgpout), -1, tmpfname, needpass)) == -1)
+ {
+ fclose (pgpout); pgpout = NULL;
+ mutt_unlink(tmpfname);
+ state_puts ("[-- Error: unable to create PGP subprocess --]\n", s);
+ state_puts (buf, s);
+ continue;
+ }
+
+ if (needpass)
+ {
+ if (!pgp_valid_passphrase ())
+ pgp_void_passphrase ();
+ fputs (PgpPass, pgpin);
+ fputc ('\n', pgpin);
+ }
+
+ fclose (pgpin);
+
+ if (s->flags & M_DISPLAY)
+ pgp_current_time (s);
+
+ mutt_wait_filter (thepid);
+
+ mutt_unlink(tmpfname);
+
+ if (s->flags & M_DISPLAY)
+ mutt_copy_stream(pgperr, s->fpout);
+ fclose (pgperr);
+
+ if (s->flags & M_DISPLAY)
+ state_puts ("\n[-- End of PGP output --]\n\n", s);
+ }
+
+ if(s->flags & M_DISPLAY)
+ {
+ if (needpass)
+ state_puts ("[-- BEGIN PGP MESSAGE --]\n\n", s);
+ else if (pgp_keyblock)
+ state_puts ("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n", s);
+ else
+ state_puts ("[-- BEGIN PGP SIGNED MESSAGE --]\n\n", s);
+ }
+
+ /* Use PGP's output if there was no clearsig signature. */
+
+ if(!clearsign)
+ {
+ fflush (pgpout);
+ rewind (pgpout);
+ while (fgets (buf, sizeof (buf) - 1, pgpout) != NULL)
+ {
+ if (s->prefix)
+ state_puts (s->prefix, s);
+ state_puts (buf, s);
+ }
+ }
+
+ /* Close the temporary files iff they were created.
+ * The condition used to be !clearsign || s->flags & M_VERIFY,
+ * but gcc would complain then.
+ */
+
+ if(pgpout)
+ {
+ fclose (pgpout);
+ pgpout = NULL;
+ mutt_unlink(outfile);
+ }
+
+ /* decode clearsign stuff */
+
+ if(clearsign)
+ {
+
+ /* rationale: We want PGP's error messages, but in the times
+ * of PGP 5.0 we can't rely on PGP to do the dash
+ * escape decoding - so we have to do this
+ * ourselves.
+ */
+
+ int armor_header = 1;
+ int complete = 1;
+
+ fseek(s->fpin, start_pos, SEEK_SET);
+ bytes += (last_pos - start_pos);
+ last_pos = start_pos;
+ offset = start_pos;
+ while(bytes > 0 && fgets(buf, sizeof(buf) - 1, s->fpin) != NULL)
+ {
+ offset = ftell(s->fpin);
+ bytes -= (offset - last_pos);
+ last_pos = offset;
+
+ if(complete)
+ {
+ if (!strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
+ break;
+
+ if(armor_header)
+ {
+ if(*buf == '\n')
+ armor_header = 0;
+ }
+ else
+ {
+ if(s->prefix)
+ state_puts(s->prefix, s);
+
+ if(buf[0] == '-' && buf [1] == ' ')
+ state_puts(buf + 2, s);
+ else
+ state_puts(buf, s);
+ }
+ }
+ else
+ {
+ if(!armor_header)
+ state_puts(buf, s);
+ }
+
+ complete = strchr(buf, '\n') != NULL;
+ }
+
+ if (complete && !strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
+ {
+ while(bytes > 0 && fgets(buf, sizeof(buf) - 1, s->fpin) != NULL)
+ {
+ offset = ftell(s->fpin);
+ bytes -= (offset - last_pos);
+ last_pos = offset;
+
+ if(complete && !strcmp(buf, "-----END PGP SIGNATURE-----\n"))
+ break;
+
+ complete = strchr(buf, '\n') != NULL;
+ }
+ }
+ }
+
+ if (s->flags & M_DISPLAY)
+ {
+ if (needpass)
+ state_puts ("\n[-- END PGP MESSAGE --]\n", s);
+ else if (pgp_keyblock)
+ state_puts ("[-- END PGP PUBLIC KEY BLOCK --]\n", s);
+ else
+ state_puts ("\n[-- END PGP SIGNED MESSAGE --]\n", s);
+ }
+ }
+ else
+ {
+ if (s->prefix)
+ state_puts (s->prefix, s);
+ state_puts (buf, s);
+ }
+ }
+
+ if (needpass == -1)
+ {
+ state_puts ("[-- Error! Could not find beginning of PGP message! --]\n\n", s);
+ return;
+ }
+
+}
+
+int pgp_query (BODY *m)
+{
+ char *p;
+ int t = 0;
+
+ /* Check for old-style APPLICATION/PGP messages */
+ if (m->type == TYPEAPPLICATION)
+ {
+ if (!strcasecmp (m->subtype, "pgp") || !strcasecmp (m->subtype, "x-pgp-message"))
+ {
+ if ((p = mutt_get_parameter ("x-action", m->parameter))
+ && (!strcasecmp (p, "sign") || !strcasecmp (p, "signclear")))
+ t |= PGPSIGN;
+ else if((p = mutt_get_parameter ("format", m->parameter))
+ && !strcasecmp(p, "keys-only"))
+ t |= PGPKEY;
+
+ if ((p = mutt_get_parameter ("format", m->parameter)) &&
+ !strcasecmp (p, "keys-only"))
+ t |= PGPKEY;
+
+ if(!t) t |= PGPENCRYPT; /* not necessarily correct, but... */
+ }
+
+ if (!strcasecmp (m->subtype, "pgp-signed"))
+ t |= PGPSIGN;
+
+ if (!strcasecmp (m->subtype, "pgp-keys"))
+ t |= PGPKEY;
+ }
+
+ /* Check for PGP/MIME messages. */
+ if (m->type == TYPEMULTIPART)
+ {
+ if (strcasecmp (m->subtype, "signed") == 0 &&
+ (p = mutt_get_parameter("protocol", m->parameter)) &&
+ strcasecmp (p, "application/pgp-signature") == 0)
+ t |= PGPSIGN;
+ else if ((strcasecmp (m->subtype, "encrypted") == 0) &&
+ (p = mutt_get_parameter ("protocol", m->parameter)) &&
+ strcasecmp (p, "application/pgp-encrypted") == 0)
+ t |= PGPENCRYPT;
+ }
+
+ if(m->type == TYPEMULTIPART || m->type == TYPEMESSAGE)
+ {
+ BODY *p;
+
+ for(p = m->parts; p; p = p->next)
+ t |= pgp_query(p);
+ }
+
+ return t;
+}
+
+/*
+ * This routine verifies a PGP/MIME signed body.
+ */
+void pgp_signed_handler (BODY *a, STATE *s)
+{
+ FILE *fp, *pgpout, *pgperr;
+ char tempfile[_POSIX_PATH_MAX], sigfile[_POSIX_PATH_MAX],
+ pgperrfile[_POSIX_PATH_MAX];
+ int bytes;
+ int hadcr;
+ int c;
+ pid_t thepid;
+
+ a = a->parts;
+
+ /* First do some error checking to make sure that this looks like a valid
+ * multipart/signed body.
+ */
+ if (a && a->next && a->next->type == TYPEAPPLICATION && a->next->subtype &&
+ strcasecmp (a->next->subtype, "pgp-signature") == 0)
+ {
+ if (s->flags & M_DISPLAY)
+ {
+ mutt_mktemp (tempfile);
+
+ if(!(fp = safe_fopen (tempfile, "w")))
+ {
+ mutt_perror(tempfile);
+ return;
+ }
+
+ fseek (s->fpin, a->hdr_offset, 0);
+ bytes = a->length + a->offset - a->hdr_offset;
+ hadcr = 0;
+ while (bytes > 0)
+ {
+ if((c = fgetc(s->fpin)) == EOF)
+ break;
+
+ bytes--;
+
+ if(c == '\r')
+ hadcr = 1;
+ else
+ {
+ if(c == '\n' && !hadcr)
+ fputc('\r', fp);
+
+ hadcr = 0;
+ }
+
+ fputc(c, fp);
+
+ }
+ fclose (fp);
+
+ /* Now grab the signature. Since signature data is required to be 7bit,
+ * we don't have to worry about doing CTE decoding...
+ */
+ snprintf (sigfile, sizeof (sigfile), "%s.asc", tempfile);
+ if(!(fp = safe_fopen (sigfile, "w")))
+ {
+ mutt_perror(sigfile);
+ unlink(tempfile);
+ return;
+ }
+
+ fseek (s->fpin, a->next->offset, 0);
+ mutt_copy_bytes (s->fpin, fp, a->next->length);
+ fclose (fp);
+
+ mutt_mktemp(pgperrfile);
+ if(!(pgperr = safe_fopen(pgperrfile, "w+")))
+ {
+ mutt_perror(pgperrfile);
+ unlink(tempfile);
+ unlink(sigfile);
+ return;
+ }
+
+ pgp_current_time (s);
+
+ if((thepid = pgp_invoke_verify (NULL, &pgpout, NULL, -1, -1, fileno(pgperr),
+ tempfile, sigfile)) != -1)
+ {
+ mutt_copy_stream(pgpout, s->fpout);
+ fclose (pgpout);
+
+ fflush(pgperr);
+ rewind(pgperr);
+ mutt_copy_stream(pgperr, s->fpout);
+ fclose(pgperr);
+
+ mutt_wait_filter (thepid);
+ }
+
+ state_puts ("[-- End of PGP output --]\n\n", s);
+
+ mutt_unlink (tempfile);
+ mutt_unlink (sigfile);
+ mutt_unlink (pgperrfile);
+
+ /* Now display the signed body */
+ state_puts ("[-- The following data is PGP/MIME signed --]\n\n", s);
+ }
+
+ mutt_body_handler (a, s);
+
+ if (s->flags & M_DISPLAY)
+ state_puts ("\n[-- End of PGP/MIME signed data --]\n", s);
+ }
+ else
+ {
+ dprint (1,(debugfile, "pgp_signed_handler: invalid format!\n"));
+ state_puts ("[-- Error! This message does not comply with the PGP/MIME specification! --]\n\n", s);
+ mutt_decode_attachment (a, s); /* just treat the data as text/plain... */
+ }
+}
+
+/* Extract pgp public keys from messages or attachments */
+
+void pgp_extract_keys_from_messages (HEADER *h)
+{
+ int i;
+ STATE s;
+ char tempfname[_POSIX_PATH_MAX];
+
+ if(h)
+ {
+ mutt_parse_mime_message(Context, h);
+ if(h->pgp & PGPENCRYPT && !pgp_valid_passphrase())
+ return;
+ }
+
+ memset(&s, 0, sizeof(STATE));
+
+ mutt_mktemp(tempfname);
+ if(!(s.fpout = safe_fopen(tempfname, "w")))
+ {
+ mutt_perror(tempfname);
+ return;
+ }
+
+ set_option(OPTDONTHANDLEPGPKEYS);
+
+ if(!h)
+ {
+ for(i = 0; i < Context->vcount; i++)
+ {
+ if(Context->hdrs[Context->v2r[i]]->tagged)
+ {
+ mutt_parse_mime_message(Context, Context->hdrs[Context->v2r[i]]);
+ if(Context->hdrs[Context->v2r[i]]->pgp & PGPENCRYPT
+ && !pgp_valid_passphrase())
+ {
+ fclose(s.fpout);
+ goto bailout;
+ }
+ mutt_pipe_message_to_state(Context->hdrs[Context->v2r[i]], &s);
+ }
+ }
+ }
+ else
+ {
+ mutt_parse_mime_message(Context, h);
+ if(h->pgp & PGPENCRYPT && !pgp_valid_passphrase())
+ {
+ fclose(s.fpout);
+ goto bailout;
+ }
+ mutt_pipe_message_to_state(h, &s);
+ }
+
+ fclose(s.fpout);
+ endwin();
+ pgp_invoke_extract(tempfname);
+ mutt_any_key_to_continue(NULL);
+
+ bailout:
+
+ mutt_unlink(tempfname);
+ unset_option(OPTDONTHANDLEPGPKEYS);
+
+}
+
+static void pgp_extract_keys_from_attachment(FILE *fp, BODY *top)
+{
+ STATE s;
+ FILE *tempfp;
+ char tempfname[_POSIX_PATH_MAX];
+
+ mutt_mktemp(tempfname);
+ if(!(tempfp = safe_fopen(tempfname, "w")))
+ {
+ mutt_perror(tempfname);
+ return;
+ }
+
+ memset(&s, 0, sizeof(STATE));
+
+ s.fpin = fp;
+ s.fpout = tempfp;
+
+ mutt_body_handler(top, &s);
+
+ fclose(tempfp);
+
+ pgp_invoke_extract(tempfname);
+ mutt_any_key_to_continue(NULL);
+
+ mutt_unlink(tempfname);
+
+}
+
+void pgp_extract_keys_from_attachment_list (FILE *fp, int tag, BODY *top)
+{
+ if(!fp)
+ {
+ mutt_error("Internal error. Inform <roessler@guug.de>.");
+ return;
+ }
+
+ endwin();
+ set_option(OPTDONTHANDLEPGPKEYS);
+
+ for(; top; top = top->next)
+ {
+ if(!tag || top->tagged)
+ pgp_extract_keys_from_attachment (fp, top);
+
+ if(!tag)
+ break;
+ }
+
+ unset_option(OPTDONTHANDLEPGPKEYS);
+}
+
+BODY *pgp_decrypt_part (BODY *a, STATE *s, FILE *fpout)
+{
+ char buf[LONG_STRING];
+ FILE *pgpin, *pgpout, *pgperr, *pgptmp;
+ struct stat info;
+ BODY *tattach;
+ int len;
+ char pgperrfile[_POSIX_PATH_MAX];
+ char pgptmpfile[_POSIX_PATH_MAX];
+ pid_t thepid;
+
+ mutt_mktemp (pgperrfile);
+ if ((pgperr = safe_fopen (pgperrfile, "w+")) == NULL)
+ {
+ mutt_perror (pgperrfile);
+ return NULL;
+ }
+ unlink (pgperrfile);
+
+ mutt_mktemp (pgptmpfile);
+ if((pgptmp = safe_fopen (pgptmpfile, "w")) == NULL)
+ {
+ mutt_perror (pgptmpfile);
+ fclose(pgperr);
+ return NULL;
+ }
+
+ /* Position the stream at the beginning of the body, and send the data to
+ * the temporary file.
+ */
+
+ fseek (s->fpin, a->offset, 0);
+ mutt_copy_bytes (s->fpin, pgptmp, a->length);
+ fclose (pgptmp);
+
+ if ((thepid = pgp_invoke_decrypt (&pgpin, &pgpout, NULL, -1, -1,
+ fileno (pgperr), pgptmpfile)) == -1)
+ {
+ fclose (pgperr);
+ unlink (pgptmpfile);
+ if (s->flags & M_DISPLAY)
+ state_puts ("[-- Error: could not create a PGP subprocess! --]\n\n", s);
+ return (NULL);
+ }
+
+ /* send the PGP passphrase to the subprocess */
+ fputs (PgpPass, pgpin);
+ fputc ('\n', pgpin);
+ fclose(pgpin);
+
+ /* Read the output from PGP, and make sure to change CRLF to LF, otherwise
+ * read_mime_header has a hard time parsing the message.
+ */
+ while (fgets (buf, sizeof (buf) - 1, pgpout) != NULL)
+ {
+ len = strlen (buf);
+ if (len > 1 && buf[len - 2] == '\r')
+ strcpy (buf + len - 2, "\n");
+ fputs (buf, fpout);
+ }
+
+ fclose (pgpout);
+ mutt_wait_filter (thepid);
+ mutt_unlink(pgptmpfile);
+
+ if (s->flags & M_DISPLAY)
+ {
+ fflush (pgperr);
+ rewind (pgperr);
+ mutt_copy_stream (pgperr, s->fpout);
+ state_puts ("[-- End of PGP output --]\n\n", s);
+ }
+ fclose (pgperr);
+
+ fflush (fpout);
+ rewind (fpout);
+ if ((tattach = mutt_read_mime_header (fpout, 0)) != NULL)
+ {
+ /*
+ * Need to set the length of this body part.
+ */
+ fstat (fileno (fpout), &info);
+ tattach->length = info.st_size - tattach->offset;
+
+ /* See if we need to recurse on this MIME part. */
+
+ if (tattach->type == TYPEMULTIPART)
+ {
+ fseek (fpout, tattach->offset, 0);
+ tattach->parts = mutt_parse_multipart (fpout, mutt_get_parameter ("boundary", tattach->parameter), tattach->offset + tattach->length, strcasecmp ("digest", tattach->subtype) == 0);
+ }
+ else if (tattach->type == TYPEMESSAGE)
+ {
+ fseek (fpout, tattach->offset, 0);
+ tattach->parts = mutt_parse_messageRFC822 (fpout, tattach);
+ }
+ }
+
+ return (tattach);
+}
+
+void pgp_encrypted_handler (BODY *a, STATE *s)
+{
+ char tempfile[_POSIX_PATH_MAX];
+ FILE *fpout, *fpin;
+ BODY *tattach;
+
+ a = a->parts;
+ if (!a || a->type != TYPEAPPLICATION || !a->subtype ||
+ strcasecmp ("pgp-encrypted", a->subtype) != 0 ||
+ !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype ||
+ strcasecmp ("octet-stream", a->next->subtype) != 0)
+ {
+ if (s->flags & M_DISPLAY)
+ state_puts ("[-- Error: malformed PGP/MIME message --]\n\n", s);
+ return;
+ }
+
+ /*
+ * Move forward to the application/pgp-encrypted body.
+ */
+ a = a->next;
+
+ mutt_mktemp (tempfile);
+ if ((fpout = safe_fopen (tempfile, "w+")) == NULL)
+ {
+ if (s->flags & M_DISPLAY)
+ state_puts ("[-- Error: could not create temporary file --]\n", s);
+ return;
+ }
+
+ if (s->flags & M_DISPLAY) pgp_current_time (s);
+
+ if ((tattach = pgp_decrypt_part (a, s, fpout)) != NULL)
+ {
+ if (s->flags & M_DISPLAY)
+ state_puts ("[-- The following data is PGP/MIME encrypted --]\n\n", s);
+
+ fpin = s->fpin;
+ s->fpin = fpout;
+ mutt_body_handler (tattach, s);
+ s->fpin = fpin;
+
+ if (s->flags & M_DISPLAY)
+ state_puts ("\n[-- End of PGP/MIME encrypted data --]\n", s);
+
+ mutt_free_body (&tattach);
+ }
+
+ fclose (fpout);
+ mutt_unlink(tempfile);
+}
+
+/* ----------------------------------------------------------------------------
+ * Routines for sending PGP/MIME messages.
+ */
+
+static void convert_to_7bit (BODY *a)
+{
+ while (a)
+ {
+ if (a->type == TYPEMULTIPART)
+ {
+ if (a->encoding != ENC7BIT)
+ {
+ a->encoding = ENC7BIT;
+ convert_to_7bit (a->parts);
+ }
+ }
+ else if (a->type == TYPEMESSAGE
+ && strcasecmp(a->subtype, "delivery-status"))
+ {
+ if(a->encoding != ENC7BIT)
+ mutt_message_to_7bit(a, NULL);
+ }
+ else if (a->encoding == ENC8BIT)
+ a->encoding = ENCQUOTEDPRINTABLE;
+ else if (a->encoding == ENCBINARY)
+ a->encoding = ENCBASE64;
+ else if (a->content && (a->content->from || (a->content->space && option (OPTPGPSTRICTENC))))
+ a->encoding = ENCQUOTEDPRINTABLE;
+ a = a->next;
+ }
+}
+
+BODY *pgp_sign_message (BODY *a)
+{
+ PARAMETER *p;
+ BODY *t;
+ char buffer[LONG_STRING];
+ char sigfile[_POSIX_PATH_MAX], signedfile[_POSIX_PATH_MAX];
+ FILE *pgpin, *pgpout, *pgperr, *fp, *sfp;
+ int err = 0;
+ int empty = 1;
+ pid_t thepid;
+
+ convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
+
+ mutt_mktemp (sigfile);
+ if ((fp = safe_fopen (sigfile, "w")) == NULL)
+ {
+ return (NULL);
+ }
+
+ mutt_mktemp (signedfile);
+ if ((sfp = safe_fopen(signedfile, "w")) == NULL)
+ {
+ mutt_perror(signedfile);
+ fclose(fp);
+ unlink(sigfile);
+ return NULL;
+ }
+
+ mutt_write_mime_header (a, sfp);
+ fputc ('\n', sfp);
+ mutt_write_mime_body (a, sfp);
+ fclose(sfp);
+
+ if((thepid = pgp_invoke_sign(&pgpin, &pgpout, &pgperr,
+ -1, -1, -1, signedfile)) == -1)
+ {
+ mutt_perror("Can't open PGP subprocess!");
+ fclose(fp);
+ unlink(sigfile);
+ unlink(signedfile);
+ return NULL;
+ }
+
+ fputs(PgpPass, pgpin);
+ fputc('\n', pgpin);
+ fclose(pgpin);
+
+ /*
+ * Read back the PGP signature. Also, change MESSAGE=>SIGNATURE as
+ * recommended for future releases of PGP.
+ */
+ while (fgets (buffer, sizeof (buffer) - 1, pgpout) != NULL)
+ {
+ if (strcmp ("-----BEGIN PGP MESSAGE-----\n", buffer) == 0)
+ fputs ("-----BEGIN PGP SIGNATURE-----\n", fp);
+ else if (strcmp("-----END PGP MESSAGE-----\n", buffer) == 0)
+ fputs ("-----END PGP SIGNATURE-----\n", fp);
+ else
+ fputs (buffer, fp);
+ empty = 0; /* got some output, so we're ok */
+ }
+
+ /* check for errors from PGP */
+ err = 0;
+ while (fgets (buffer, sizeof (buffer) - 1, pgperr) != NULL)
+ {
+ err = 1;
+ fputs (buffer, stdout);
+ }
+
+ mutt_wait_filter (thepid);
+ fclose (pgperr);
+ fclose (pgpout);
+ unlink (signedfile);
+
+ if (fclose (fp) != 0)
+ {
+ mutt_perror ("fclose");
+ unlink (sigfile);
+ return (NULL);
+ }
+
+ if (err)
+ mutt_any_key_to_continue (NULL);
+ if (empty)
+ {
+ unlink (sigfile);
+ return (NULL); /* fatal error while signing */
+ }
+
+ t = mutt_new_body ();
+ t->type = TYPEMULTIPART;
+ t->subtype = safe_strdup ("signed");
+ t->use_disp = 0;
+ t->encoding = ENC7BIT;
+
+ t->parameter = p = mutt_new_parameter ();
+ p->attribute = safe_strdup ("protocol");
+ p->value = safe_strdup ("application/pgp-signature");
+
+ p->next = mutt_new_parameter ();
+ p = p->next;
+ p->attribute = safe_strdup ("micalg");
+ p->value = safe_strdup (PgpSignMicalg);
+
+ p->next = mutt_new_parameter ();
+ p = p->next;
+ p->attribute = safe_strdup ("boundary");
+ p->value = mutt_generate_boundary ();
+
+ t->parts = a;
+ a = t;
+
+ t->parts->next = mutt_new_body ();
+ t = t->parts->next;
+ t->type = TYPEAPPLICATION;
+ t->subtype = safe_strdup ("pgp-signature");
+ t->filename = safe_strdup (sigfile);
+ t->use_disp = 0;
+ t->encoding = ENC7BIT;
+ t->unlink = 1; /* ok to remove this file after sending. */
+
+ return (a);
+}
+
+/* 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 *pgp_findKeys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc)
+{
+ char *pubring = pgp_pubring(PGP_ENCRYPT);
+ char *key, *keylist = NULL;
+ size_t keylist_size = 0;
+ size_t keylist_used = 0;
+ ADDRESS *tmp = NULL;
+ ADDRESS **last = &tmp;
+ ADDRESS *p;
+ int i;
+ KEYINFO *db;
+ KEYINFO *k_info;
+
+ db = pgp_read_keyring(pubring);
+
+ 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);
+ }
+
+ tmp = mutt_remove_duplicates (tmp);
+
+ for (p = tmp; p ; p = p->next)
+ {
+ if ((k_info = ki_getkeybyaddr (p, db, KEYFLAG_CANENCRYPT)) == NULL)
+ {
+ char buf[LONG_STRING];
+ snprintf (buf, sizeof (buf), "Enter keyID for %s: ", p->mailbox);
+
+ if ((key = pgp_ask_for_key (pubring, db, buf, p->mailbox,
+ KEYFLAG_CANENCRYPT, NULL)) == NULL)
+ {
+ pgp_closedb (db);
+ safe_free ((void **)&keylist);
+ rfc822_free_address (&tmp);
+ return NULL;
+ }
+ }
+ else
+ key = pgp_keyid(k_info);
+
+ keylist_size += strlen (key) + 4;
+ safe_realloc ((void **)&keylist, keylist_size);
+ sprintf (keylist + keylist_used, "%s0x%s", keylist_used ? " " : "",
+ key);
+ keylist_used = strlen (keylist);
+ }
+ rfc822_free_address (&tmp);
+ pgp_closedb (db);
+ return (keylist);
+}
+
+BODY *pgp_encrypt_message (BODY *a, char *keylist, int sign)
+{
+ char buf[LONG_STRING];
+ char tempfile[_POSIX_PATH_MAX], pgperrfile[_POSIX_PATH_MAX];
+ char pgpinfile[_POSIX_PATH_MAX];
+ FILE *pgpin, *pgperr, *fpout, *fptmp;
+ BODY *t;
+ PARAMETER *p;
+ int err = 0;
+ int empty;
+ pid_t thepid;
+
+ mutt_mktemp (tempfile);
+ if ((fpout = safe_fopen (tempfile, "w+")) == NULL)
+ {
+ mutt_perror (tempfile);
+ return (NULL);
+ }
+
+ mutt_mktemp (pgperrfile);
+ if ((pgperr = safe_fopen (pgperrfile, "w+")) == NULL)
+ {
+ mutt_perror (pgperrfile);
+ unlink(tempfile);
+ fclose(fpout);
+ return NULL;
+ }
+ unlink (pgperrfile);
+
+ mutt_mktemp(pgpinfile);
+ if((fptmp = safe_fopen(pgpinfile, "w")) == NULL)
+ {
+ mutt_perror(pgpinfile);
+ unlink(tempfile);
+ fclose(fpout);
+ fclose(pgperr);
+ return NULL;
+ }
+
+ if (sign)
+ convert_to_7bit (a);
+
+ mutt_write_mime_header (a, fptmp);
+ fputc ('\n', fptmp);
+ mutt_write_mime_body (a, fptmp);
+ fclose(fptmp);
+
+ if ((thepid = pgp_invoke_encrypt (&pgpin, NULL, NULL, -1,
+ fileno (fpout), fileno (pgperr),
+ pgpinfile, keylist, sign)) == -1)
+ {
+ fclose (pgperr);
+ unlink(pgpinfile);
+ return (NULL);
+ }
+
+ if (sign)
+ {
+ fputs (PgpPass, pgpin);
+ fputc ('\n', pgpin);
+ }
+ fclose(pgpin);
+
+ mutt_wait_filter (thepid);
+ unlink(pgpinfile);
+
+ fflush (fpout);
+ rewind (fpout);
+ empty = (fgetc (fpout) == EOF);
+ fclose (fpout);
+
+ fflush (pgperr);
+ rewind (pgperr);
+ while (fgets (buf, sizeof (buf) - 1, pgperr) != NULL)
+ {
+ err = 1;
+ fputs (buf, stdout);
+ }
+ fclose (pgperr);
+
+ /* pause if there is any error output from PGP */
+ if (err)
+ mutt_any_key_to_continue (NULL);
+
+ if (empty)
+ {
+ /* fatal error while trying to encrypt message */
+ unlink (tempfile);
+ return (NULL);
+ }
+
+ t = mutt_new_body ();
+ t->type = TYPEMULTIPART;
+ t->subtype = safe_strdup ("encrypted");
+ t->encoding = ENC7BIT;
+ t->use_disp = 0;
+
+ t->parameter = p = mutt_new_parameter ();
+ p->attribute = safe_strdup ("protocol");
+ p->value = safe_strdup ("application/pgp-encrypted");
+
+ p->next = mutt_new_parameter ();
+ p = p->next;
+ p->attribute = safe_strdup ("boundary");
+ p->value = mutt_generate_boundary ();
+
+ t->parts = mutt_new_body ();
+ t->parts->type = TYPEAPPLICATION;
+ t->parts->subtype = safe_strdup ("pgp-encrypted");
+ t->parts->encoding = ENC7BIT;
+ t->parts->use_disp = 0;
+
+ t->parts->next = mutt_new_body ();
+ t->parts->next->type = TYPEAPPLICATION;
+ t->parts->next->subtype = safe_strdup ("octet-stream");
+ t->parts->next->encoding = ENC7BIT;
+ t->parts->next->filename = safe_strdup (tempfile);
+ t->parts->next->use_disp = 0;
+ t->parts->next->unlink = 1; /* delete after sending the message */
+
+ mutt_free_body (&a); /* no longer needed! */
+
+ return (t);
+}
+
+int pgp_protect (HEADER *msg)
+{
+ char *pgpkeylist = NULL;
+ BODY *pbody = NULL;
+
+ /* Do a quick check to make sure that we can find all of the encryption
+ * keys if the user has requested this service.
+ */
+
+ set_option (OPTPGPCHECKTRUST);
+
+ if (msg->pgp & PGPENCRYPT)
+ {
+ if ((pgpkeylist = pgp_findKeys (msg->env->to, msg->env->cc, msg->env->bcc)) == NULL)
+ return (-1);
+ }
+
+ if ((msg->pgp & PGPSIGN) && !pgp_valid_passphrase ())
+ return (-1);
+
+ endwin ();
+ if (msg->pgp & PGPENCRYPT)
+ {
+ pbody = pgp_encrypt_message (msg->content, pgpkeylist, msg->pgp & PGPSIGN);
+ safe_free ((void **) &pgpkeylist);
+ if (!pbody)
+ return (-1);
+ }
+ else if (msg->pgp & PGPSIGN)
+ {
+ if ((pbody = pgp_sign_message (msg->content)) == NULL)
+ return (-1);
+ }
+ msg->content = pbody;
+ return 0;
+}
+
+#endif /* _PGPPATH */
</