Commits

Anonymous committed dea0678

mailcrypt 3.5.4

  • Participants
  • Parent commits cd14035
  • Tags sumo-1999-12-09, sumo-1999-12-11

Comments (0)

Files changed (14)

-  Mailcrypt 3.5.3 is Available for Download
+  Mailcrypt 3.5.4 is Available for Download
 
 
   1.  Introduction
   to message encryption with PGP (you do use Emacs to handle your mail
   and news, right?). Mailcrypt version 3.5b1 and higher have been
   extended to handle both PGP 2.6.x and PGP 5.x.  Versions 3.5 and
-  higher, including 3.5.3, also support encryption with GnuPG. Thanks
+  higher, including 3.5.4, also support encryption with GnuPG, thanks
   to Brian Warner.
 
 
   2.  Features
 
-  2.1. New Features/Enhancements
+  2.1. New in this Release
 
-   We have key fetching for PGP 5.0! Paul Koning has added support for
-   the Horowitz Key Protocol! PGP 5 keyservers can now be queried. If
-   no bugs are exposed in this release, then Mailcrypt's PGP5 support
-   can be considered feature-complete.
-
-   Improved compatibility for XEmacs users! We've fixed up several
-   gotchas which prevented version 3.5.1 from working "out of the box"
-   with XEmacs. Cross your fingers...it should be just fine now.
-
-   Fixed a minor compatibility bug with GPG support and GPG verson
-   0.9.3. Fixed a bug which dumped GNUS users into the wrong buffer after
-   verifying a signature. Fixed a compatibility bug between XEmacs and FSF
-   Emacs in mc-setversion. Fixed a bug in mh-e support so that users can
-   view decrypted messages without saving them. Several minor bugfixes.
+  This release is contains no new features, but a great many bug
+  fixes. Lots of things have been tightened up, for example: "fascist"
+  dialog boxes now run conditionally; signed messages can now quote
+  other signed messages without confusing Mailcrypt; email addresses
+  parsing is fully RFC 822 compliant, so recipient names are extracted
+  correctly even if they're weird; passphrase expiration works
+  correctly; Mailcrypt code now compiles with almost none of those
+  scary warnings.
 
 
   2.2.	Stable Features for PGP 5.0 and GnuPG
 
-  Support for GnuPG.  Key snarfing functionality for PGP 5.0.
+  Support for GnuPG.  Key fetching/snarfing functionality for PGP 5.0.
   Messages from PGP 5.0 operations now resemble more closely the
   messages generated for version 2.6.  Encryption works correctly,
   even when the secret keyring is not available (a requested feature
 
   2.4.	Still To Do
 
-  o  Restore functionality on Windows 95/98/NT. If tiny-pgp can do it,
-     we can too!
+  o  Fix signing of foreign-language emails under GNUS/Mule. Currently,
+     foreign characters are preceded by a '\207' byte, which is
+     present at signing, but stripped when messages are sent.
+
+  o  Get Mailcrypt to work with PGP 5.0 on NT.
+
+  o  Test Mailcrypt against PGP 6.5.
+
+  o  Fix the annoying Texinfo bug which spreads the manual index
+     out, four lines to a page.
 
   o  Refine Mailcrypt schemes so that alternate backends, like Crypt++,
      can be used.
 
   3.  Downloading Mailcrypt
 
-  The Official Mailcrypt version 3.5.3 can be downloaded at:
+  The Official Mailcrypt version 3.5.4 can be downloaded at:
 
   <http://www.pobox.com/~lbudney/linux/software/mailcrypt.html>
 
+1999-07-20  Leonard R. Budney  <lbudney@pobox.com>
+
+	* mc-pgp.el: Added check for PGP version mismatch when using
+	PGP 2.6.
+
+	* mailcrypt.info-1, mailcrypt.info-2, mailcrypt.info: Latest
+	info files.
+
+	* mc-gpg.el: Incorporated Brian Warner's Changes into GPG support.
+	Specific changes are noted in his Changelog entries, below.
+
+1999-07-18  Leonard R. Budney  <lbudney@pobox.com>
+
+	* mc-toplev.el: Changed GNUS interface to look at raw
+	articles. Thanks to Mr. Stainless Steel Rat.
+
+	* configure, configure.in: Modified configure script test
+	for emacs. When building Mailcrypt in a subshell of XEmacs,
+	the script would get confused because XEmacs would set the
+	environment variable EMACS to t. Thanks to Todd Sabin.
+
+	* mc-gpg.el: Added catchall pattern in GPG decrypt parser to
+	catch arbitrary "decryption failed" messages. This prevents
+	random failure from resulting in deleted message bodies! Thanks
+	to Robert Bihlmeyer.
+
+1999-07-17  Leonard R. Budney  <lbudney@pobox.com>
+
+	* mc-toplev.el: Changed rmail support so that errors do not
+	leave the user in some wacky buffer.
+
+1999-06-15  Brian Warner  <warner@lothar.com>
+
+	* mc-gpg.el: update comments to reflect GPG behavior through
+	0.9.7 (no code changes). Tested against 0.9.7, works ok, but
+	we're not taking advantage of the new GPG status codes yet.
+
+1999-05-25  Leonard R. Budney  <lbudney@pobox.com>
+
+	* mailcrypt.el: Changed message-box to message-or-box, which
+	doesn't annoy as many people with that invasive message
+	box. Thanks to Dave Love and Gunnar Evermann.
+
+	* Makefile.in, mc-gpg.el, mc-pgp.el, mc-pgp5.el, mc-remail.el,
+	mc-setversion.el: Cleaned up Makefile a bit, thanks to Francois
+	Pinard. Also added some defvar's to quiet almost all of those
+	pesky compiler warnings.  Thanks to Francois Pinard and to
+	Dave Love.
+
+1999-05-18  Leonard R. Budney  <lbudney@pobox.com>
+
+	* mc-gpg.el, mc-pgp.el, mc-pgp5.el: Tightened regular expressions
+	for matching PGP/GPG header strings only when they begin a
+	line. Also improved the comment in mc-gpg.el. Thanks to Ulrik
+	Dickow.
+
+1999-04-20  Leonard R. Budney  <lbudney@pobox.com>
+
+	* mailcrypt.el: Changed email address parsing to use the rfc822
+	library. Now extraction of recipient addresses is fully RFC
+	822 compliant.
+
+	* mc-toplev.el: Changed email address parsing to use the rfc822
+	library. Now extraction of recipient addresses is fully RFC 822
+	compliant. Thanks to Jack Twilley.
+
+1999-03-28  Brian Warner  <warner@lothar.com>
+
+	* mc-gpg.el: totally revamp decrypt-parser and verify-parser.
+	Tested against gpg-0.4.5 and gpg-0.9.5, checked GPG output
+	messages for 0.4.5 and 0.9.[012345] and these parsers should work
+	with them. GPG gives better and better status messages
+	(easy to parse, not locale-specific) in recent versions so I split
+	out the code that needs to scan stderr for information and created
+	a variable called mc-gpg-handle-pre095 (default to t) that
+	controls whether to use such code or not. If gpg-1.0 comes out and
+	everyone uses it and never uses old crufty versions, then this
+	will make it easier to rip that code out and have a much cleaner
+	set of parsers.
+
+1999-03-24  Brian Warner  <warner@lothar.com>
+
+	* mc-gpg.el (mc-gpg-verify-parser): cleanup, handle all gpg from 0.4.5
+	through 0.9.5, if the message is signed by an unknown key, get the
+	keyid efficiently and safely from gpg-0.9.5, else pull it from a
+	stderr message [less safe]
+
+1999-03-23  Brian Warner  <warner@lothar.com>
+
+	* mc-gpg.el (mc-gpg-insert-parser): if rc != 0, don't insert
+	anything, and return error information instead of erroring
+	out. Change all callers to deal with it. This fixes the "if we
+	can't find gpg, attempting to decrypt removes all text from the
+	buffer" bug.
+
+	* mc-gpg.el (mc-gpg-null-parser): fix [unused] null parser
+
+	* mc-gpg.el (mc-gpg-decrypt-parser): another message changed in
+	gpg-0.9.3 . Changed regexp to match either old ".. Secret key not
+	available" or new ".. public key not found"
+
+1999-03-20  Leonard R. Budney  <lbudney@pobox.com>
+
+	* ChangeLog, mc-pgp5.el: Fixed passphrase expiration when signing
+	messages. Thanks to Greg Shapiro.
+
 1999-03-12  Gregory Neil Shapiro  <gshapiro@sendmail.org>
 
 	* mc-pgp5.el (mc-pgp50-sign-parser): Deactivate the
-mailcrypt|Len Budney and Jin Choi and Pat LoPresti|lbudney@pobox.com, jin@atype.com, patl@lcs.mit.edu|PGP 5.0 and 2.6.* interface (plus anonymous remailer support) for RMAIL, VM, mh-e, GNUS|31-Jan-98|3.5.3|~/interfaces/mailcrypt-3.5.3.tar.gz
+mailcrypt|Len Budney and Jin Choi and Pat LoPresti|lbudney@pobox.com, jin@atype.com, patl@lcs.mit.edu|PGP 5.0 and 2.6.* interface (plus anonymous remailer support) for RMAIL, VM, mh-e, GNUS|31-Jan-98|3.5.4|~/interfaces/mailcrypt-3.5.4.tar.gz
 # the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 # Boston, MA 02111-1307, USA.
 
-VERSION = 2.01
-AUTHOR_VERSION = 3.5.3
+VERSION = 2.02
+AUTHOR_VERSION = 3.5.4
 MAINTAINER = XEmacs Development Team <xemacs-beta@xemacs.org>
 PACKAGE = mailcrypt
 PKG_TYPE = regular
 	$(srcdir)/mkinstalldirs $(lispdir) $(infodir) $(bindir)
 
 install-info: info
-	# There may be a newer info file in . than in srcdir.
-	-if test -f mailcrypt.info; then d=.; \
-	 else d=$(srcdir); fi; \
-	for file in $(INFOFILES); do\
-	  $(INSTALL_DATA) $$d/$$file $(infodir)/$$file; \
-	done;
-	$(INSTALL_INFO) --info-dir=$(infodir) \
+	cd $(srcdir) && for file in $(INFOFILES); do\
+	  $(INSTALL_DATA) $$file $(infodir)/$$file; \
+	done
+	-$(INSTALL_INFO) --info-dir=$(infodir) \
 	--entry="* Mailcrypt: (mailcrypt).       An Emacs/PGP interface" \
-	mailcrypt.info 
+	mailcrypt.info
 
 uninstall:
 	-cd $(lispdir) && rm -f $(SOURCES) $(OBJECTS)
+Noteworthy changes in Mailcrypt version 3.5.4:
+
+ * None, really; this is a bugfix release.
+
+
+
 Noteworthy changes in Mailcrypt version 3.5.3:
 
  * Added support for the Horowitz Key Protocol. PGP5 key fetching is

File mailcrypt.el

-;; mailcrypt.el v3.5.3, mail encryption with PGP
+;; mailcrypt.el v3.5.4, mail encryption with PGP
 ;; Copyright (C) 1995  Jin Choi <jin@atype.com>
 ;;                     Patrick LoPresti <patl@lcs.mit.edu>
 ;;           (C) 1998  Len Budney <lbudney@pobox.com>
 
 (require 'easymenu)
 (require 'comint)
+(require 'rfc822)
 
 (eval-and-compile
   (condition-case nil (require 'itimer) (error nil))
 ;;}}}
 
 ;;{{{ User variables.
-(defconst mc-version "3.5.3")
+(defconst mc-version "3.5.4")
 (defvar mc-temp-directory "/tmp"
   "*Default temp directory to be used by Mailcrypt.")
 (defvar mc-default-scheme 'mc-scheme-pgp "*Default encryption scheme to use.")
 
 (defsubst mc-strip-address (addr)
   "Strip everything from ADDR except the basic Email address."
-  (car (cdr (mail-extract-address-components addr))))
+  (car (rfc822-addresses addr)))
 
 (defun mc-strip-addresses (addr-list)
   "Strip everything from the addresses in ADDR-LIST except the basic
   (if (not (listp addr-list)) (setq addr-list (list addr-list)))
   (setq addr-list
 	(mapcar
-	 (function (lambda (s) (mc-split "\\([ \t\n]*,[ \t\n]*\\)" s)))
+	 (function (lambda (s) (rfc822-addresses s)))
 	 addr-list))
   (setq addr-list (apply 'append addr-list))
   (mapconcat 'mc-strip-address addr-list ", "))
 		     (match-beginning 0) (match-end 0))
 		  (setq retval nil)
 		  default))))
-    (if msg (if window-system
-		(progn (message nil) (message-box "%s" msg))
-	      (message "%s" msg)))
+    (if msg (message-or-box "%s" msg))
     retval))
 
 (defun mc-process-region (beg end passwd program args parser &optional buffer)

File mailcrypt.texi

 @syncodeindex fn cp
 
 @set TITLE Mailcrypt
-@set VERSION 3.5.3
+@set VERSION 3.5.4
 @set UPDATED August 29, 1998
 
 @ifinfo
 (defvar mc-gpg-alternate-keyring nil
   "*Public keyring to use instead of default.")
 (defvar mc-gpg-comment
-;  (format "Processed by Mailcrypt %s, an Emacs/GPG interface" mc-version)
-  nil
+   (format "Processed by Mailcrypt %s and Gnu Privacy Guard <http://www.gnupg.org/>" mc-version)
   "*Comment field to appear in ASCII armor output.  If nil, let GPG use its 
 default.")
-(defconst mc-gpg-msg-begin-line "-----BEGIN PGP MESSAGE-----"
+(defconst mc-gpg-msg-begin-line "^-----BEGIN PGP MESSAGE-----\r?$"
   "Text for start of GPG message delimiter.")
-(defconst mc-gpg-msg-end-line "-----END PGP MESSAGE-----\n?"
+(defconst mc-gpg-msg-end-line "^-----END PGP MESSAGE-----\r?$"
   "Text for end of GPG message delimiter.")
-(defconst mc-gpg-signed-begin-line "-----BEGIN PGP SIGNED MESSAGE-----"
+(defconst mc-gpg-signed-begin-line "^-----BEGIN PGP SIGNED MESSAGE-----\r?$"
   "Text for start of GPG signed messages.")
-(defconst mc-gpg-signed-end-line "-----END PGP SIGNATURE-----\n?"
+(defconst mc-gpg-signed-end-line "^-----END PGP SIGNATURE-----\r?$"
   "Text for end of GPG signed messages.")
 (defconst mc-gpg-key-begin-line "^-----BEGIN PGP PUBLIC KEY BLOCK-----\r?$"
   "Text for start of GPG public key.")
   (if (and (boundp 'mc-gpg-debug-buffer) mc-gpg-debug-buffer)
       (print string mc-gpg-debug-buffer)))
 
-;; the insert parser will return t and insert the whole of stdout if rc == 0,
-;; and will error out with the stderr text if rc != 0
+;; the insert parser will return '(t) and insert the whole of stdout if 
+;; rc == 0, and will return '(nil rc stderr) if rc != 0
 (defun mc-gpg-insert-parser (stdoutbuf stderrbuf statusbuf rc)
   (mc-gpg-debug-print 
    (format "(mc-gpg-generic-parser stdoutbuf=%s stderrbuf=%s rc=%s"
 	   stdoutbuf stderrbuf rc))
   (if (= rc 0)
-      '(t t)
-    ;;(list rc nil nil)
-    (error (with-current-buffer stderrbuf (buffer-string)))
-))
+      '(t (t))
+    (list nil nil rc (with-current-buffer stderrbuf (buffer-string))))
+)
 
 ;; the null parser returns rc and never inserts anything
 (defun mc-gpg-null-parser (stdoutbuf stderrbuf statusbuf rc)
-  (list t rc))
+  (list nil rc))
 
 ; utility function (variant of mc-process-region):
 ; take region in current buffer, send as stdin to a process
 ))
 
 
+; GPG DECRYPT BEHAVIOR:
 
-; todo: verify all possible cases
+; signed (not encrypted) by a known key [S.s1v]:
+;  rc == 0, stdout has message
+;  stderr: gpg: Signature made <date> using DSA key ID <pubkeyid32>
+;  stderr: gpg: Good signature from "<keyname>"
+;  status: GOODSIG <pubkeyid32> <keyname>
+;  status: TRUST_<level>
+;   0.9.0: GOODSIG <pubkeyid64> <keyname>
+;   0.9.1: VALIDSIG <pubkey-fingerprint>
+;   0.9.4: SIGID <sigprint> YYYY-MM-DD
+;   0.9.7: SIGID <sigprint> YYYY-MM-DD <longtime>
+;          VALIDSIG <pubkey-fingerprint> YYYY-MM-DD <longtime>
 
-; gpg's behavior:
-;  encrypted to a key we do not have: emits
-;   gpg: public key decryption failed: Secret key not available
-;   gpg: decryption failed: Secret key not available
-;   rc == 2
-;  encrypted to a key we do have but in --batch without --passphrase-fd, emits
-;   gpg: fatal: Can't query password in batchmode
-;   secmem usage: 1472/1472 bytes in 3/3 blocks of pool 1472/16384
-;   [GNUPG:] NEED_PASSPHRASE 414F124832654831
-;   rc == 2
-;  encrypted to a key we do have, but our passphrase was wrong:
-;   [GNUPG:] NEED_PASSPHRASE 414F124832654831
-;   gpg: public key decryption failed: Bad passphrase
-;   gpg: decryption failed: Secret key not available
-;   rc == 2
-;  encrypted to our key, passphrase ok:
-;   [GNUPG:] NEED_PASSPHRASE 414F124832654831
-;   <message>
-;   rc == 0
-;  encrypted to our key, sig from known key, passphrase ok:
-;   <message>
-;   gpg: Signature made Thu Aug  6 16:35:13 1998 using DSA key ID C63B6750
-;   [GNUPG:] GOODSIG C63B6750 Brian Warner (temporary ...
-;   gpg: Good signature from "Brian Warner (temporary GPG key) <warner@lothar.com>"
-;   [GNUPG:] TRUST_ULTIMATE
-;   rc == 0
-; encrypted to us, sig from unknown key, passphrase ok:
-;   [GNUPG:] NEED_PASSPHRASE 414F124832654831
-;   <message>
-;   gpg: Signature made Thu Aug  6 17:17:35 1998 using DSA key ID 567E33CF
-;   [GNUPG:] ERRSIG
-;   gpg: Can't check signature: Public key not found
-;   rc == 2
-; conventionally encrypted but we didn't give a passphrase
-;   gpg: fatal: Can't query password in batchmode
-;   rc == 2
-; conventionally encrypted, we gave the wrong passphrase
-;   gpg: decryption failed: Bad key
-;   rc == 2
-; conventionally encrypted, correct passphrase
-;   <message>
-;   rc == 0
-; signed, not encrypted, not clearsigned, known key
-;   <message>
-;   GOODSIG
-;   TRUST_ULTIMATE
-;   gpg: Signature made Fri Sep 18 23:15:47 1998 PD using DSA key ID 4B75DDCF
-;   gpg: Good signature from "owner1 <user@test>"
+; signed (not encrypted) by unknown key [S.s4]:
+;  rc == 2, stdout has message
+;  stderr: gpg: Signature made <date> using DSA key ID <pubkeyid32>
+;  stderr: Can't check signature: [Pp]ublic key not found
+;  status: ERRSIG
+;   0.9.5: ERRSIG <pubkeyid64> <algo-id>
+;   0.9.7: ERRSIG <pubkeyid64> <pubkey-algo-id> <hash-algo-id> <sig-class==00> <longtime> <rc==9>
+;      <rc>: 4 unknown algorithm, 9 missing pubkey
+
+; encrypted to a private key we don't have [E.e3]:
+;  rc == 2,
+;  stderr: gpg: decryption failed: [Ss]ecret key not available
+;  status:
+;  0.4.4: none
+;  0.9.5: ENC_TO <keyid>
+;  0.9.6: DECRYPTION_FAILED
+
+; encrypted to us, but we didn't give a passphrase [E.e1r, no pw]:
+;  rc == 2
+;  stderr: gpg: fatal: Can't query password in batchmode
+;  status:
+;   0.4.4: NEED_PASSPHRASE <subkeyid>
+;   0.9.1: NEED_PASSPHRASE <subkeyid> <pubkeyid>
+;   0.9.5: ENC_TO <subkeyid>
+
+; encrypted to us, but we used the wrong passphrase [E.e1r, bad pw]:
+;  rc == 2
+;  stderr: gpg: public key decryption failed: [Bb]ad passphrase
+;  status:
+;   0.4.4: NEED_PASSPHRASE <subkeyid>
+;   0.9.1: NEED_PASSPHRASE <subkeyid> <pubkeyid>
+;   0.9.5: ENC_TO <subkeyid>
+;          BAD_PASSPHRASE <subkeyid>
+;   0.9.6: DECRYPTION_FAILED
+
+; encrypted to us, good passphrase [E.e1r, good pw]:
+;  rc == 0, stdout has message
+;  status:
+;   0.4.4: NEED_PASSPHRASE <subkeyid>
+;   0.9.1: NEED_PASSPHRASE <subkeyid> <pubkeyid>
+;   0.9.5: ENC_TO <subkeyid>
+;   0.9.6: GOOD_PASSPHRASE
+;          DECRYPTION_OKAY
+
+; encrypted to us, good passphrase, signed by trusted/untrusted party
+;                                        [ES.e1r.s1v, good ps]:
+;  rc == 0, stdout has message
+;  stderr: gpg: Signature made <date> using DSA key ID <pubkeyid>
+;  stderr: gpg: Good signature from "<keyname>"
+;  status: NEED_PASSPHRASE (as above)
+;  status: GOODSIG <pubkeyid> <keyname>
+;  status: TRUST_<level>
+;   0.4.5: GOODSIG <32bit-pubkeyid>. >=0.9.0: GOODSIG <64bit-pubkeyid>
+;   0.9.1 added VALIDSIG <big-fingerpring-of-pubkeyid>
+;   0.9.4 added SIG_ID <base64-sigid> <YYYY-MM-DD>
+;   0.9.5 added ENC_TO <64bit-subkeyid>
+;   0.9.6: GOOD_PASSPHRASE
+;          DECRYPTION_OKAY
+;   0.9.7: SIG_ID <base64-sigid> <YYY-MM-DD> <longtime>
+;          VALIDSIG <big> <YYYY-MM-DD> <longtime>
+
+; encrypted to us, good passphrase, signed by unknown party [ES.e1r.s4]:
+;  rc == 2, stdout has message
+;  stderr: gpg: Signature made <date> using DSA key ID <pubkeyid>
+;  stderr: gpg: Can't check signature: [Pp]ublic key not found
+;  status: NEED_PASSPHRASE (as above)
+;  status: ERRSIG
+;   0.9.5: ERRSIG <64bit-pubkeyid> <algo-id>
+;          ENC_TO <subkeyid>
+;   0.9.6: GOOD_PASSPHRASE
+;          DECRYPTION_OKAY
+;   0.9.7: ERRSIG <64bit-pubkeyid> <algo-id> <hashid> <sig-class==01> <longtime> <rc==9>
+
+; symmetrically encrypted, we didn't give a passphrase
+;  rc == 2, stderr: gpg: fatal: Can't query password in batchmode
+;  status: none
+;   0.9.6: NEED_PASSPHRASE_SYM 4 1 3
+
+; symmetrically encrypted, we gave the wrong passphrase
+;  rc == 2, stderr: gpg: decryption failed: [Bb]ad key
+;  status: none
+;   0.9.6: NEED_PASSPHRASE_SYM 4 1 3
+;          DECRYPTION_FAILED
+
+; symmetrically encrypted, good passphrase
+;  rc == 0, stdout: message
+;  status: none
+;   0.9.6: NEED_PASSPHRASE_SYM 4 1 3  (cipheralgo, s2kmode, s2khash)
+;          DECRYPTION_OKAY
+
+; armored [A]:
+;  rc == 0, stdout: message
+;  (note: indistinguishable from symmetric with good passphrase)
+
+; corrupted armor
+;  rc == 2, stderr: gpg: CRC error; stuff - stuff
+
+; ( to test: multiple recipients, keys without passphrases)
+
+;; note that if rc != 0, one of the following has occurred:
+;; 1message is to us but we didn't give a passphrase
+;;   (no SIG messages, has NEED_PASSPHRASE)
+;; 2message was encrypted to a key we don't have
+;;   (no SIG messages, no NEED_PASSPHRASE messages, >0.9.5 has ENC_TO)
+;; 3message was symmetrically encrypted and we have no or wrong passphrase
+;;   (no SIG, no NEED_PASSPHRASE)
+;; 4everything decrypted OK, but the message was signed by an unknown key
+;;   (ERRSIG will be in status)
+;; 5message is corrupted
+;;   (no status messages at all, stderr says CRC error)
+;;
+;; so unless we've got 0.9.5 or later, we have to parse stderr to distinguish
+;; between #2 and #3 (and we must, to determine if we need to ask the
+;; user for a passphrase or not).
+;; For all versions, we have to parse stderr to distinguish between #3 and #5.
 
 ;; this parser's return convention:
 ;;   '( (
-;;0      have-secret-key ; we are a recipient (TODO: stealth)
+;;       replacep ; consumed by process-region: decrypt was successful
+;;0      have-secret-key ; t: we are a recipient (TODO: stealth), 
+;;                         'symmetric : need passphrase
+;;                         'signed : signed not encrypted
+;;                         nil: not a recipient
 ;;1      passphrase-ok ; t was good, nil was bad, keyid: need pw for keyid
 ;;2      signature: 
 ;;        nil: no sig
 ;;        '(keyid-string t trust date) : good signature on date with trust
 ;;        '(keyid-string nil trust date) : bad signature on date with trust
 ;;       )
-;;      begin end )
+;;      )
 ; todo: stealth ("--throw-keyid")?
 
 ;; cases:
 ;;  *not addressed to us (nil nil nil)
+;;  *just armored (same as good symmetric) ('symmetric t nil)
 ;;  conventionally encrypted
 ;;   *didn't give passphrase ('symmetric "***** CONVENTIONAL *****" nil)
 ;;   did give passphrase
 ;;  signed (not clearsigned), not encrypted
 ;;    *don't have key ('signed t keyid)
 ;;    do have key
-;;     *good sig ('signed t (t keyid-string date trust))
-;;     *bad sig ('signed t (nil keyid-string date trust))
-;;  addressed to us:
+;;     *good sig ('signed t (t keyid-string trust date))
+;;     *bad sig ('signed t (nil keyid-string trust date))
+;;  encrypted to us:
 ;;   *didn't give passphrase (t keyid nil)
 ;;   gave passphrase:
 ;;    *bad passphrase (t nil nil)
 ; code in -decrypt-region will worry about reporting other status information
 ; like signatures
 
-; FIXME: if the process dies (say, gpg can't be found), this replaces the
-; region with void. Bad, parser, bad.
+(defvar mc-gpg-handle-pre095 t
+  "Include parsing code to handle versions of GnuPG older than gpg-0.9.5 .
+Recent versions of GPG have better, more parseable status messages and don't
+require as many locale-specific stderr messages to be parsed. If t, parse
+stderr messages to figure out what gpg did. Leave this set to t unless you are
+running a recent GPG in a non-US locale and encounter problems with GPG
+messages not being recognized properly. Think of this variable as more of a
+#define constant to keep some parsing code clean.")
 
 (defun mc-gpg-decrypt-parser (stdoutbuf stderrbuf statusbuf rc)
-  (let (keyid sigtype symmetric sigid sigdate sigtrust)
+  (let (enckeyid keyid symmetric badpass sigtype sigid sigdate sigtrust)
     (set-buffer statusbuf)
+    ;; keyid: the message is encrypted to one of our keys. which one?
     (goto-char (point-min))
-    (if (re-search-forward "NEED_PASSPHRASE \\(\\S +\\)" nil t)
+    (if (re-search-forward "^\\[GNUPG:\\]\\s +NEED_PASSPHRASE\\s +\\(\\S +\\)" 
+			   nil t)
 	(setq keyid (concat "0x" (match-string 1))))
     (goto-char (point-min))
-    (if (re-search-forward "\\(\\S +SIG\\)" nil t)
-	(setq sigtype (match-string 1)))
+    ;; badpass: t if gpg reported a bad passphrase (>=0.9.5)
+    ;;          we gave one, but it was wrong. this is different than us not
+    ;;          giving one.
+    (if (re-search-forward "^\\[GNUPG:\\]\\s +BAD_PASSPHRASE\\b" 
+			   nil t)
+	(setq badpass (concat "0x" (match-string 1))))
+    (if (and (not (= rc 0))
+	     (not badpass))
+	(progn
+	  (set-buffer stderrbuf)
+	  (goto-char (point-min))
+	  (if mc-gpg-handle-pre095
+	     (progn
+	       ;; gpg < 0.9.5 reports a bad PKE passphrase with rc!=0 and a
+	       ;; stderr msg. look for it
+	       (if (re-search-forward
+		    "^gpg: public key decryption failed: [Bb]ad passphrase" 
+		    nil t)
+		   (setq badpass t))
+	       ))
+	  ;; All versions (at least through 0.9.5) fail to distinguish (in the
+	  ;; --status-fd output) between not getting a passphrase for
+	  ;; symmetric encryption and getting a bad one. We need to figure out
+	  ;; the difference to pass up, since the caller must either ask for a
+	  ;; passphrase or error out because of a bad one.
+	  ;;
+	  ;; Although really we know whether we gave it a passphrase or not,
+	  ;; so if we passed suitable state information into this function
+	  ;; then this test wouldn't be necessary.
+	  (goto-char (point-min))
+	  (if (re-search-forward
+	       "^gpg: decryption failed: [Bb]ad key" nil t)
+	      (setq badpass t)) ;; ditto for symmetric encryption
+	  ;; The same problem exists for distinguishing that case from the
+	  ;; ascii-armored message being corrupted (a CRC error). Scan stderr
+	  ;; for the error message and error out right now if it was bogus
+	  (goto-char (point-min))
+	  (if (re-search-forward
+	       "^gpg: CRC error" nil t)
+	      (error "Corrupt GPG message"))
+	  (set-buffer statusbuf)
+	  ))
+    ;; sigtype: GOOD, BAD, ERR
+    ;; sigid: who made the signature?
+    ;; sigdate: date string of when the sig was made
     (goto-char (point-min))
-    (if (re-search-forward "\\(TRUST_\\S +\\)$" nil t)
+    (if (re-search-forward "^\\[GNUPG:\\]\\s +\\(GOOD\\|BAD\\|ERR\\)SIG" nil t)
+	(progn
+	  (setq sigtype (match-string 1))
+	  (goto-char (point-min))
+	  (if (and (or (equal sigtype "GOOD") (equal sigtype "BAD"))
+		   (re-search-forward
+		    "^\\[GNUPG:\\]\\s +\\(GOOD\\|BAD\\)SIG\\s +\\(\\S +\\)\\s +\\(.*\\)$" nil t))
+	      (setq sigid (match-string 3)))
+	  ;; match-string 2 is the hex keyid of the signator. m-s 3 is the name
+	  (goto-char (point-min))
+	  (if (and (equal sigtype "ERR")
+		   (re-search-forward
+		    "^\\[GNUPG:\\]\\s +ERRSIG\\s +\\(\\S +\\)\\s +\\(.*\\)$" nil t))
+	      (setq sigid (concat "0x" (match-string 1))))
+	  ;; match-string 1 is the hex keyid, 2 is the algorithm ID
+	  ;;  (17: DSA, 1,3: RSA, 20: Elgamal)
+	  ;; sigdate is only in stderr. 0.9.4 adds SIG_ID <sigprint> YYYY-MM-DD
+	  ;; but time is only in stderr
+	  (set-buffer stderrbuf)
+	  (goto-char (point-min))
+	  (if (re-search-forward
+	       "^gpg: Signature made \\(.*\\) using" nil t)
+	      (setq sigdate (match-string 1)))
+	  ;; get the keyname that made the signature if it wasn't available in
+	  ;; the GOODSIG/BADSIG/ERRSIG status (true for ERRSIG in gpg <0.9.5
+	  ;; where it must be pulled out of stderr)
+	  (if mc-gpg-handle-pre095
+	      (progn
+		(goto-char (point-min))
+		(if (and (not sigid)
+			 (re-search-forward
+			  "^gpg: Signature made .* key ID \\(.*\\)$" nil t))
+		    (setq sigid (concat "0x" (match-string 1))))))
+	  (set-buffer statusbuf)
+	  ))
+    
+    ;; sigtrust: how trusted is the signing key?
+    (goto-char (point-min))
+    (if (re-search-forward "^\\[GNUPG:\\]\\s +\\(TRUST_\\S +\\)$" nil t)
 	(setq sigtrust (match-string 1)))
-    (set-buffer stderrbuf)
+    ;; enckeyid: who is the message encrypted to (not necessarily us)
+    ;; TODO: possibly multiple entries?
     (goto-char (point-min))
-    (if (and (not keyid)
-	     (re-search-forward 
-	      "^gpg: public key decryption failed: Secret key not available$" 
-	      nil t))
-	;; encrypted to a key we do not have. Bail now.
-	(list nil nil nil nil)
-      (progn
-	(goto-char (point-min))
-	(if (re-search-forward
-	     "^gpg: Signature made \\(.*\\) using" nil t)
-	    (setq sigdate (match-string 1)))
-	(goto-char (point-min))
-	(if (equal sigtype "ERRSIG")
-	    (if (re-search-forward
-		 "^gpg: Signature made .* key ID \\(.*\\)$" nil t)
-		(setq sigid (concat "0x" (match-string 1))))
-	  (if (re-search-forward 
-	       "^gpg: \\S + signature from \"\\(.*\\)\"$" nil t)
-	      (setq sigid (match-string 1))))
-	(goto-char (point-min))
-	(cond
-	 ((re-search-forward 
-	   "^gpg: fatal: Can't query password in batchmode$" nil t)
-	    ;; didn't give a password.
-	  (if keyid
-	      ;; public key encryption
-	      (list nil t keyid nil)
-	    ;; symmetric encryption
-	    (list nil 'symmetric "***** CONVENTIONAL *****" nil)))
-	 ;; did give a password
-	 ((re-search-forward
-	   "^gpg: public key decryption failed: Bad passphrase$" nil t)
-	    ;; bad passphrase
-	  (list nil t nil nil) ; pke
+    (if (re-search-forward "^\\[GNUPG:\\]\\s +ENC_TO \\(\\S +\\)" 
+			   nil t)
+	(setq enckeyid (concat "0x" (match-string 1))))
+
+    (mc-gpg-debug-print 
+     (format
+      "decrypt-parser: handle-pre095=%s enckeyid=%s keyid=%s symmetric=%s badpass=%s sigtype=%s sigid=%s sigdate=%s sigtrust=%s"
+      mc-gpg-handle-pre095 enckeyid keyid symmetric badpass sigtype sigid 
+      sigdate sigtrust))
+
+    (cond
+     
+     ((or (and enckeyid (not keyid)) ;; encrypted but not to us, gpg>=0.9.5
+	  ;; need the following for gpg < 0.9.5
+	  (and mc-gpg-handle-pre095
+	       (not keyid)
+	       ;; no keyid (no passphrase needed). one of:
+	       ;;  just armored (rc==0, no msgs about sigs)
+	       ;;  only signed (rc==0 if key found, rc==2 if not, yes sig msg)
+	       ;;  encrypted but not to us (rc==2, no msg about sigs)
+	       ;;  symmetric, we didn't give a passphrase (rc==2, stderr msg)
+	       ;;  symmetric, bad passphrase (rc==2, stderr msg -> badpass)
+	       (not (= rc 0)) ;; remove armored, well-signed cases
+	       (not sigtype) ;; remove badly-signed case
+	       (not badpass) ;; remove symmetric-bad-passphrase case
+	       (progn ;; remove symmetric-no-passphrase case
+		 (set-buffer stderrbuf)
+		 (goto-char (point-min))
+		 (not (re-search-forward
+		       "^gpg: fatal: Can't query password in batchmode" nil t))
+		 ))
 	  )
-	 ((re-search-forward
-	   "^gpg: decryption failed: Bad key$" nil t)
-	  (list nil 'symmetric nil nil))
-	 ;; password/passphrase was good (we were able to decrypt the message)
-	 ;; or the message was just signed
-	 ;; figure out signatureness
-	 ((not keyid)
-	  ;; not PKE: symmetric or just signed
-	  (if sigtype
-	      ;; signed only
-	      (cond
-	       ((equal sigtype "ERRSIG")
-		(list t 'signed t sigid))
-	       ((equal sigtype "GOODSIG")
-		(list t 'signed t (list t sigid sigtrust sigdate)))
-	       (t
-		(list t 'signed t (list nil sigid sigtrust sigdate)))
-	       )
-	    ;; symmetric. Don't bother with sig
-	    (list t 'symmetric t nil)))
-	 ((not sigtype)
-	  ;; no signature
-	  (list t t t nil))
-	 ((equal sigtype "ERRSIG")
-	  ;; missing key
-	  (list t t t sigid))
-	 ((equal sigtype "GOODSIG")
-	  (list t t t (list t sigid sigtrust sigdate)))
-	 (t
-	  (list t t t (list nil sigid sigtrust sigdate)))
-	 )))
+      ;; encrypted to a key we do not have. Bail now.
+      (list nil nil nil nil))
+
+     ;; check rc != 0 case. Five possibilities (#2 was eliminated above):
+     ;;  #1: message is to us but we didn't give/gave bad passphrase
+     ;;      (no SIG messages, has NEED_PASSPHRASE)
+     ;;  #2: message is encrypted but not to us
+     ;;  #3: message is symmetric but we didn't give/gave bad passphrase
+     ;;      (no SIG, no NEED_PASSPHRASE)
+     ;;  #4: message decrypted OK but was signed by unknown key
+     ;;      (ERRSIG, NEED_PASSPHRASE)
+     ;;  #5: message wasn't encrypted, only signed, but by an unknown key
+     ;;      (ERRSIG, no NEED_PASSPHRASE)
+
+     ((and (not (= rc 0)) (not keyid) sigtype)
+      ;; case #5: not encrypted but can't check signature
+      (list t 'signed t sigid))
+
+     ((and (not (= rc 0)) (not keyid))
+      ;; case #3: need passphrase for symmetric encryption
+      (if badpass
+	  (list nil 'symmetric nil nil)
+	(list nil 'symmetric "***** CONVENTIONAL *****" nil)))
+
+     ((and (not (= rc 0)) (not sigtype))
+      ;; case #1: need passphrase for public key encryption
+      (if badpass
+	  (list nil t nil nil)
+	(list nil t keyid nil)))
+
+     ((and (not (= rc 0)) (not (equal sigtype "ERR")))
+      (error "mc-gpg.el error, should never happen"))
+
+     ;; case #4: decrypted OK but signed by an unknown key: drops through
+
+     ((and (not keyid) (not sigtype))
+      ;; not public-key encrypted, not signed. is symmetric. must have worked
+      (list t 'symmetric t nil))
+
+     ((not keyid)
+      ;; not PKE, not symmetric: just signed
+      (cond
+       ((equal sigtype "ERR")
+	(list t 'signed t sigid)) ; signed by an unknown key
+
+       ((equal sigtype "GOOD")
+	(list t 'signed t (list t sigid sigtrust sigdate))) ; good sig
+       (t
+	(list t 'signed t (list nil sigid sigtrust sigdate))) ; bad sig
+       ))
+
+     ;; at this point, it was PKE and we decrypted it successfully. The
+     ;; only question is whether it was signed or not
+     ((not sigtype)
+      (list t t t nil))  ; not signed
+     ((equal sigtype "ERR")
+      (list t t t sigid)) ; signed by unknown key
+     ((equal sigtype "GOOD")
+      (list t t t (list t sigid sigtrust sigdate))) ; good sig
+     (t
+      (list t t t (list nil sigid sigtrust sigdate))) ; bad sig
+     )))
+
+;; message about who made the signature. This is a bit wide.. the date can
+;; easily run off the echo area. Consider replacing 'Good signature' with
+;; 'good sig', but keep it consistent with everything else. This function is
+;; used by both the decrypt section and the verify section
+(defun mc-gpg-format-sigline (goodp sigid sigtrust sigdate)
+  (if goodp
+      (format "Good signature from '%s' %s made %s"
+	      sigid sigtrust sigdate)
+    (format "BAD SIGNATURE from '%s' made %s"
+	    sigid sigdate)
     ))
 
 ;; decrypt-region is first called without ID. This means we'll try to decrypt
 	  (mc-gpg-process-region
 	   start end passwd mc-gpg-path args 'mc-gpg-decrypt-parser buffer))
     ;(message "Decrypting... Done.")
+    ;; result: '(HAVE-SECRET-KEY PASSPHRASE-OK SIG)
+    ;;  SIG: nil, sigkeyid, or '(KEYID GOODP TRUSTLEVEL DATESTRING)
     (cond
      ((not (nth 0 result)) ;; we were not a recipient
       (error "This message is not addressed to you"))
 	    ))
 	 ((nth 0 sig) ;; good signature
 	  (progn
-	    ;; message about who made the signature. This is a bit wide..
-	    ;; the date can easily run off the echo area. Consider replacing
-	    ;; 'Good signature' with 'good sig', but keep it consistent with
-	    ;; everything else.
-	    (message (format "Good signature from '%s' %s made %s"
-			     (nth 1 sig) (nth 2 sig) (nth 3 sig)))
+	    (message (mc-gpg-format-sigline 
+		      t (nth 1 sig) (nth 2 sig) (nth 3 sig)))
 	    '(t . t)
 	    ))
 	 (t ;; bad signature
 	  (progn
-	    ;; message about who made the bad signature?? misleading?
-	    (message (format "BAD SIGNATURE from '%s' %s made %s"
-			     (nth 1 sig) (nth 2 sig) (nth 3 sig)))
+	    (ding)
+	    (message (mc-gpg-format-sigline 
+		      nil (nth 1 sig) (nth 2 sig) (nth 3 sig)))
 	    '(t . nil)
 	    ))
        )))
     (car result)
 ))
 
-; our convention for this parser: return '((STATUS MESSAGE) nil nil). 
-;  STATUS= 'good: MESSAGE is displayed in the echo area (name of signator,
-;                 timestamp of signature, trust level
-;  STATUS= 'bad:  MESSAGE is displayed with a beep. (just dump stderr)
-;  STATUS= 'needkey: MESSAGE is the hex keyid required
 
-; the way GPG works:
+; GPG VERIFY BEHAVIOR
 
-;  GOOD sig from a trusted key (gpgm --check-trustdb):
-;   rc=0, 
-;   stderr:
-;gpg: Signature made Mon Sep  7 01:19:29 1998 using DSA key ID FE8E94E9
-;gpg: Good signature from "trusted <trusted@test>"
-;   status:
-;[GNUPG:] GOODSIG
-;[GNUPG:] TRUST_FULLY
+; corrupted sig (armor is corrupt) [CS.s1bad]:
+;  rc == 8, segfault
+;  stderr: gpg: CRC error; stuff - stuff
+;          gpg: packet(1) with unknown version
 
-;  GOOD sig from an untrusted key
-;   rc=0
-;gpg: Signature made Mon Sep  7 01:19:29 1998 using DSA key ID DA4E030E
-;gpg: Good signature from "untrusted <untrusted@test>"
-;gpg: WARNING: This key is not certified with a trusted signature!
-;gpg:          There is no indication that the signature belongs to the owner.
-;[GNUPG:] GOODSIG
-;[GNUPG:] TRUST_UNDEFINED
+; GOOD sig from a known key
+;  rc == 0
+;  stderr: gpg: Signature made <date> using DSA key ID <pubkeyid32>
+;          gpg: Good signature from "<keyname>"
+;   (if key is untrusted, get big warning about it)
+;  status: GOODSIG <pubkeyid32> <keyname>
+;          TRUST_<level>
+;   0.9.0: GOODSIG<pubkeyid64> <keyname>
+;   0.9.1: VALIDSIG <pubkey-fingerprint>
+;   0.9.4: SIG_ID <sigprint> YYYY-MM-DD
+;   0.9.7: SIG_ID <sigprint> YYYY-MM-DD <longtime>
+;          VALIDSIG <pubkey-fingerprint> YYYY-MM-DD <longtime>
 
-;  BAD sig from a trusted key:
-;   rc=1
-;   stderr:
-;gpg: Signature made Mon Sep  7 01:19:28 1998 using DSA key ID 4B75DDCF
-;gpg: BAD signature from "owner1 <user@test>"
-;   status:
-;[GNUPG:] BADSIG
+; BAD sig from a known key [CS.s1f]:
+;  rc == 1
+;  stderr: gpg: Signature made <date> using DSA key ID <pubkeyid32>
+;          gpg: BAD signature from "<keyname>"
+;  status: BADSIG <pubkeyid32> <keyname>
+;   0.9.0: BADSIG <pubkeyid64> <keyname>
 
-;  sig from a missing key:
-;   rc=2
-;   stderr:
-;gpg: Signature made Mon Sep  7 01:19:29 1998 using DSA key ID 2B09EB69
-;gpg: Can't check signature: Public key not found
-;   status:
-;[GNUPG:] ERRSIG
+; unknown key [CS.s4]:
+;  rc == 2
+;  stderr: gpg: Signature made <date> using DSA key ID <pubkeyid32>
+;          gpg: Can't check signature: [pP]ublic key not found
+;  status: ERRSIG
+;   0.9.5: ERRSIG <pubkeyid64> <algo-id>
+;   0.9.7: ERRSIG <pubkeyid64> <algo-id> <hash-id> <sigclass==01> <longtime> <rc==9>
+
+;; so if rc != 0 and we see no mention of a signature (GOOD,BAD,ERR) then
+;;  assume armor corruption
+
+;; return convention for mc-gpg-verify-parser:
+;;  (same as sig section of decrypt parser)
+;;   sigid : signed by an unknown key, need this key to verify
+;;   '(t sigid sigtrust sigdate): good sig from sigid
+;;   '(nil sigid sigtrust sigdate): forged sig "from" sigid
+;; (actual return includes a leading nil because the verify-parser should
+;;  never replace the region with stdout)
 
 (defun mc-gpg-verify-parser (stdoutbuf stderrbuf statusbuf rc)
-  (let (
-	(status (let (msg)
-		  (set-buffer statusbuf)
-		  (goto-char (point-min))
-		  (while (re-search-forward "^\\[GNUPG:\\]\\s +\\(\\S +\\)"
-					    nil t)
-		    (setq msg (append msg (list (match-string 1)))))
-		  (mapconcat 'identity msg " ")))
-	(needkey (progn
-		   (if (and (progn
-			      (set-buffer statusbuf)
-			      (goto-char (point-min))
-			      (re-search-forward "^\\[GNUPG:\\]\\s +ERRSIG$" 
-						 nil t))
-			    (progn
-			      (set-buffer stderrbuf)
-			      (goto-char (point-min))
-			      (re-search-forward 
-			       "gpg: Can't check signature: Public key not found$"
-			       nil t))
-			    )
-		       (progn
-			 (set-buffer stderrbuf)
-			 (goto-char (point-min))
-			 (re-search-forward
-			  "key ID \\(\\S +\\)$" nil t)
-			 (match-string 1)))))
-	)
-    (mc-gpg-debug-print (format " status is %s" status))
-    (cond 
-     ((= rc 0)
-      ;; good signature. stderr has info about the signature
-      ;; status has GOODSIG and a keyword with trust info
-      ;; return GOODSIG TRUST_FULLY from "keyid"
-      (list nil 
-	    'good 
-	    (progn
-	      (set-buffer stderrbuf)
-	      (goto-char (point-min))
-	      (if (re-search-forward "^gpg: Good signature \\(from.*\\)$"
-				     nil t)
-		  (setq status (concat status " " (match-string 1))))
-	      status)))
-     ((= rc 1)
-      ;; bad signature
-      (list nil 'bad status))
-     (needkey
-      (list nil 'needkey needkey))
-     (t ;corrupted message?
-      (error (with-current-buffer stderrbuf (buffer-string))))
-     )
+  (let (sigtype sigid sigdate sigtrust)
+    ;; parse FOOSIG with the same code as decrypt-parser
+    (set-buffer statusbuf)
+    (goto-char (point-min))
+    (if (re-search-forward "^\\[GNUPG:\\]\\s +\\(GOOD\\|BAD\\|ERR\\)SIG" nil t)
+	(progn
+	  (setq sigtype (match-string 1))
+	  (goto-char (point-min))
+	  (if (and (or (equal sigtype "GOOD") (equal sigtype "BAD"))
+		   (re-search-forward
+		    "^\\[GNUPG:\\]\\s +\\(GOOD\\|BAD\\)SIG\\s +\\(\\S +\\)\\s +\\(.*\\)$" nil t))
+	      (setq sigid (match-string 3)))
+	  ;; match-string 2 is the hex keyid of the signator. m-s 3 is the name
+	  (goto-char (point-min))
+	  (if (and (equal sigtype "ERR")
+		   (re-search-forward
+		    "^\\[GNUPG:\\]\\s +ERRSIG\\s +\\(\\S +\\)\\s +\\(.*\\)$" nil t))
+	      (setq sigid (concat "0x" (match-string 1))))
+	  ;; match-string 1 is the hex keyid, 2 is the algorithm ID
+	  ;;  (17: DSA, 1,3: RSA, 20: Elgamal)
+	  ;; sigdate is only in stderr. 0.9.4 adds SIG_ID <sigprint> YYYY-MM-DD
+	  ;; but time is only in stderr
+	  (set-buffer stderrbuf)
+	  (goto-char (point-min))
+	  (if (re-search-forward
+	       "^gpg: Signature made \\(.*\\) using" nil t)
+	      (setq sigdate (match-string 1)))
+	  (goto-char (point-min))
+	  ;; get the keyname that made the signature if it wasn't available in
+	  ;; the GOODSIG/BADSIG/ERRSIG status (true for ERRSIG in gpg <0.9.5
+	  ;; where it must be pulled out of stderr)
+	  (if (and (not sigid)
+		   mc-gpg-handle-pre095
+		   (re-search-forward
+		    "^gpg: Signature made .* key ID \\(.*\\)$" nil t))
+	      (setq sigid (concat "0x" (match-string 1))))
+	  (set-buffer statusbuf)
+	  (goto-char (point-min))
+	  (if (re-search-forward "^\\[GNUPG:\\]\\s +\\(TRUST_\\S +\\)$" nil t)
+	      (setq sigtrust (match-string 1)))
+	  ))
+
+    (mc-gpg-debug-print 
+     (format
+      "decrypt-parser: handle-pre095=%s sigtype=%s sigid=%s sigdate=%s sigtrust=%s"
+      mc-gpg-handle-pre095 sigtype sigid sigdate sigtrust))
+    
+    (set-buffer stderrbuf)
+    (goto-char (point-min))
+    (if (and (not (= rc 0)) 
+	     (not sigtype) 
+	     (re-search-forward "^gpg: CRC error" nil t))
+	(error "Corrupt GPG message"))
+
+    (cond
+     ((equal sigtype "ERR")
+      (list nil sigid))
+     ((equal sigtype "GOOD")
+      (list nil (list t sigid sigtrust sigdate))) ;; good sig
+     (t
+      (list nil (list nil sigid sigtrust sigdate))))
     ))
 
 
-;gpg: Signature made Wed Aug  5 17:47:07 1998 using DSA key ID C63B6750
-;gpg: Good signature from "Brian Warner (temporary GPG key) <warner@lothar.com>"
+; check a signature, print a message about its validity. Returns t if the
+; sig was valid, nil otherwise
 
 (defun mc-gpg-verify-region (start end &optional no-fetch)
   (let ((buffer (get-buffer-create mc-buffer-name))
 	(obuf (current-buffer))
-	args gpg-id result)
+	args result)
     (setq args '("--batch" "--verify"))
     (if mc-gpg-alternate-keyring
 	(setq args (append "--keyring" mc-gpg-alternate-keyring args)))
     (setq result (mc-gpg-process-region
 		  start end nil mc-gpg-path args 'mc-gpg-verify-parser buffer))
     (mc-gpg-debug-print (format "process-region returned %s" result))
+    (setq result (car result))
+
     (cond 
-     ((eq (nth 0 result) 'good)
-      (message (nth 1 result)))
-     ((eq (nth 0 result) 'bad)
-      (error (nth 1 result)))
-     ((eq (nth 0 result) 'needkey)
+
+     ((atom result) 
+      ;; need key
       (if (and
 	   (not no-fetch)
-	   (setq gpg-id
-		 (concat "0x" (nth 1 result)))
 	   (not (eq mc-gpg-always-fetch 'never))
 	   (or mc-gpg-always-fetch
 	       (y-or-n-p
-		(format "Key %s not found; attempt to fetch? " gpg-id)))
-	   (mc-gpg-fetch-key (cons nil gpg-id))
+		(format "Key %s not found; attempt to fetch? " result)))
+	   (mc-gpg-fetch-key (cons nil result))
 	   (set-buffer obuf))
 	  (mc-gpg-verify-region start end t)
-	(error "Can't check signature: Public key 0x%s not found" (nth 1 result))))
+	(error "Can't check signature: Public key %s not found" result)))
+
+     ((nth 0 result)
+      ;; good sig
+      (progn
+	(message (mc-gpg-format-sigline
+		  t (nth 1 result) (nth 2 result) (nth 3 result)))
+	t))
+
      (t
-      (error (nth 1 result))))
-    ))
-
+      ;; bad sig
+      (progn
+	(ding)
+	(message (mc-gpg-format-sigline
+		  nil (nth 1 result) (nth 2 result) (nth 3 result)))
+	nil))
+    )
+))
 
 (defun mc-gpg-insert-public-key (&optional id)
   (let ((buffer (get-buffer-create mc-buffer-name))
 (defvar mc-pgp-path "pgp" "*The PGP executable.")
 (defvar mc-pgp-display-snarf-output nil
   "*If t, pop up the PGP output window when snarfing keys.")
+(defvar mc-pgp-always-fetch nil
+  "*If t, always fetch missing keys. If nil, prompt user. If 'never,
+never fetch keys, and don't ask.")
 (defvar mc-pgp-alternate-keyring nil
   "*Public keyring to use instead of default.")
 (defvar mc-pgp-comment
   "*Comment field to appear in ASCII armor output.  If nil, let PGP
 use its default.")
 
-(defconst mc-pgp-msg-begin-line "-----BEGIN PGP MESSAGE-----"
+(defconst mc-pgp-msg-begin-line "^-----BEGIN PGP MESSAGE-----\r?$"
   "Text for start of PGP message delimiter.")
-(defconst mc-pgp-msg-end-line "-----END PGP MESSAGE-----\n?"
+(defconst mc-pgp-msg-end-line "^-----END PGP MESSAGE-----\r?$"
   "Text for end of PGP message delimiter.")
-(defconst mc-pgp-signed-begin-line "-----BEGIN PGP SIGNED MESSAGE-----"
+(defconst mc-pgp-signed-begin-line "^-----BEGIN PGP SIGNED MESSAGE-----\r?$"
   "Text for start of PGP signed messages.")
-(defconst mc-pgp-signed-end-line "-----END PGP SIGNATURE-----"
+(defconst mc-pgp-signed-end-line "^-----END PGP SIGNATURE-----\r?$"
   "Text for end of PGP signed messages.")
 (defconst mc-pgp-key-begin-line "^-----BEGIN PGP PUBLIC KEY BLOCK-----\r?$"
   "Text for start of PGP public key.")
 	   "^\\(\\(pub\\|sec\\)\\s +[^/]+/\\(\\S *\\)\\s +\\S +\\s +\\(.*\\)\\)$")
 	  (revoke-regexp "REVOKED")
 	  (obuf (current-buffer))
-	  buffer)
+	  buffer key-start key-end)
       (if (null result)
 	  (unwind-protect
 	      (progn
 	 (re-search-forward "^File is conventionally encrypted" nil t)))
       (if (null key) (mc-deactivate-passwd t))
       (mc-pgp-decrypt-region start end "***** CONVENTIONAL *****"))
+     ;; Or maybe this is the wrong PGP version
+     ((save-excursion
+	(and
+	 (set-buffer buffer)
+	 (goto-char (point-min))
+	 (re-search-forward "Unsupported packet format" nil t)))
+      (mc-message mc-pgp-error-re buffer "Not encrypted for PGP 2.6"))
      (t
       (mc-display-buffer buffer)
       (if (mc-message "^\aError: +Bad pass phrase\\.$" buffer)
 (defvar mc-pgp50-pgpk-path "pgpk" "*The PGP 5.0 'pgpk' executable.")
 (defvar mc-pgp50-display-snarf-output nil
   "*If t, pop up the PGP output window when snarfing keys.")
+(defvar mc-pgp50-always-fetch nil
+  "*If t, always fetch missing keys. If 'never, never fetch. If nil,
+ask the user.")
 (defvar mc-pgp50-alternate-keyring nil
   "*Public keyring to use instead of default.")
 (defvar mc-pgp50-comment
   "*Comment field to appear in ASCII armor output.  If nil, let PGP
 use its default.")
 
-(defconst mc-pgp50-msg-begin-line "-----BEGIN PGP MESSAGE-----"
+(defconst mc-pgp50-msg-begin-line "^-----BEGIN PGP MESSAGE-----\r?$"
   "Text for start of PGP message delimiter.")
-(defconst mc-pgp50-msg-end-line "-----END PGP MESSAGE-----\n?"
+(defconst mc-pgp50-msg-end-line "^-----END PGP MESSAGE-----\r?$"
   "Text for end of PGP message delimiter.")
-(defconst mc-pgp50-signed-begin-line "-----BEGIN PGP SIGNED MESSAGE-----"
+(defconst mc-pgp50-signed-begin-line "^-----BEGIN PGP SIGNED MESSAGE-----\r?$"
   "Text for start of PGP signed messages.")
-(defconst mc-pgp50-signed-end-line "-----END PGP SIGNATURE-----"
+(defconst mc-pgp50-signed-end-line "^-----END PGP SIGNATURE-----\r?$"
   "Text for end of PGP signed messages.")
 (defconst mc-pgp50-key-begin-line "^-----BEGIN PGP PUBLIC KEY BLOCK-----\r?$"
   "Text for start of PGP public key.")
 	  t)))))
 
 (defun mc-pgp50-decrypt-parser (proc oldbuf start end newbuf passwd)
+ (let (rgn result results)
   (setenv "PGPPASSFD" "0")
   (set-buffer newbuf)
   (goto-char (point-max))
 	     (exit
 	      (setq results (list 
 			     (process-exit-status proc) nil)))))))
-    results))
+    results)))
 
 (defun mc-pgp50-decrypt-region (start end &optional id)
   ;; returns a pair (SUCCEEDED . VERIFIED) where SUCCEEDED is t if
 	  (while (and (not key) keyring-list)
 	    (setq buf (generate-new-buffer " *mailcrypt temp*"))
 	    (setq proc
-		  (start-process "*PGP*" buf mc-pgp50-path "-kxaf"
+		  (start-process "*PGP*" buf mc-pgp50-pgpk-path "-kxaf"
 				 "+verbose=0" "+batchmode"
 				 (format "+pubring=%s" (car keyring-list))
 				 (or (cdr id) (car id))))

File mc-remail.el

 	  (mapconcat 'identity regexps "\\)\\|\\(")
 	  "\\)"))
 
+; Quiet a warning message when the user hasn't set this.
+(defvar gnus-user-from-line nil)
+
 (defun mc-user-mail-address ()
   "Figure out the user's Email address as best we can."
   (mc-strip-address

File mc-setversion.el

 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 ;;}}}
 
+(defvar mc-default-scheme 'mc-scheme-pgp
+  "*Set the default encryption scheme for Malicrypt to use. Defaults
+to pgp 2.6 for backward compatibility.")
+
 (defun mc-setversion (&optional version)
   "Reset path and argument information for the selected version of PGP.
 Possible values of VERSION are 2.6, 5.0, and gpg."

File mc-toplev.el

 ;;{{{ Load some required packages
 (require 'mailcrypt)
 (require 'mail-utils)
+(require 'rfc822)
 
 (eval-when-compile
   ;; RMAIL
   ;; assuming they were possibly extracted from the headers of a reply,
   ;; returns a list of the address components.
   (mapcar 'mc-strip-address
-	  (mc-split "\\([ \t\n]*,[ \t\n]*\\)+" str)))
+	  (rfc822-addresses str)))
 
 (defun mc-find-headers-end ()
   (save-excursion
 
 (defun mc-gnus-verify-signature ()
   (interactive)
-  (gnus-summary-select-article)
+  (gnus-summary-select-article t)
   (save-excursion
     (set-buffer gnus-original-article-buffer)
     (save-restriction (widen) (mc-verify-signature))))
 
 (defun mc-gnus-snarf-keys ()
   (interactive)
-  (gnus-summary-select-article)
+  (gnus-summary-select-article t)
   (gnus-eval-in-buffer-window gnus-original-article-buffer
     (save-restriction (widen) (mc-snarf-keys))))
 
 (defun mc-gnus-decrypt-message ()
   (interactive)
-  (gnus-summary-select-article)
+  (gnus-summary-select-article t)
   ;; Gnus 5 has the string "Gnus" instead of "GNUS" in gnus-version.
   (if (not (let ((case-fold-search nil))
 	     (string-match "Gnus" gnus-version)))
 	(set-buffer mh-show-buffer)
       (let ((tmp (generate-new-buffer "*Mailcrypt Viewing*")))
         (copy-to-buffer tmp (point-min) (point-max))
-        (switch-to-buffer tmp t)
+        ;(switch-to-buffer tmp t)
+	(set-buffer tmp)
         (goto-char (point-min))
         (set-buffer-modified-p nil)
         (if (setq decrypt-okay (car (mc-decrypt-message)))