diff -Ncr pine4.53/imap/src/c-client/mail.c pine4.53-smime/imap/src/c-client/mail.c
*** pine4.53/imap/src/c-client/mail.c	Thu Jan  2 17:28:42 2003
--- pine4.53-smime/imap/src/c-client/mail.c	Wed Jan 15 12:48:15 2003
***************
*** 5198,5203 ****
--- 5198,5207 ----
  
  void mail_free_body_data (BODY *body)
  {
+   /* cleanup body if requested by application */
+   if (body->cleanup)
+     (*body->cleanup)(body);
+ 
    switch (body->type) {		/* free contents */
    case TYPEMULTIPART:		/* multiple part */
      mail_free_body_part (&body->nested.part);
diff -Ncr pine4.53/imap/src/c-client/mail.h pine4.53-smime/imap/src/c-client/mail.h
*** pine4.53/imap/src/c-client/mail.h	Tue Jan  7 12:33:50 2003
--- pine4.53-smime/imap/src/c-client/mail.h	Wed Jan 15 12:48:15 2003
***************
*** 655,660 ****
--- 655,663 ----
      unsigned long bytes;	/* size of text in octets */
    } size;
    char *md5;			/* MD5 checksum */
+ 
+   void *sparep;     	    	/* spare pointer reserved for main program */
+   void (*cleanup)(BODY *);  	/* cleanup function */
  };
  
  
diff -Ncr pine4.53/pine/README.smime pine4.53-smime/pine/README.smime
*** pine4.53/pine/README.smime	Wed Dec 31 16:00:00 1969
--- pine4.53-smime/pine/README.smime	Wed Jan 15 12:48:15 2003
***************
*** 0 ****
--- 1,129 ----
+ Quick Start
+ ===========
+ 
+ To enable S/MIME support, ensure the SSL related lines in the platform
+ makefile are uncommented (they're all next to one another).
+ 
+ CA certificates are expected to be found in the OpenSSL 'cert' dir, in the
+ standard hashed format.
+ 
+ User Configuration
+ ==================
+ 
+ A directory '~/.pine-smime' must be created. Within this, a further 
+ three directories are required:
+ 
+     ~/.pine-smime/
+                   ca/
+                   private/
+                   public/
+ 
+ Other people's certificate files should be copied into the 'public' directory.
+ Your certificate file(s) should be copied into the 'private' directory.
+ Certificates for any additional trusted CAs should be put in the 'ca' directory.
+ 
+ There are three extra configuration options:
+ 
+     sign-default-on
+     encrypt-default-on
+     remember-smime-passphrase
+ 
+ Certificates
+ ============
+ 
+ The certificate files specified above should have the following form:
+ 
+     public certificates: user@emaildomain.crt
+     private keys:        user@emaildomain.key
+ 
+ Thus, a typical installation might look like this:
+ 
+     ~/.pine-smime/
+                   ca/
+                           [additional trusted CAs here]
+                   private/
+                           paisleyj@dcs.gla.ac.uk.crt
+                           paisleyj@dcs.gla.ac.uk.key			  
+                   public/
+                           myfriend@dcs.gla.ac.uk.crt
+                           myotherfriend@dcs.gla.ac.uk.crt
+ 
+ Implementation Details
+ ======================
+ 
+ Link with the OpenSSL crypto library for PKCS7 support.
+ Only tested on linux (slx) and solaris (so5).
+ 
+ Added three extra source files (+headers):
+ 
+     smime.c 	Main S/MIME support code
+     smkeys.c	Very basic X509 key handling/storage (using the above dirs)
+     bss_so.c	OpenSSL BIO using pine STORE_S objects
+ 
+ Patches to existing pine sources:
+ 
+     init.c
+     	    	Add references to new configuration options.
+ 
+     mailcmd.c
+     	    	Add implementation of MC_DECRYPT command which prompts
+ 		the user for a passphrase if it's required.
+  
+     mailpart.c
+     	    	Comment added to help me remember what I'd done.
+     
+     mailview.c
+     	    	Added description of Decrypt menu option.
+ 		Make calls out to smime.c functions to handle the decryption.
+ 		This is done shortly after the BODY of a message is 
+ 		obtained.
+ 		Added function to describe encrypted messages when they're
+ 		being displayed.
+ 		Added code to describe the special case of PKCS7 attachments.
+     
+     makefile.lnx
+     makefile.so5
+     	    	Added SSL variables etc.
+ 
+     pine.h
+     	    	Add enumerations for new configuration options and definition
+ 		of MC_DECRYPT command
+     	    	Exported the prototype of pine_write_body_header,
+ 		pine_rfc822_output_body and pine_encode_body since they're
+ 		needed in smime.c.
+     
+     pine.hlp
+     	    	Added help info for new configuration options.
+     
+     send.c
+     	    	Added 'Encrypt' and 'Sign' menu options when sending email.
+     	    	Make calls to smime.c functions to fiddle message on the 
+ 		way out.
+ 		Extend pine_encode_body so it makes a few more checks
+ 		before adding a boundary.
+ 
+ Basic method:
+ 
+     Incoming
+ 
+     Scan BODY of viewed message before it is formatted. If it contains 
+     PKCS7 parts, decode them and attempt to decrypt/verify signatures. The
+     original BODY is fiddled in place and turned into a multipart body
+     with one subpart -- the decrypted data. This may consist of a multipart
+     with attachments, for example.
+     
+     This all depends on stashing a pointer to the decrypted data in 
+     body->contents.text.data and relying on the fact that the mail_* routines
+     will use this data in preference to fetching it over the network. We
+     also depend on it not being garbage collected while the message is 
+     being viewed!
+     
+     Outgoing
+     
+     smime.c pre-builds the message using pine_encode_body, pine_write_body_header
+     and pine_rfc822_output_body, encrypting/signing the resulting data. The
+     body that was going to be sent is then fiddled appropriately after
+     the PKCS7 objects have been built.
+     
+ paisleyj@dcs.gla.ac.uk
+ Mar 7 2001
diff -Ncr pine4.53/pine/TODO.smime pine4.53-smime/pine/TODO.smime
*** pine4.53/pine/TODO.smime	Wed Dec 31 16:00:00 1969
--- pine4.53-smime/pine/TODO.smime	Wed Jan 15 12:48:15 2003
***************
*** 0 ****
--- 1,31 ----
+ 
+     Need to be able to view stored certificates to see details
+     (particularly the fingerprint for comparing over the phone, say)
+     	--> proper key management system
+ 
+     Add client private key and certificate request generation.
+ 
+     Send certificate for CA along with certificate of signer.
+ 
+     Verify recipient certificate before sending encrypted message.
+ 
+     Verify certificates in general.
+ 
+     Cache the result of pre-formatting the message during the send/encrypt/sign
+     phase rather than letting call_mailer re-format it all over again.
+ 
+     Tidy up the use of global variables considerably.
+ 
+     Intelligently pick a certificate for signing purposes based on the
+     From address rather than just picking the first one on the list.
+ 
+     Figure out platform dependancies from using readdir() in smkeys.c
+ 
+     Handle message/rfc822 sub-parts!
+ 
+     Consider what happens with all our cached data.
+     
+     S/MIME info screen help.
+ 
+ paisleyj@dcs.gla.ac.uk
+ Mar 7 2001
diff -Ncr pine4.53/pine/bss_so.c pine4.53-smime/pine/bss_so.c
*** pine4.53/pine/bss_so.c	Wed Dec 31 16:00:00 1969
--- pine4.53-smime/pine/bss_so.c	Wed Jan 15 12:48:15 2003
***************
*** 0 ****
--- 1,184 ----
+ #ifdef SMIME
+ 
+ /*
+     bss_so.c
+     
+     Basic implementation of an OpenSSL BIO which is
+     backed by a pine STORE_S object
+  */
+ 
+ #include "headers.h"
+ 
+ #include <stdio.h>
+ #include <errno.h>
+ #include <openssl/bio.h>
+ #include <openssl/err.h>
+ 
+ static int bss_so_write(BIO *h, char *buf, int num);
+ static int bss_so_read(BIO *h, char *buf, int size);
+ static int bss_so_puts(BIO *h, char *str);
+ static int bss_so_gets(BIO *h, char *str, int size);
+ static long bss_so_ctrl(BIO *h, int cmd, long arg1, char *arg2);
+ static int bss_so_new(BIO *h);
+ static int bss_so_free(BIO *data);
+ static BIO_METHOD methods_sop =
+     {
+ 	BIO_TYPE_MEM,
+ 	"Storage Object",
+ 	bss_so_write,
+ 	bss_so_read,
+ 	bss_so_puts,
+ 	bss_so_gets,
+ 	bss_so_ctrl,
+ 	bss_so_new,
+ 	bss_so_free,
+ 	NULL,
+     };
+ 
+ BIO *BIO_new_so(STORE_S *so)
+ {
+     BIO *ret;
+ 
+     if ((ret = BIO_new(&methods_sop)) == NULL)
+         return (NULL);
+ 
+     BIO_set_fp(ret, (FILE*) so, 0);
+     return (ret);
+ }
+ 
+ 
+ static int bss_so_new(BIO *bi)
+ {
+     bi->init = 0;
+     bi->num = 0;
+     bi->ptr = NULL;
+     return (1);
+ }
+ 
+ static int bss_so_free(BIO *a)
+ {
+     if (a == NULL) return (0);
+     if (a->shutdown) {
+         if ((a->init) && (a->ptr != NULL)) {
+             a->ptr = NULL;
+         }
+         a->init = 0;
+     }
+     return (1);
+ }
+ 
+ static int bss_so_read(BIO *b, char *out, int outl)
+ {
+     int ret = 0;
+     STORE_S *so = (STORE_S*) b->ptr;
+ 
+     if (b->init && (out != NULL)) {
+ 
+         while (ret < outl) {
+             if (!so->readc((unsigned char *)out, so))
+                 break;
+             out++;
+             ret++;
+         }
+ 
+     }
+     return (ret);
+ }
+ 
+ static int bss_so_write(BIO *b, char *in, int inl)
+ {
+     int ret = 0;
+ 
+     if (b->init && (in != NULL)) {
+         if (so_nputs((STORE_S *)b->ptr, in, inl))
+             ret = inl;
+ 
+     }
+     return (ret);
+ }
+ 
+ static long bss_so_ctrl(BIO *b, int cmd, long num, char *ptr)
+ {
+     long ret = 1;
+     STORE_S *so = (STORE_S *)b->ptr;
+     FILE **fpp;
+     char p[4];
+ 
+     switch (cmd) {
+         case BIO_C_FILE_SEEK:
+         case BIO_CTRL_RESET:
+             ret = so_seek(so, num, 0);
+             break;
+         case BIO_CTRL_EOF:
+             ret = 0;
+             break;
+         case BIO_C_FILE_TELL:
+         case BIO_CTRL_INFO:
+             ret = 0;
+             break;
+         case BIO_C_SET_FILE_PTR:
+             bss_so_free(b);
+             b->shutdown = (int)num & BIO_CLOSE;
+             b->ptr = (char *)ptr;
+             b->init = 1;
+             break;
+         case BIO_C_SET_FILENAME:
+             ret = 0;
+             break;
+         case BIO_C_GET_FILE_PTR:
+             if (ptr != NULL) {
+                 fpp = (FILE **)ptr;
+                 *fpp = (FILE *)NULL;
+             }
+             break;
+         case BIO_CTRL_GET_CLOSE:
+             ret = (long)b->shutdown;
+             break;
+         case BIO_CTRL_SET_CLOSE:
+             b->shutdown = (int)num;
+             break;
+         case BIO_CTRL_FLUSH:
+             break;
+         case BIO_CTRL_DUP:
+             ret = 1;
+             break;
+ 
+         case BIO_CTRL_WPENDING:
+         case BIO_CTRL_PENDING:
+         case BIO_CTRL_PUSH:
+         case BIO_CTRL_POP:
+         default:
+             ret = 0;
+             break;
+     }
+     return (ret);
+ }
+ 
+ static int bss_so_gets(BIO *bp, char *buf, int size)
+ {
+     int ret = 0;
+     char	*b = buf;
+     char	*bend = buf + size - 1;
+     STORE_S *so = (STORE_S*) bp->ptr;
+ 
+     do {
+         if (!so->readc((unsigned char *)b, so))
+             break;
+         b++;
+     } while (b < bend && b[ -1] != '\n');
+ 
+     *b = 0;
+ 
+     ret = b - buf;
+     return (ret);
+ }
+ 
+ static int bss_so_puts(BIO *bp, char *str)
+ {
+     STORE_S *so = (STORE_S*) bp->ptr;
+ 
+     return so->puts(so, str);
+ }
+ 
+ 
+ #endif /* SMIME */
diff -Ncr pine4.53/pine/bss_so.h pine4.53-smime/pine/bss_so.h
*** pine4.53/pine/bss_so.h	Wed Dec 31 16:00:00 1969
--- pine4.53-smime/pine/bss_so.h	Wed Jan 15 12:48:15 2003
***************
*** 0 ****
--- 1 ----
+ BIO *BIO_new_so(STORE_S *so);
diff -Ncr pine4.53/pine/filter.c pine4.53-smime/pine/filter.c
*** pine4.53/pine/filter.c	Wed Jan  8 12:13:50 2003
--- pine4.53-smime/pine/filter.c	Wed Jan 15 12:48:15 2003
***************
*** 903,909 ****
  
  
  /* get a character from a file */
! /* assumes gf_out struct is filled in */
  int
  gf_freadc(c)
  unsigned char *c;
--- 903,909 ----
  
  
  /* get a character from a file */
! /* assumes gf_in struct is filled in */
  int
  gf_freadc(c)
  unsigned char *c;
***************
*** 938,944 ****
  
  
  /* get a character from a string, return nonzero if things OK */
! /* assumes gf_out struct is filled in */
  int
  gf_sreadc(c)
  unsigned char *c;
--- 938,944 ----
  
  
  /* get a character from a string, return nonzero if things OK */
! /* assumes gf_in struct is filled in */
  int
  gf_sreadc(c)
  unsigned char *c;
diff -Ncr pine4.53/pine/init.c pine4.53-smime/pine/init.c
*** pine4.53/pine/init.c	Mon Jan  6 19:39:16 2003
--- pine4.53-smime/pine/init.c	Wed Jan 15 12:48:15 2003
***************
*** 2445,2451 ****
  	 F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND},
  	{"use-sender-not-x-sender",
  	 F_USE_SENDER_NOT_X, h_config_use_sender_not_x, PREF_SEND},
! 
  /* Folder */
  	{"combined-subdirectory-display",
  	 F_CMBND_SUBDIR_DISP, h_config_combined_subdir_display, PREF_FLDR},
--- 2445,2458 ----
  	 F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND},
  	{"use-sender-not-x-sender",
  	 F_USE_SENDER_NOT_X, h_config_use_sender_not_x, PREF_SEND},
! #ifdef SMIME
! 	{"sign-default-on",
! 	 F_SIGN_DEFAULT_ON, h_config_sign_default_on, PREF_SEND},
! 	{"encrypt-default-on",
! 	 F_ENCRYPT_DEFAULT_ON, h_config_encrypt_default_on, PREF_SEND},
! 	{"remember-smime-passphrase",
! 	 F_REMEMBER_SMIME_PASSPHRASE, h_config_remember_smime_passphrase, PREF_SEND},
! #endif
  /* Folder */
  	{"combined-subdirectory-display",
  	 F_CMBND_SUBDIR_DISP, h_config_combined_subdir_display, PREF_FLDR},
diff -Ncr pine4.53/pine/mailcmd.c pine4.53-smime/pine/mailcmd.c
*** pine4.53/pine/mailcmd.c	Fri Jan 10 15:29:29 2003
--- pine4.53-smime/pine/mailcmd.c	Wed Jan 15 12:48:15 2003
***************
*** 53,58 ****
--- 53,59 ----
  #include "headers.h"
  #include "../c-client/imap4r1.h"
  
+ #include "smime.h"
  
  /*
   * Internal Prototypes
***************
*** 1439,1444 ****
--- 1440,1458 ----
  	break;
  
  
+ #ifdef SMIME
+           /*------- Try to decrypt message -----------*/
+ 	case MC_DECRYPT:
+ 	    if (g_need_passphrase)
+ 		get_passphrase();
+ 	    a_changed = TRUE;
+ 	break;
+ 
+ 	case MC_SECURITY:
+     	    state->next_screen = smime_info_screen;
+ 	break;
+ #endif
+ 		
            /*------- Bounce -----------*/
        case MC_BOUNCE :
  	cmd_bounce(state, msgmap, 0);
diff -Ncr pine4.53/pine/mailpart.c pine4.53-smime/pine/mailpart.c
*** pine4.53/pine/mailpart.c	Wed Nov 27 15:22:54 2002
--- pine4.53-smime/pine/mailpart.c	Wed Jan 15 12:48:15 2003
***************
*** 4534,4539 ****
--- 4534,4545 ----
      frd->flags   = flags;
      frd->size    = size;
      frd->readc	 = fetch_readc;
+ 	
+     /*	The call to imap_cache below will return true in the case where
+ 	we've already stashed fake data in the content of the part.
+ 	This happens when an S/MIME message is decrypted.
+      */
+ 	
      if(modern_imap_stream(stream)
         && !imap_cache(stream, msgno, section, NULL, NULL)
         && size > INIT_FETCH_CHUNK
diff -Ncr pine4.53/pine/mailview.c pine4.53-smime/pine/mailview.c
*** pine4.53/pine/mailview.c	Tue Jan  7 16:17:00 2003
--- pine4.53-smime/pine/mailview.c	Wed Jan 15 12:48:15 2003
***************
*** 47,52 ****
--- 47,53 ----
  
  
  #include "headers.h"
+ #include "smime.h"
  
  
  /*----------------------------------------------------------------------
***************
*** 207,214 ****
--- 208,220 ----
  
  	HELP_MENU,
  	OTHER_MENU,
+ #ifdef SMIME
+ 	{"^D","Decrypt", {MC_DECRYPT,1,{ctrl('d')},KS_NONE}},
+ 	{"^E","Security", {MC_SECURITY,1,{ctrl('e')},KS_NONE}},
+ #else
  	NULL_MENU,
  	NULL_MENU,
+ #endif
  	RCOMPOSE_MENU,
  	NULL_MENU,
  	NULL_MENU,
***************
*** 227,232 ****
--- 233,240 ----
  #define BOUNCE_KEY		33
  #define FLAG_KEY		34
  #define VIEW_PIPE_KEY		35
+ #define DECRYPT_KEY		(VIEW_PIPE_KEY + 3)
+ #define SECURITY_KEY		(DECRYPT_KEY + 1)
  
  static struct key simple_text_keys[] =
         {HELP_MENU,
***************
*** 432,437 ****
--- 440,447 ----
  	else
  	  ps->unseen_in_view = !mc->seen;
  
+ 	flags = 0;
+ 
  #if	defined(DOS) && !defined(WIN32)
  	/* 
  	 * Handle big text for DOS here.
***************
*** 459,465 ****
  			 ps->ttyo->screen_rows - (SCROLL_LINES_ABOVE(ps)
  						  + SCROLL_LINES_BELOW(ps)));
  
! 	flags = FM_DISPLAY;
  	if((last_message_viewed != mn_get_cur(ps->msgmap)
  	    || last_was_full_header == 1))
  	  flags |= FM_NEW_MESS;
--- 469,475 ----
  			 ps->ttyo->screen_rows - (SCROLL_LINES_ABOVE(ps)
  						  + SCROLL_LINES_BELOW(ps)));
  
! 	flags |= FM_DISPLAY;
  	if((last_message_viewed != mn_get_cur(ps->msgmap)
  	    || last_was_full_header == 1))
  	  flags |= FM_NEW_MESS;
***************
*** 467,472 ****
--- 477,488 ----
  	if(offset)		/* no pre-paint during resize */
  	  view_writec_killbuf();
  
+ #ifdef SMIME
+ 	/* Attempt to handle S/MIME bodies */
+ 	if (fiddle_smime_message(body,raw_msgno,(flags&FM_NEW_MESS)!=0))
+ 	 flags |= FM_NEW_MESS; /* body was changed, force a reload */
+ #endif
+ 
  #ifdef _WINDOWS
  	mswin_noscrollupdate(1);
  #endif
***************
*** 541,546 ****
--- 557,567 ----
  
  	if(F_OFF(F_ENABLE_FULL_HDR, ps_global))
  	  clrbitn(VIEW_FULL_HEADERS_KEY, scrollargs.keys.bitmap);
+ 	  
+ #ifdef SMIME
+ 	if (!g_need_passphrase)
+ 	  clrbitn(DECRYPT_KEY, scrollargs.keys.bitmap);
+ #endif
  
  	if(!handles){
  	    /*
***************
*** 754,760 ****
--- 775,821 ----
  }
  
  
+ #ifdef SMIME
+ /*----------------------------------------------------------------------
+     Add descriptive lines to the top of a message being formatted
+     that describe the status of any S/MIME enclosures that
+     have been encountered.
+     
+   Args: body   -- top-level body of the message being described
+         pc -- output function for writing to the message display
+ 
+   ----*/
+ static int describe_smime_bodies(BODY *body,gf_io_t pc)
+ {
+     PART      *part;
+     int result = 0;
+    
+     if (!body)
+     	return result;
+     
+     if (body->type == TYPEMULTIPART) {
+     
+     	if (body->subtype && strucmp(body->subtype,"x-pkcs7-enclosure")==0) {
+ 
+     	    if (body->description) {
+         	format_editorial(body->description,ps_global->ttyo->screen_cols,pc);
+ 		gf_puts(NEWLINE,pc);
+ 		result = 1;
+ 	    }
  
+ 	    for (part=body->nested.part; part; part=part->next) {
+ 		result |= describe_smime_bodies(&(part->body),pc);
+ 	    }
+ 	
+ 	}
+     } else if (body->type == TYPEMESSAGE && 
+     	    body->subtype && strucmp(body->subtype, "rfc822")==0) {
+ 	result |= describe_smime_bodies(body->nested.msg->body,pc);
+     }
+     
+     return result;
+ }
+ #endif
  
  /*----------------------------------------------------------------------
      Add lines to the attachments structure
***************
*** 1677,1682 ****
--- 1738,1751 ----
  
      show_parts = 0;
  
+ #ifdef SMIME
+     if (flgs & FM_DISPLAY) {
+         if (describe_smime_bodies(body,pc)) {
+ 	    gf_puts(NEWLINE, pc);
+ 	}
+     }
+ #endif
+ 
      /*======== Now loop through formatting all the parts =======*/
      for(a = ps_global->atmts; a->description != NULL; a++) {
  
***************
*** 6150,6155 ****
--- 6219,6236 ----
  
      t = &tmp_20k_buf[strlen(tmp_20k_buf)];
  
+ #ifdef SMIME
+     if (is_pkcs7_body(body) && type!=3) { /* if smime and not attempting print */
+ 
+     	sstrcpy(&t,"\015\012");
+ 	
+     	sstrcpy(&t,
+     	"This part is a PKCS7 S/MIME enclosure. "
+ 	"You may be able to view it by entering the correct passphrase "
+     	"with the \"^D\" command. Press \"^E\" for more information.");
+     
+     } else 
+ #endif
      if(type){
  	sstrcpy(&t, "\015\012");
  	switch(type) {
diff -Ncr pine4.53/pine/makefile.lnx pine4.53-smime/pine/makefile.lnx
*** pine4.53/pine/makefile.lnx	Tue Sep 10 14:34:39 2002
--- pine4.53-smime/pine/makefile.lnx	Wed Jan 15 12:48:15 2003
***************
*** 60,79 ****
  LDAPOFILES=   addrbook.o adrbkcmd.o args.o bldaddr.o init.o \
  	      mailview.o other.o pine.o strings.o takeaddr.o
  
  STDLIBS=     -lncurses
  LOCLIBS=     $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
  LIBS=        $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
               `cat $(CCLIENTDIR)/LDFLAGS`
  
  STDCFLAGS=   -DLNX -DSYSTYPE=\"LNX\" -DMOUSE
  CFLAGS=      $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
  	     $(STDCFLAGS)
  
  OFILES=	addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
  	folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
  	mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
  	reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
! 	os.o
  
  HFILES=	headers.h os.h pine.h context.h helptext.h \
  	$(PICODIR)/headers.h $(PICODIR)/estruct.h \
--- 60,92 ----
  LDAPOFILES=   addrbook.o adrbkcmd.o args.o bldaddr.o init.o \
  	      mailview.o other.o pine.o strings.o takeaddr.o
  
+ SSLDIR=      $(HOME)/local/ssl
+ SSLCERTS=    $(SSLDIR)/certs
+ SSLINCLUDE=  $(SSLDIR)/include
+ SSLLIB=      $(SSLDIR)/lib
+ 
+ SSLCFLAGS=   -I$(SSLINCLUDE) \
+ 	     -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" \
+ 	     -DSMIME
+ SSLLDFLAGS=  -L$(SSLLIB) -lcrypto
+ 
+ 
  STDLIBS=     -lncurses
  LOCLIBS=     $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
  LIBS=        $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
+              $(SSLLDFLAGS) \
               `cat $(CCLIENTDIR)/LDFLAGS`
  
  STDCFLAGS=   -DLNX -DSYSTYPE=\"LNX\" -DMOUSE
  CFLAGS=      $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
+  	     $(SSLCFLAGS) \
  	     $(STDCFLAGS)
  
  OFILES=	addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
  	folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
  	mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
  	reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
! 	os.o bss_so.o smime.o smkeys.o
  
  HFILES=	headers.h os.h pine.h context.h helptext.h \
  	$(PICODIR)/headers.h $(PICODIR)/estruct.h \
***************
*** 135,137 ****
--- 148,154 ----
  		osdep/sendmail osdep/execview \
  		osdep/postreap.wtp osdep/os-lnx.ic
  		cd osdep; $(MAKE) includer os-lnx.c; cd ..
+ 
+ jon.o: jon.c
+ 	$(CC) $(CFLAGS) -Wall -Wstrict-prototypes  -c $< -o $@
+ 
diff -Ncr pine4.53/pine/makefile.so5 pine4.53-smime/pine/makefile.so5
*** pine4.53/pine/makefile.so5	Tue Oct 23 15:24:51 2001
--- pine4.53-smime/pine/makefile.so5	Wed Jan 15 12:48:15 2003
***************
*** 62,67 ****
--- 62,78 ----
  LDAPOFILES=   addrbook.o adrbkcmd.o args.o bldaddr.o init.o \
  	      mailview.o other.o pine.o strings.o takeaddr.o
  
+ SSLDIR=      $(HOME)/local/ssl
+ SSLCERTS=    $(SSLDIR)/certs
+ SSLINCLUDE=  $(SSLDIR)/include
+ SSLLIB=      $(SSLDIR)/lib
+ 
+ SSLCFLAGS=   -I$(SSLINCLUDE) \
+              -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" \
+ 	     -DSMIME
+ SSLLDFLAGS=  -L$(SSLLIB) -lcrypto
+ 
+ 
  # LDCC= /usr/bin/cc
  # If you don't have /usr/bin/cc (our Solaris 2.2 doesn't seem to have it,
  # it only has /usr/ucb/cc) then change LDCC to the following line and
***************
*** 72,88 ****
  STDLIBS=     -ltermlib
  LOCLIBS=     $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
  LIBS=        $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
               `cat $(CCLIENTDIR)/LDFLAGS`
  
  STDCFLAGS=   -Dconst= -DSV4 -DSYSTYPE=\"SOL\" -DMOUSE
  CFLAGS=      $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
  	     $(STDCFLAGS)
  
  OFILES=	addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
  	folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
  	mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
  	reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
! 	os.o
  
  HFILES=	headers.h os.h pine.h context.h helptext.h \
  	$(PICODIR)/headers.h $(PICODIR)/estruct.h \
--- 83,101 ----
  STDLIBS=     -ltermlib
  LOCLIBS=     $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
  LIBS=        $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
+              $(SSLLDFLAGS) \
               `cat $(CCLIENTDIR)/LDFLAGS`
  
  STDCFLAGS=   -Dconst= -DSV4 -DSYSTYPE=\"SOL\" -DMOUSE
  CFLAGS=      $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
+  	     $(SSLCFLAGS) \
  	     $(STDCFLAGS)
  
  OFILES=	addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
  	folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
  	mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
  	reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
! 	os.o bss_so.o smime.o smkeys.o
  
  HFILES=	headers.h os.h pine.h context.h helptext.h \
  	$(PICODIR)/headers.h $(PICODIR)/estruct.h \
diff -Ncr pine4.53/pine/pine.h pine4.53-smime/pine/pine.h
*** pine4.53/pine/pine.h	Fri Jan 10 15:25:55 2003
--- pine4.53-smime/pine/pine.h	Wed Jan 15 12:48:15 2003
***************
*** 1134,1139 ****
--- 1134,1144 ----
  	F_DISABLE_SHARED_NAMESPACES,
  	F_EXPOSE_HIDDEN_CONFIG,
  	F_ALT_COMPOSE_MENU,
+ #ifdef SMIME
+ 	F_SIGN_DEFAULT_ON,
+ 	F_ENCRYPT_DEFAULT_ON,
+ 	F_REMEMBER_SMIME_PASSPHRASE,
+ #endif
  	F_ALWAYS_SPELL_CHECK,
  	F_QUELL_TIMEZONE,
  	F_COLOR_LINE_IMPORTANT,
***************
*** 2031,2036 ****
--- 2036,2043 ----
      } cmdline_val;		/* user typed as cmdline arg                */
  };
  
+ #define MC_DECRYPT 800
+ #define MC_SECURITY 801
  
  
  /*
***************
*** 4368,4373 ****
--- 4375,4383 ----
  char	   *pine_send_status PROTO((int, char *, char *, int *));
  void	    phone_home PROTO((char *));
  void	    pine_free_body PROTO((BODY **));
+ int	    pine_write_body_header PROTO((BODY *, soutr_t, TCPSTREAM *));
+ long        pine_rfc822_output_body PROTO((BODY *,soutr_t,TCPSTREAM *));
+ void       pine_encode_body PROTO((BODY *));
  void	    simple_header_parse PROTO((char *, char **, char **));
  int	    valid_subject PROTO((char *, char **, char **,BUILDER_ARG *,int *));
  long	    new_mail_for_pico PROTO((int, int));
diff -Ncr pine4.53/pine/pine.hlp pine4.53-smime/pine/pine.hlp
*** pine4.53/pine/pine.hlp	Wed Jan 15 11:55:09 2003
--- pine4.53-smime/pine/pine.hlp	Wed Jan 15 12:48:15 2003
***************
*** 24338,24340 ****
--- 24338,24347 ----
  ========== h_select_by_smaller_size ==========
  Enter a number or ^C to cancel. All messages less than this many characters
  in size will be selected. Examples: 2176, 1.53K (1530), or 3M (3000000).
+ ========== h_config_sign_default_on ==========
+ If enabled, the 'Sign' option will default to on when sending messages.
+ ========== h_config_encrypt_default_on ==========
+ If enabled, the 'Encrypt' option will default to on when sending messages.
+ ========== h_config_remember_smime_passphrase ==========
+ If enabled, you will only have to enter your passphrase for your private key
+ once during a pine session.
diff -Ncr pine4.53/pine/send.c pine4.53-smime/pine/send.c
*** pine4.53/pine/send.c	Tue Jan 14 13:22:59 2003
--- pine4.53-smime/pine/send.c	Wed Jan 15 12:48:15 2003
***************
*** 50,55 ****
--- 50,56 ----
  #include "../c-client/smtp.h"
  #include "../c-client/nntp.h"
  
+ #include "smime.h"
  
  #ifndef TCPSTREAM
  #define TCPSTREAM void
***************
*** 5490,5495 ****
--- 5491,5513 ----
  	opts[i++].label = "";
      }
  
+ #ifdef SMIME
+     {
+     	opts[i].ch  	= 'e';
+ 	opts[i].rval	= 'e';
+ 	opts[i].name	= "E";
+ 	opts[i++].label	= "Encrypt";
+     
+     	opts[i].ch  	= 'g';
+ 	opts[i].rval	= 'g';
+ 	opts[i].name	= "G";
+ 	opts[i++].label	= "Sign";
+     
+ 	g_do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON,ps_global);
+ 	g_do_sign = F_ON(F_SIGN_DEFAULT_ON,ps_global);
+     }		
+ #endif
+ 
      opts[i].ch = -1;
      no_help = (i >= 12);
  
***************
*** 5574,5579 ****
--- 5592,5627 ----
  	    sstrcpy(&optp, dsn_string);
  	}
  
+ #ifdef SMIME
+     	if (g_do_encrypt) {
+ 	    if(!lparen){
+ 	      *optp++ = ' ';
+ 	      *optp++ = '(';
+ 	      lparen++;
+ 	    }
+ 	    else{
+ 		*optp++ = ',';
+ 		*optp++ = ' ';
+ 	    }
+ 
+ 	    sstrcpy(&optp, "Encrypted");
+ 	}
+ 	
+     	if (g_do_sign) {
+ 	    if(!lparen){
+ 	      *optp++ = ' ';
+ 	      *optp++ = '(';
+ 	      lparen++;
+ 	    }
+ 	    else{
+ 		*optp++ = ',';
+ 		*optp++ = ' ';
+ 	    }
+ 
+ 	    sstrcpy(&optp, "Signed");
+ 	}
+ #endif
+ 
  	if(lparen)
  	  *optp++ = ')';
  
***************
*** 5675,5680 ****
--- 5723,5734 ----
  	     * body on failure.
  	     */
  	    dsn_requested = (DSN_SHOW | DSN_SUCCESS | DSN_DELAY | DSN_FULL);
+ #ifdef SMIME
+ 	} else if (rv=='e') {
+ 	    g_do_encrypt = !g_do_encrypt;
+ 	} else if (rv=='g') {
+ 	    g_do_sign = !g_do_sign;
+ #endif
  	}
  
  	sprintf(dsn_string, "DSN requested[%s%s%s%s]",
***************
*** 6418,6423 ****
--- 6472,6478 ----
      char	*verbose_file = NULL;
      BODY	*bp = NULL;
      PINEFIELD	*pf;
+     BODY    	*origBody = body;
  
  #define MAX_ADDR_ERROR 2  /* Only display 2 address errors */
  
***************
*** 6434,6439 ****
--- 6489,6518 ----
  	return(0);
      }
  
+ 
+ #ifdef SMIME
+     if (g_do_encrypt || g_do_sign) {
+     	int result;
+ 	
+     	STORE_S *so = lmc.so;
+ 	lmc.so = NULL;
+     
+     	result = 1;
+     
+     	if (g_do_encrypt)
+     	    result = encrypt_outgoing_message(header,&body);
+ 	
+ 	/* need to free new body from encrypt if sign fails? */
+ 	if (result && g_do_sign)
+ 	    result = sign_outgoing_message(header,&body,g_do_encrypt);
+ 	
+ 	lmc.so = so;
+ 	
+ 	if (!result)
+ 	    return 0;
+     }
+ #endif
+ 
      /* set up counts and such to keep track sent percentage */
      send_bytes_sent = 0;
      gf_filter_init();				/* zero piped byte count, 'n */
***************
*** 6742,6747 ****
--- 6821,6844 ----
        mail_free_envelope(&fake_env);
  
    done:
+   
+ #ifdef SMIME
+     /* Free replacement encrypted body */
+     if (body != origBody) {
+     
+     	if (body->type==TYPEMULTIPART) {
+ 	    /* Just get rid of first part, it's actually origBody */
+ 	    void *x = body->nested.part;
+ 	    
+ 	    body->nested.part = body->nested.part->next;
+ 	    
+ 	    fs_give(&x);
+ 	}
+     
+     	pine_free_body(&body);
+     }
+ #endif
+   
      if(we_cancel)
        cancel_busy_alarm(0);
  
***************
*** 8721,8733 ****
    dprint(4, (debugfile, "-- pine_encode_body: %d\n", body ? body->type : 0));
    if (body) switch (body->type) {
    case TYPEMULTIPART:		/* multi-part */
!     if (!body->parameter) {	/* cookie not set up yet? */
        char tmp[MAILTMPLEN];	/* make cookie not in BASE64 or QUOTEPRINT*/
        sprintf (tmp,"%ld-%ld-%ld=:%ld",gethostid (),random (),time (0),
  	       getpid ());
!       body->parameter = mail_newbody_parameter ();
!       body->parameter->attribute = cpystr ("BOUNDARY");
!       body->parameter->value = cpystr (tmp);
      }
      part = body->nested.part;	/* encode body parts */
      do pine_encode_body (&part->body);
--- 8818,8834 ----
    dprint(4, (debugfile, "-- pine_encode_body: %d\n", body ? body->type : 0));
    if (body) switch (body->type) {
    case TYPEMULTIPART:		/* multi-part */
!     if (!body->parameter || strucmp(body->parameter->attribute,"BOUNDARY")!=0) {	/* cookie not set up yet? */
        char tmp[MAILTMPLEN];	/* make cookie not in BASE64 or QUOTEPRINT*/
+       PARAMETER *param;
+       
        sprintf (tmp,"%ld-%ld-%ld=:%ld",gethostid (),random (),time (0),
  	       getpid ());
!       param = mail_newbody_parameter ();
!       param->next = body->parameter;
!       param->attribute = cpystr ("BOUNDARY");
!       param->value = cpystr (tmp);
!       body->parameter = param;
      }
      part = body->nested.part;	/* encode body parts */
      do pine_encode_body (&part->body);
diff -Ncr pine4.53/pine/smime.c pine4.53-smime/pine/smime.c
*** pine4.53/pine/smime.c	Wed Dec 31 16:00:00 1969
--- pine4.53-smime/pine/smime.c	Wed Jan 15 12:48:15 2003
***************
*** 0 ****
--- 1,1776 ----
+ /*
+     File:   	    smime.c
+     Author: 	    paisleyj@dcs.gla.ac.uk
+     Date:   	    01/2001
+     
+     Description:
+     	    	    This file contains all the low-level functions
+ 		    required for dealing with S/MIME objects.
+ 		    
+ 		    References are made to the functions in this file 
+ 		    from the following locations:
+ 		    
+ 		    	mailview.c:part_desc()  -> is_pkcs7_body()
+ 			send.c:call_mailer()	-> encrypt_outgoing_message()
+ 			send.c:call_mailer()	-> sign_outgoing_message()
+ 			mailcmd.c:process_cmd() -> get_passphrase()
+ 			mailcmd.c:process_cmd() -> smime_info_screen()
+  */
+ 
+ #ifdef SMIME
+ 
+ #include "headers.h"
+ 
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <time.h>
+ 
+ #include <openssl/err.h>
+ #include <openssl/objects.h>
+ #include <openssl/evp.h>
+ 
+ #include <openssl/x509.h>
+ #include <openssl/pkcs7.h>
+ #include <openssl/pem.h>
+ #include <openssl/rand.h>
+ 
+ #include "bss_so.h"
+ #include "smkeys.h"
+ #include "smime.h"
+ 
+ #define PINE_SMIME_DIRNAME ".pine-smime"
+ 
+ /*  Set true if loading a key failed due to lack of passphrase.
+     Queried in mailcmd.c:process_cmd() before calling get_passphrase()
+  */
+ int 	    	g_need_passphrase = 0;
+ /*  User has entered a passphrase */
+ static int 	s_entered_passphrase = 0;
+ /*  Storage for the entered passphrase */
+ static char	    	s_passphrase[80];
+ static char	    	*s_passphrase_emailaddr;
+ 
+ /*  Set true if encrypting/signing (respectively)
+     Referenced from send.c:call_mailer() and send.c:send_exit_for_pico
+  */
+ int 	    	g_do_encrypt;
+ int 	    	g_do_sign;
+ 
+ /*  Full pathname to ~/.pine-smime */
+ static char 	*g_pine_smime_dir;
+ 
+ static BIO	*bio_err;
+ 
+ /*  Linked list of PERSONAL_CERT objects */
+ static PERSONAL_CERT *s_personal_certs;
+ 
+ static X509_STORE   *s_cert_store;
+ 
+ /* State management for randomness functions below */
+ static int seeded = 0;
+ static int egdsocket = 0;
+ 
+ /* Forget any cached private keys */
+ static void forget_private_keys()
+ {
+     PERSONAL_CERT   *pcert;
+     
+     for (pcert=s_personal_certs; pcert; pcert=pcert->next) {
+     	
+     	if (pcert->key) {
+ 	    EVP_PKEY_free(pcert->key);
+ 	    pcert->key = NULL;
+ 	}
+     }
+ }
+ 
+ /* taken from openssl/apps/app_rand.c */
+ static int app_RAND_load_file(const char *file, BIO *bio_e, int dont_warn)
+ {
+     int consider_randfile = (file == NULL);
+     char buffer[200];
+ 
+     if (file == NULL)
+ 	    file = RAND_file_name(buffer, sizeof buffer);
+     else if (RAND_egd(file) > 0)
+     {
+ 	/* we try if the given filename is an EGD socket.
+ 	   if it is, we don't write anything back to the file. */
+ 	egdsocket = 1;
+ 	return 1;
+     }
+     if (file == NULL || !RAND_load_file(file, -1))
+     {
+ 	if (RAND_status() == 0 && !dont_warn)
+ 	{
+ 	    BIO_printf(bio_e,"unable to load 'random state'\n");
+ 	    BIO_printf(bio_e,"This means that the random number generator has not been seeded\n");
+ 	    BIO_printf(bio_e,"with much random data.\n");
+ 	    if (consider_randfile) /* explanation does not apply when a file is explicitly named */
+ 	    {
+ 		BIO_printf(bio_e,"Consider setting the RANDFILE environment variable to point at a file that\n");
+ 		BIO_printf(bio_e,"'random' data can be kept in (the file will be overwritten).\n");
+ 	    }
+ 	}
+ 	return 0;
+     }
+     seeded = 1;
+     return 1;
+ }
+ 
+ /* copied and fiddled from pine/imap/src/osdep/unix/auth_ssl.c */
+ static void openssl_extra_randomness(void)
+ {
+ #if !defined(WIN32)
+     int fd;
+     unsigned long i;
+     char tmp[MAXPATH];
+     struct stat sbuf;
+ 				/* if system doesn't have /dev/urandom */
+     if (stat ("/dev/urandom",&sbuf)) {
+       if ((fd = open (tmpnam (tmp),O_WRONLY|O_CREAT,0600)) < 0)
+ 	i = (unsigned long) tmp;
+       else {
+ 	unlink (tmp);		/* don't need the file */
+ 	fstat (fd,&sbuf);	/* get information about the file */
+ 	i = sbuf.st_ino;	/* remember its inode */
+ 	close (fd);		/* or its descriptor */
+       }
+ 				/* not great but it'll have to do */
+       sprintf (tmp + strlen (tmp),"%.80s%lx%lx%lx",
+ 	       tcp_serverhost (),i,
+ 	       (unsigned long) (time (0) ^ gethostid ()),
+ 	       (unsigned long) getpid ());
+       RAND_seed (tmp,strlen (tmp));
+     }
+ #endif
+ }
+ 
+ /* taken from openssl/apps/app_rand.c */
+ static int app_RAND_write_file(const char *file, BIO *bio_e)
+ {
+     char buffer[200];
+ 
+     if (egdsocket || !seeded)
+ 	/* If we did not manage to read the seed file,
+ 	 * we should not write a low-entropy seed file back --
+ 	 * it would suppress a crucial warning the next time
+ 	 * we want to use it. */
+ 	return 0;
+ 
+     if (file == NULL)
+ 	    file = RAND_file_name(buffer, sizeof buffer);
+     if (file == NULL || !RAND_write_file(file))
+     {
+ 	BIO_printf(bio_e,"unable to write 'random state'\n");
+ 	return 0;
+     }
+     return 1;
+ }
+ 
+ /* Installed as an atexit() handler to save the random data */
+ static void openssl_deinit(void)
+ {
+     app_RAND_write_file(NULL, bio_err);
+ }
+ 
+ /* Initialise openssl stuff if needed */
+ static void openssl_init(void)
+ {
+     static int inited = 0;
+ 
+     if (!inited) {
+ 
+         char	*p;
+         char	buf[MAXPATH];
+ 
+     	/* Find where that .pine-smime thing is */
+ 	/* Perhaps we should just use the user's home directory as a start? */
+         p = last_cmpnt(ps_global->pinerc);
+         buf[0] = '\0';
+         if(p != NULL) {
+             strncpy(buf, ps_global->pinerc, min(p - ps_global->pinerc, sizeof(buf)-1));
+             buf[min(p - ps_global->pinerc, sizeof(buf)-1)] = '\0';
+         }
+ 
+         strncat(buf, PINE_SMIME_DIRNAME, sizeof(buf)-1-strlen(buf));
+     	buf[sizeof(buf)-1] = 0;
+ 
+         if (can_access(buf, ACCESS_EXISTS)==0) {
+ 
+             g_pine_smime_dir = cpystr(buf);
+             s_cert_store = get_ca_store(g_pine_smime_dir);
+             s_personal_certs = get_personal_certs(g_pine_smime_dir);
+ 	    
+         } else g_pine_smime_dir = ""; /* prevent null dereference later */
+ 
+         SSLeay_add_all_algorithms();
+         ERR_load_crypto_strings();
+ 
+     	/* this won't make any sense (since the terminal's in a funny mode) */
+         if ((bio_err=BIO_new(BIO_s_file())) != NULL)
+             BIO_set_fp(bio_err,stderr,BIO_NOCLOSE|BIO_FP_TEXT);
+ 
+ 	app_RAND_load_file(NULL, bio_err, 1);
+ 
+     	openssl_extra_randomness();
+ 
+     	/* save the rand file when we're done */
+ 	atexit(openssl_deinit);
+ 
+         inited = 1;
+     }
+     
+     ERR_clear_error();
+ }
+ 
+ /*  Get a pointer to a string describing the most recent OpenSSL error.
+     It's statically allocated, so don't change or attempt to free it.
+  */
+ static const char *openssl_error_string(void)
+ {
+     char	*errs;
+     const char	*data = NULL;
+     long errn;
+ 
+     errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
+     errs = (char*)ERR_reason_error_string(ERR_GET_REASON(errn));
+ 
+     if (errs)
+     	return errs;
+     else if (data)
+     	return data;
+ 
+     return "unknown error";
+ }
+ 
+ /* Return true if the body looks like a PKCS7 object */
+ int is_pkcs7_body(BODY *body)
+ {
+     int result;
+ 
+     result = body->type==TYPEAPPLICATION &&
+              body->subtype &&
+              (strucmp(body->subtype,"pkcs7-mime")==0 ||
+               strucmp(body->subtype,"x-pkcs7-mime")==0 ||
+ 	      strucmp(body->subtype,"pkcs7-signature")==0 ||
+ 	      strucmp(body->subtype,"x-pkcs7-signature")==0);
+ 
+     return result;
+ }
+ 
+ /* debug utility to dump the contents of a BIO to a file */
+ static void dump_bio_to_file(BIO *in,char *filename)
+ {
+     char iobuf[4096];
+     int len;
+     BIO *out;
+     
+     out = BIO_new_file(filename,"w");
+     
+     if (out) {
+     	BIO_reset(in);
+     
+ 	while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
+ 	    BIO_write(out, iobuf, len);
+     	BIO_free(out);
+     }
+ }
+ 
+ /* prompt the user for their passphrase 
+     (possibly prompting with the email address in s_passphrase_emailaddr)
+  */
+ int get_passphrase(void)
+ {
+     int rc;
+     int	flags;
+     char prompt[50];
+     HelpType help = NO_HELP;
+ 
+     sprintf(prompt,
+             "Enter passphrase for <%s>: ",s_passphrase_emailaddr ? s_passphrase_emailaddr : "unknown");
+ 
+     do {
+         flags = OE_PASSWD | OE_DISALLOW_HELP;
+         rc =  optionally_enter(s_passphrase, -FOOTER_ROWS(ps_global), 0, sizeof(s_passphrase),
+                                prompt, NULL, help, &flags);
+     } while (rc!=0 && rc!=1 && rc>0);
+ 
+     if (rc==0)
+         s_entered_passphrase = 1;
+ 
+     return rc==0;
+ }
+ 
+ /*  Recursively stash a pointer to the decrypted data in our
+     manufactured body.
+  */
+ static void create_local_cache(char *base,BODY *b)
+ {
+     if (b->type==TYPEMULTIPART) {
+         PART	*p;
+ 
+ #if 0 
+         cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
+ #else
+     	/* don't really want to copy the real body contents. It shouldn't be
+ 	   used, and in the case of a message with attachments, we'll be 
+ 	   duplicating the files multiple times
+ 	 */
+     	cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
+ #endif
+ 
+         for (p=b->nested.part;p;p=p->next) {
+             create_local_cache(base,(BODY*) p);
+         }
+     } else {
+         cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
+     }
+ }
+ 
+ static long rfc822_output_func(void *stream,char *string)
+ {
+     STORE_S *so = (STORE_S*) stream;
+ 
+     return so_puts(so,string)!=0;
+ }
+ 
+ /*  Load a private key from the given file */
+ static EVP_PKEY *load_key(char *file, char *pass)
+ {
+     BIO *in;
+     EVP_PKEY *key;
+     if(!(in = BIO_new_file(file, "r"))) return NULL;
+     key = PEM_read_bio_PrivateKey(in, NULL,NULL,pass);
+     BIO_free(in);
+     return key;
+ }
+ 
+ /*  Attempt to load the private key for the given PERSONAL_CERT.
+     This sets the appropriate passphrase globals in order to
+     interact with the user correctly.
+  */
+ static int load_private_key(PERSONAL_CERT *pcert)
+ {
+     if (!pcert->key) {
+     
+     	/* Try empty password by default */
+     	char	*password = "";
+     
+     	if (g_need_passphrase) {
+ 	    /* We've already been in here and discovered we need a different password */
+ 	    
+ 	    if (s_entered_passphrase)
+     	    	password = s_passphrase; /* Use the passphrase if it's been entered */
+ 	    else return 0;
+ 	}
+ 
+         ERR_clear_error();
+ 
+         if(!(pcert->key = load_key(pcert->file, password))) {
+             long err = ERR_get_error();
+ 
+     	    /* Couldn't load key... */
+ 
+     	    if (s_entered_passphrase) {
+ 
+     	    	/* The user got the password wrong maybe? */
+ 
+         	if ((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
+                 	(ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
+                     q_status_message(SM_ORDER | SM_DING,1,1,"Wrong password");
+         	else q_status_message1(SM_ORDER,1,1,"Couldn't read key: %s",(char*)openssl_error_string());
+     	    	
+ 		/* This passphrase is no good; forget it */
+ 		s_entered_passphrase = 0;
+ 	    }
+ 	    
+ 	    /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
+ 	    g_need_passphrase = 1;
+ 	    
+ 	    fs_give((void**) &s_passphrase_emailaddr);
+ 	    s_passphrase_emailaddr = get_x509_subject_email(pcert->cert);
+             return 0;
+         } else {
+ 	    /* This key will be cached, so we won't be called again */
+ 	    s_entered_passphrase = 0;
+ 	    g_need_passphrase = 0;
+ 	}
+ 	
+ 	return 1;
+     }
+     
+     return 0;
+ }
+ 
+ static void setup_pkcs7_body_for_signature(BODY *b,char *description,char *type,char *filename)
+ {
+     b->type = TYPEAPPLICATION;
+     b->subtype = cpystr(type);
+     b->encoding = ENCBINARY;
+ 
+     b->description = cpystr(description);
+ 
+     b->disposition.type = cpystr("attachment");
+     b->disposition.parameter = mail_newbody_parameter();
+     b->disposition.parameter->attribute = cpystr("filename");
+     b->disposition.parameter->value = cpystr(filename);
+ 
+     b->parameter = mail_newbody_parameter();
+     b->parameter->attribute = cpystr("name");
+     b->parameter->value = cpystr(filename);
+ }
+ 
+ /*
+     Look for a personal certificate matching the 
+     given address
+  */
+ PERSONAL_CERT *match_personal_cert_to_email(ADDRESS *a)
+ {
+     PERSONAL_CERT   *pcert;
+     char	buf[MAXPATH];
+     char    	*email;
+ 
+     if (!a || !a->mailbox || !a->host)
+     	return NULL;
+     
+     snprintf(buf,sizeof(buf),"%s@%s",a->mailbox,a->host);
+     
+     for (pcert=s_personal_certs;pcert;pcert=pcert->next) {
+     
+     	if (!pcert->cert)
+ 	    continue;
+     
+         email = get_x509_subject_email(pcert->cert);
+ 
+     	if (email && strucmp(email,buf)==0) {
+     	    fs_give((void**) &email);
+ 	    break;
+ 	}
+ 
+     	fs_give((void**) &email);
+     }
+     
+     return pcert;
+ }
+ 
+ /*
+     Look for a personal certificate matching the from
+     (or reply_to? in the given envelope)
+  */
+ PERSONAL_CERT *match_personal_cert(ENVELOPE *env)
+ {
+     PERSONAL_CERT   *pcert;
+     
+     pcert = match_personal_cert_to_email(env->reply_to);
+     if (!pcert)
+     	pcert = match_personal_cert_to_email(env->from);
+         
+     return pcert;
+ }
+ 
+ /*
+     Flatten the given body into its MIME representation.
+     Return the result in a CharStar STORE_S.
+  */
+ static STORE_S *body_to_store(BODY *body)
+ {
+     STORE_S   *store;
+     store = so_get(CharStar, NULL, EDIT_ACCESS);
+     if (!store)
+     	return NULL;
+     
+     pine_encode_body(body); /* this attaches random boundary strings to multiparts */
+     pine_write_body_header (body, rfc822_output_func,store);
+     pine_rfc822_output_body(body, rfc822_output_func,store);
+ 
+     /*  now need to truncate by two characters since the above
+     	appends CRLF to the stream
+      */
+ 
+     /** Eek! No way of telling size of a STORE_S. We depend on knowing it's
+     	a CharStar */
+     so_truncate(store,((char*)store->eod-(char*)store->txt)-2);
+ 
+     so_seek(store,0,SEEK_SET);
+     
+     return store;
+ }
+ 
+ 
+ 
+ /*
+     Sign a message. Called from call_mailer in send.c.
+     
+     This takes the header for the outgoing message as well as a pointer
+     to the current body (which may be reallocated).
+  */
+ int sign_outgoing_message(METAENV *header,BODY **bodyP,int dont_detach)
+ {
+     STORE_S *store = NULL;
+     STORE_S *outs = NULL;
+     BODY    *body = *bodyP;
+     BODY    *newBody = NULL;
+     PART    *p1 = NULL;
+     PART    *p2 = NULL;
+     PERSONAL_CERT   *pcert;
+     BIO *in = NULL;
+     BIO *out = NULL;
+     PKCS7   *p7 = NULL;
+     int result = 0;
+     PARAMETER	*param;
+     
+     int flags = dont_detach ? 0 : PKCS7_DETACHED;
+ 
+     openssl_init();
+ 
+     store = body_to_store(body);
+ 
+     /* Look for a private key matching the sender address... */
+     
+     pcert = match_personal_cert(header->env);
+     
+     if (!pcert) {
+         q_status_message(SM_ORDER,1,1,"Couldn't find the certificate needed to sign.");
+ 	goto end;
+     }
+     
+     if (!load_private_key(pcert) && g_need_passphrase) {
+     	/* Couldn't load key with blank password, try again */
+     	get_passphrase();
+ 	load_private_key(pcert);
+     }
+     
+     if (!pcert->key)
+ 	goto end;
+     
+     in = BIO_new_so(store);
+ 
+ #if 0
+     dump_bio_to_file(in,"/tmp/signed-data");
+ #endif
+ 
+     BIO_reset(in);
+ 
+     p7 = PKCS7_sign(pcert->cert, pcert->key, NULL, in, flags);
+     if (!p7) {
+         q_status_message(SM_ORDER,1,1,"Error creating PKCS7 object.");
+ 	goto end;
+     }
+    
+     outs = so_get(CharStar,NULL,EDIT_ACCESS);
+     out = BIO_new_so(outs);
+ 
+     i2d_PKCS7_bio(out, p7);
+     BIO_flush(out);
+     
+     so_seek(outs,0,SEEK_SET);
+     
+     if ((flags&PKCS7_DETACHED)==0) {
+    
+     	/* the simple case: the signed data is in the pkcs7 object */
+     
+ 	newBody = mail_newbody();
+     	
+ 	setup_pkcs7_body_for_signature(newBody,"S/MIME Cryptographically Signed Message","x-pkcs7-mime","smime.p7m");
+ 
+     	newBody->contents.text.data = (char*) outs;
+ 	*bodyP = newBody;
+ 
+ 	result = 1;
+     } else {
+     
+ 	/*  OK.
+     	    We have to create a new body as follows:
+ 
+ 	    multipart/signed; blah blah blah
+ 		reference to existing body
+ 
+ 		pkcs7 object
+ 	 */
+ 
+ 	newBody = mail_newbody();
+ 
+ 	newBody->type = TYPEMULTIPART;
+ 	newBody->subtype = cpystr("signed");
+ 	newBody->encoding = ENC7BIT;
+ 
+ 	newBody->parameter = param = mail_newbody_parameter();
+ 	param->attribute = cpystr("protocol");
+ 	param->value = cpystr("application/x-pkcs7-signature");
+ 
+ 	newBody->parameter->next = param = mail_newbody_parameter();
+ 	param->attribute = cpystr("micalg");
+ 	param->value = cpystr("sha1");
+ 
+ 	p1 = mail_newbody_part();
+ 	p2 = mail_newbody_part();
+ 
+     	/*  this is nasty. We're just copying the body in here,
+ 	    but since our newBody is freed at the end of call_mailer,
+ 	    we mustn't let this body (the original one) be freed twice.
+ 	 */
+ 	p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
+ 
+ 	p1->next = p2;
+ 
+ 	setup_pkcs7_body_for_signature(&p2->body,"S/MIME Cryptographic Signature","x-pkcs7-signature","smime.p7s");
+     	p2->body.contents.text.data = (char*) outs;
+ 
+     	newBody->nested.part = p1;
+ 
+ 	*bodyP = newBody;
+ 	
+ 	result = 1;
+     }
+ 
+ end:
+ 
+     PKCS7_free(p7);
+     BIO_free(in);
+     BIO_free(out);
+     if (store)
+     	so_give(&store);
+ 
+     return result;
+ }
+ 
+ /*
+     Encrypt a message on the way out. Called from call_mailer in send.c
+     The body may be reallocated.
+  */
+ int encrypt_outgoing_message(METAENV *header,BODY **bodyP)
+ {
+     PKCS7 *p7 = NULL;
+     BIO	*in = NULL;
+     BIO *out = NULL;
+     EVP_CIPHER *cipher = NULL;
+     STACK_OF(X509) *encerts = NULL;
+     STORE_S *store = NULL;
+     STORE_S *outs = NULL;
+     PINEFIELD	*pf;
+     ADDRESS		*a;
+     BODY	*body = *bodyP;
+     BODY	*newBody = NULL;
+     int		result = 0;
+ 
+     openssl_init();
+ 
+     cipher = EVP_des_cbc();
+ 
+     encerts = sk_X509_new_null();
+ 
+     /* Look for a certificate for each of the recipients */
+     for(pf = header->local; pf && pf->name; pf = pf->next)
+         if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr) {
+ 
+             for (a=*pf->addr;a;a=a->next) {
+                 X509	*cert;
+                 char	buf[MAXPATH];
+ 
+                 snprintf(buf,sizeof(buf),"%s@%s",a->mailbox,a->host);
+ 
+                 cert = get_cert_for(g_pine_smime_dir,buf);
+                 if (cert)
+                     sk_X509_push(encerts,cert);
+                 else {
+                     q_status_message2(SM_ORDER,1,1,
+                                       "Unable to find certificate for <%s@%s>",a->mailbox,a->host);
+                     goto end;
+                 }
+             }
+         }
+ 
+ 
+     store = body_to_store(body);
+ 
+     in = BIO_new_so(store);
+ 
+     p7 = PKCS7_encrypt(encerts, in, cipher, 0);
+ 
+     outs = so_get(CharStar,NULL,EDIT_ACCESS);
+     out = BIO_new_so(outs);
+ 
+     i2d_PKCS7_bio(out, p7);
+     BIO_flush(out);
+     so_seek(outs,0,SEEK_SET);
+ 
+     newBody = mail_newbody();
+ 
+     newBody->type = TYPEAPPLICATION;
+     newBody->subtype = cpystr("x-pkcs7-mime");
+     newBody->encoding = ENCBINARY;
+ 
+     newBody->description = cpystr("S/MIME Encrypted Message");
+ 
+     newBody->contents.text.data = (char*) outs;
+ 
+     *bodyP = newBody;
+ 
+     result = 1;
+ 
+ end:
+ 
+     BIO_free(in);
+     BIO_free(out);
+     PKCS7_free(p7);
+     sk_X509_pop_free(encerts, X509_free);
+     if (store)
+         so_give(&store);
+ 
+     return result;
+ }
+ 
+ /*
+     Plonk the contents (mime headers and body) of the given
+     section of a message to a CharStar STORE_S object.
+  */
+ static STORE_S *get_raw_part(int msgno,const char *section)
+ {
+     long len;
+     STORE_S *store = NULL;
+     char	*text;
+ 
+     store = so_get(CharStar, NULL, EDIT_ACCESS);
+     if (store) {
+ 
+         /* First grab headers of the chap */
+         text = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) section, &len, 0);
+ 
+         if (text) {
+             so_nputs(store,text,len);
+ 
+             /** Now grab actual body */
+             text = mail_fetch_body (ps_global->mail_stream, msgno, (char*) section, &len, 0);
+             if (text) {
+                 so_nputs(store,text,len);
+ 
+                 so_seek(store,0,SEEK_SET);
+ 
+             } else so_give(&store);
+ 
+         } else so_give(&store);
+ 
+     }
+     return store;
+ }
+ 
+ /*
+     Get (and decode) the body of the given section of msg
+  */
+ static STORE_S *get_part_contents(int msgno,const char *section)
+ {
+     long len;
+     gf_io_t     pc;
+     STORE_S *store = NULL;
+     char	*err;
+ 
+     store = so_get(CharStar, NULL, EDIT_ACCESS);
+     if (store) {
+         gf_set_so_writec(&pc,store);
+ 
+         err = detach(ps_global->mail_stream, msgno, (char*) section,&len, pc, NULL);
+ 
+         gf_clear_so_writec(store);
+ 
+         so_seek(store,0,SEEK_SET);
+ 
+         if (err)
+             so_give(&store);
+     }
+     return store;
+ }
+ 
+ static PKCS7 *get_pkcs7_from_part(int msgno,const char *section)
+ {
+     STORE_S *store = NULL;
+     PKCS7 *p7 = NULL;
+     BIO	*in = NULL;
+ 
+     store = get_part_contents(msgno,section);
+ 
+     if (store) {
+         in = BIO_new_so(store);
+         if (in) {
+             p7=d2i_PKCS7_bio(in,NULL);
+         }
+     }
+ 
+     if (store)
+         so_give(&store);
+ 
+     BIO_free(in);
+ 
+     return p7;
+ }
+ 
+ /*
+     Try to verify a signature.
+     
+     p7  - the pkcs7 object to verify
+     in  - the plain data to verify (NULL if not detached)
+     out - BIO to which to write the opaque data
+  */
+ static int do_signature_verify(PKCS7 *p7,BIO *in,BIO *out)
+ {
+     STACK_OF(X509)	*otherCerts = NULL;
+     int	result;
+     const char    *data;
+     long    err;
+     
+ #if 0
+     dump_bio_to_file(in,"/tmp/verified-data");
+ #endif
+ 
+     BIO_reset(in);
+     
+ #if 0
+     /* testing verification stuff */
+     {
+     	X509	*c;
+ 	
+         c = get_cert_for(g_pine_smime_dir,"xx");
+ 	if (c) {
+     	    X509_add1_reject_object(c, OBJ_nid2obj(NID_email_protect));
+ 
+     	    X509_STORE_add_cert(s_cert_store,c);
+ 	    
+ 	    save_cert_for(g_pine_smime_dir,cpystr("yy"),c);
+ 	}
+     
+     
+     	ERR_clear_error();
+     
+     }
+ #endif
+     
+     result = PKCS7_verify(p7, otherCerts, s_cert_store,
+                       in, out, 0);
+     
+     if (result) {
+ 	q_status_message(SM_ORDER,1,1,"S/MIME signature verified ok");
+     } else {
+ 	err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
+ 
+ 	if (out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)) {
+ 
+ 	    /* Retry verification so we can get the plain text */
+ 	    /* Might be better to reimplement PKCS7_verify here? */
+ 	    
+ 	    PKCS7_verify(p7, otherCerts, s_cert_store,
+                       in, out, PKCS7_NOVERIFY);
+ 
+ 	}
+ 
+ 	q_status_message1(SM_ORDER | SM_DING,1,1,"Couldn't verify S/MIME signature: %s",(char*) openssl_error_string());
+ 
+     	return result;
+     }
+ 
+     /* now try to extract the certificates of any signers */
+     {
+         STACK_OF(X509)	*signers;
+         int	i;
+ 
+         signers = PKCS7_get0_signers(p7, NULL, 0);
+ 
+         if (signers)
+             for (i=0;i<sk_X509_num(signers);i++) {
+ 		char	*email;
+                 X509	*x = sk_X509_value(signers,i);
+ 		X509	*cert;
+ 		
+                 if (!x)
+                     continue;
+ 
+     	    	email = get_x509_subject_email(x);
+     	    	
+ 		if (email) {
+                     cert = get_cert_for(g_pine_smime_dir,email);
+ 		    if (cert) {
+ 		    	X509_free(cert);
+ 		    } else {
+     	    	    	save_cert_for(g_pine_smime_dir,email,x);
+     	    	    }
+ 		    fs_give((void**) &email);
+     	    	}
+             }
+ 
+         sk_X509_free(signers);
+     }
+ 
+     return result;
+ }
+ 
+ /*  Hook inside BODY structure for cleaning up S/MIME message bodies */
+ static void smime_body_cleanup(BODY *b)
+ {
+ #if 0
+     q_status_message(SM_ORDER,1,1,"smime_body_cleanup called");
+ #endif
+ 
+     if (b->sparep) {
+     	PKCS7_free((PKCS7*) b->sparep);
+         b->sparep = NULL;
+     }
+ }
+ 
+ /*
+     Given a multipart body of type multipart/signed, attempt to verify
+     it
+  */
+ static int do_detached_signature_verify(BODY *b,int msgno,char *section)
+ {
+     STORE_S *toVerify = NULL;
+     PKCS7   *p7 = NULL;
+     BIO	    *in = NULL;
+     PART    *p;
+     int	    result = 0;
+     char    seq[100];
+     char    *what_we_did;
+ 
+     openssl_init();
+ 
+     snprintf(seq,sizeof(seq),"%s1",section);
+     toVerify = get_raw_part(msgno,seq);
+ 
+     if (toVerify) {
+ 
+         in = BIO_new_so(toVerify);
+         if (!in)
+             goto end;
+ 
+         snprintf(seq,sizeof(seq),"%s2",section);
+         p7 = get_pkcs7_from_part(msgno,seq);
+ 
+         if (!p7)
+             goto end;
+ 
+     	result = do_signature_verify(p7,in,NULL);
+ 
+         if (b->subtype) fs_give((void**) &b->subtype);
+         b->subtype = cpystr("x-pkcs7-enclosure");
+         b->encoding = ENC8BIT;
+ 
+     	if (b->description) fs_give ((void**) &b->description);
+ 
+    	what_we_did = result ? 	"This message was cryptographically signed." :
+ 	    	    	    	"This message was cryptographically signed but the signature could not be verified.";
+ 
+ 	b->description = cpystr(what_we_did);
+ 
+     	b->sparep = p7;
+ 	p7 = NULL;
+ 	b->cleanup = smime_body_cleanup;
+ 
+     	p = b->nested.part;
+ 	
+ 	/* p is signed plaintext */
+ 	if (p && p->next)
+ 	    mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
+ 
+         result = 0;
+     }
+ end:
+     BIO_free(in);
+ 
+     PKCS7_free(p7);
+ 
+     if (toVerify)
+         so_give(&toVerify);
+ 
+     return result;
+ }
+ 
+ static PERSONAL_CERT *find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
+ {
+     PERSONAL_CERT	*x;
+ 
+     for (x=s_personal_certs;x;x=x->next) {
+         X509	*mine;
+ 
+         mine = x->cert;
+ 
+         if (!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
+                 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)) {
+             break;
+         }
+     }
+     
+     return x;
+ }
+ 
+ static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7)
+ {
+     int i;
+     STACK_OF(PKCS7_RECIP_INFO) *recips;
+     PERSONAL_CERT *x = NULL;
+ 
+     recips = p7->d.enveloped->recipientinfo;
+ 
+     for (i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++) {
+         PKCS7_RECIP_INFO	*ri;
+ 
+         ri=sk_PKCS7_RECIP_INFO_value(recips,i);
+ 
+         if ((x=find_certificate_matching_recip_info(ri))!=0) {
+             break;
+         }
+     }
+     
+     return x;
+ }
+ 
+ /*
+     Try to decode (decrypt or verify a signature) a PKCS7 body
+  */
+ static int do_decoding(BODY *b,int msgno,const char *section)
+ {
+     STORE_S	*outs = NULL;
+     int result = 0;
+ 
+     BIO *out = NULL;
+     PKCS7 *p7 = NULL;
+     X509 *recip = NULL;
+     EVP_PKEY *key = NULL;
+     PERSONAL_CERT 	*pcert = NULL;
+     
+     char    *what_we_did = "";
+ 
+     openssl_init();
+ 
+     /*
+     	Extract binary data from part to an in-memory store
+      */
+ 
+     if (b->sparep) {
+     
+     	p7 = (PKCS7*) b->sparep;
+ 	
+     } else {
+ 
+ 	p7 = get_pkcs7_from_part(msgno,section);
+ 	if (p7 == NULL) {
+             q_status_message1(SM_ORDER,1,1,"Couldn't load PKCS7 object: %s",(char*)openssl_error_string());
+             goto end;
+ 	}
+ 
+     	/*  Save the PKCS7 object for later dealings by the user interface.
+ 	    It will be cleaned up when the body is garbage collected
+ 	 */
+ 	b->sparep = p7;
+ 	b->cleanup = smime_body_cleanup;
+     }
+ 
+     if (PKCS7_type_is_signed(p7)) {
+     	int 	sigok;
+ 	
+ 	outs = so_get(CharStar, NULL, EDIT_ACCESS);
+ 	so_puts(outs,"MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
+     	out = BIO_new_so(outs);
+ 
+     	sigok = do_signature_verify(p7,NULL,out);
+     	
+ 	/* shouldn't really duplicate these messages */
+    	what_we_did = sigok ? 	"This message was cryptographically signed." :
+ 	    	    	    	"This message was cryptographically signed but the signature could not be verified.";
+ 	
+     } else if (!PKCS7_type_is_enveloped(p7)) {
+         q_status_message(SM_ORDER,1,1,"PKCS7 object not recognised.");
+         goto end;
+     } else { /* It *is* enveloped */
+ 
+ 	what_we_did = "This message was encrypted.";
+ 
+ 	/*
+     	    Now need to find a cert that can decrypt this boy
+ 	 */
+ 	pcert = find_certificate_matching_pkcs7(p7);
+ 
+ 	if (!pcert) {
+             q_status_message(SM_ORDER,1,1,"Couldn't find the certificate needed to decrypt.");
+             goto end;
+ 	}
+ 
+ 	recip = pcert->cert;
+ 
+ 	load_private_key(pcert);
+ 
+ 	key = pcert->key;
+ 	if (!key)	
+     	    goto end;
+ 
+ 	outs = so_get(CharStar, NULL, EDIT_ACCESS);
+ 	so_puts(outs,"MIME-Version: 1.0\r\n");
+ 
+ 	out = BIO_new_so(outs);
+ 
+ 	if(!PKCS7_decrypt(p7, key, recip, out, 0)) {
+             q_status_message1(SM_ORDER,1,1,"Error decrypting PKCS7: %s",(char*) openssl_error_string());
+             goto end;
+ 	}
+     
+     }
+ 
+     /*	We've now produced a flattened MIME object in store outs.
+     	It needs to be turned back into a BODY
+      */
+ 
+     {
+         BODY	*body;
+         ENVELOPE	*env;
+         char	*h;
+         char	*bstart;
+         STRING	s;
+ 
+         h = so_text(outs);
+ 
+         /* look for start of body */
+         bstart = strstr(h,"\r\n\r\n");
+ 
+         if (!bstart) {
+             q_status_message(SM_ORDER,1,1,"Encrypted data couldn't be parsed.");
+      	} else {
+             bstart += 4; /* skip over CRLF*2 */
+ 
+             INIT(&s,mail_string,bstart,strlen(bstart));
+             rfc822_parse_msg_full(&env,&body,h,bstart-h-2,&s,BADHOST,0,0);
+             mail_free_envelope(&env); /* Don't care about this */
+ 
+             /*
+             	Now convert original body (application/pkcs7-mime)
+             	to a multipart body with one sub-part (the decrypted body)
+ 		Note that the sub-part may also be multipart!
+              */
+ 
+             b->type = TYPEMULTIPART;
+             if (b->subtype) fs_give((void**) &b->subtype);
+ 
+     	    /*  This subtype is used in mailview.c to annotate the display of
+ 	    	encrypted or signed messages. We know for sure then that it's a PKCS7
+ 		part because the sparep field is set to the PKCS7 object (see above)
+ 	     */
+             b->subtype = cpystr("x-pkcs7-enclosure");
+             b->encoding = ENC8BIT;
+ 
+     	    if (b->description) fs_give ((void**) &b->description);
+ 	    b->description = cpystr(what_we_did);
+ 
+             if (b->disposition.type) fs_give ((void **) &b->disposition.type);
+ 
+             if (b->contents.text.data) fs_give ((void **) &b->contents.text.data);
+ 
+     	    if (b->parameter) mail_free_body_parameter(&b->parameter);
+ 
+             /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
+             b->nested.part = fs_get(sizeof (PART));
+             b->nested.part->body = *body;
+             b->nested.part->next = NULL;
+ 
+             fs_give((void**) &body);
+ 
+             /* 	IMPORTANT BIT: set the body->contents.text.data elements to contain the decrypted
+             	data. Otherwise, it'll try to load it from the original data. Eek.
+              */
+             create_local_cache(bstart,&b->nested.part->body);
+ 
+             result = 1;
+         }
+     }
+ 
+ end:
+ 
+     BIO_free(out);
+ 
+     if (outs)
+         so_give(&outs);
+ 
+     return result;
+ }
+ 
+ /*
+     Recursively handle PKCS7 bodies in our message.
+ 
+     Returns non-zero if some fiddling was done.
+  */
+ static int do_fiddle_smime_message(BODY *b,int msgno,char *section)
+ {
+     int result = 0;
+ 
+     if (is_pkcs7_body(b)) {
+     
+         if (do_decoding(b,msgno,*section ? section : "1")) {
+             /*
+             	b should now be a multipart message: fiddle it too in case it's been multiply-encrypted!
+              */
+ 
+             /* fallthru */
+             result = 1;
+         }
+     }
+ 
+     if (b->type==TYPEMULTIPART) {
+ 
+         PART	*p;
+         int		partNum;
+         char	newSec[100];
+ 
+         if (b->subtype && strucmp(b->subtype,"signed")==0) {
+ 
+             /* Ahah. We have a multipart signed entity. */
+ 
+             /*	part 1 (signed thing)
+             	part 2 (the pkcs7 object)
+              */
+ 
+             do_detached_signature_verify(b,msgno,section);
+ 
+         } else {
+ 
+             for (p=b->nested.part,partNum=1;p;p=p->next,partNum++) {
+ 
+                 /* Append part number to the section string */
+ 
+                 snprintf(newSec,sizeof(newSec),"%s%s%d",section,*section ? "." : "",partNum);
+ 
+                 result |= do_fiddle_smime_message(&p->body,msgno,newSec);
+             }
+ 
+         }
+ 
+     }
+ 
+     return result;
+ }
+ 
+ /*
+     Fiddle a message in-place by decrypting/verifying S/MIME entities.
+     Returns non-zero if something was changed.
+  */
+ 
+ int fiddle_smime_message(BODY *b,int msgno,int is_new_message)
+ {
+     if (F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
+         forget_private_keys();
+     return do_fiddle_smime_message(b,msgno,"");
+ }
+ 
+ 
+ /********************************************************************************/
+ 
+ static struct key smime_info_keys[] = 
+        {HELP_MENU,
+     	OTHER_MENU,
+ 	{"<","Back",{MC_VIEW_TEXT,2,{'<',','}},KS_EXITMODE},
+     	NULL_MENU,
+     	NULL_MENU,
+     	NULL_MENU,
+ 	PREVPAGE_MENU,
+ 	NEXTPAGE_MENU,
+     	NULL_MENU,
+     	NULL_MENU,
+     	NULL_MENU,
+     	NULL_MENU,
+ 
+ 	HELP_MENU,
+ 	OTHER_MENU,
+ 	MAIN_MENU,
+ 	QUIT_MENU,
+     	NULL_MENU,
+     	NULL_MENU,
+     	NULL_MENU,
+     	NULL_MENU,
+     	NULL_MENU,
+ 	INDEX_MENU,
+     	NULL_MENU,
+     	NULL_MENU,
+ };
+ INST_KEY_MENU(smime_info_keymenu, smime_info_keys);
+ 
+ #define SMIME_PARENT_KEY 2
+ 
+ static void get_fingerprint(X509 *cert,const EVP_MD *type,char *buf,int maxLen)
+ {
+     unsigned char    md[128];
+     char    *b;
+     int     len,i;
+ 
+     len = sizeof(md);
+ 
+     X509_digest(cert,type,md,&len);
+ 
+     b = buf;
+     *b = 0;
+     for (i=0; i<len; i++)
+     {
+     	if (b-buf+3>=maxLen)
+ 	    break;
+ 	
+ 	if (i != 0)
+ 	    *b++ = ':';
+ 	sprintf(b,"%02x",md[i]);
+     	b+=2;
+     }
+ }
+ 
+ static void output_X509_NAME(X509_NAME *name,gf_io_t pc)
+ {
+     int i,c;
+     int nid;
+     char    buf[256];
+     
+     c = X509_NAME_entry_count(name);
+     
+     for (i=c-1;i>=0;i--) {
+     	X509_NAME_ENTRY *e;
+ 	
+     	e = X509_NAME_get_entry(name,i);
+ 	if (!e) continue;
+ 	
+     	X509_NAME_get_text_by_OBJ(name, e->object,buf,sizeof(buf));
+ 	
+     	gf_puts(buf,pc);
+ 	gf_puts(NEWLINE,pc);    
+     }
+ 
+ }
+ 
+ /*
+     Output a string in a distinctive style
+  */
+ static void gf_puts_uline(const char *txt,gf_io_t pc)
+ {
+ #if 0
+     pc(TAG_EMBED); pc(TAG_ULINEON);
+     gf_puts(txt,pc);
+     pc(TAG_EMBED); pc(TAG_ULINEOFF);
+ #else
+     pc(TAG_EMBED); pc(TAG_BOLDON);
+     gf_puts(txt,pc);
+     pc(TAG_EMBED); pc(TAG_BOLDOFF);
+ #endif
+ }
+ 
+ /*
+     Get a line from the given store (including \n)
+  */
+ static int so_gets(STORE_S *store,char *buf,int len)
+ {
+     unsigned char   c;
+     char   *bend = buf + len - 1;
+     char    *b = buf;
+     
+     do {
+         if (!store->readc(&c,store)) {
+ 	    *b = 0;
+ 	    return b!=buf;
+ 	}
+ 	*b++ = c;
+     } while (c!='\n' && b<bend);
+ 
+     *b = 0;
+ 
+     return 1;
+ }
+ 
+ /*
+     Wrap the text in the given store to the given width.
+     A new store is created for the result.
+  */
+ static STORE_S *wrap_store(STORE_S *in,int width)
+ {
+     STORE_S *result;
+     void  *ws;
+     gf_io_t ipc,opc;
+     
+     if (width<10)
+     	width = 10;
+     
+     result = so_get(CharStar,NULL,EDIT_ACCESS);
+     ws = gf_wrap_filter_opt(width,width,0,0);
+ 
+     gf_filter_init();
+     gf_link_filter(gf_wrap,ws);
+ 
+     gf_set_so_writec(&opc,result);
+     gf_set_so_readc(&ipc,in);
+ 
+     gf_pipe(ipc,opc);
+     
+     gf_clear_so_readc(in);
+     gf_clear_so_writec(result);
+     
+     return result;
+ }
+ 
+ /*
+     Output the contents of the given stores (left and right)
+     to the given gf_io_t.
+     The width of the terminal is inspected and two columns
+     are created to fit the stores into. They are then wrapped
+     and merged.
+  */
+ static void side_by_side(STORE_S *left,STORE_S *right,gf_io_t pc)
+ {
+     STORE_S *left_wrapped;
+     STORE_S *right_wrapped;
+ 
+     char    buf_l[256];
+     char    buf_r[256];
+     char    *b;
+     int i;
+     int w = ps_global->ttyo->screen_cols/2 - 1;
+     
+     so_seek(left,0,0);
+     so_seek(right,0,0);
+     
+     left_wrapped = wrap_store(left,w);
+     right_wrapped = wrap_store(right,w);
+     
+     so_seek(left_wrapped,0,0);
+     so_seek(right_wrapped,0,0);
+ 
+     for (;;) {
+     
+     	i = so_gets(left_wrapped,buf_l,sizeof(buf_l));
+     	i += so_gets(right_wrapped,buf_r,sizeof(buf_r));
+     
+     	if (i==0)
+ 	    break;
+     
+ 	for (i=0, b=buf_l;i<w && *b && *b!='\r' && *b!='\n';i++,b++) {
+ 	    pc(*b);
+ 	    /* reduce accumulated width if an embed tag is discovered */
+ 	    if (*b==TAG_EMBED)
+ 	    	i-=2;
+ 	}
+ 
+ 	if (buf_r[0]) {
+ 
+     	    while (i<w) {
+ 	    	pc(' ');
+ 		i++;
+ 	    }
+ 
+ 	    for (i=0, b=buf_r;i<w && *b && *b!='\r' && *b!='\n';i++,b++) {
+ 		pc(*b);
+ 	    }
+ 	}
+ 
+ 	gf_puts(NEWLINE,pc);
+     }
+     
+     so_give(&left_wrapped);
+     so_give(&right_wrapped);
+ }
+ 
+ static void print_separator_line(int percent,int ch,gf_io_t pc)
+ {
+     int i;
+     int start,len;
+     
+     len = ps_global->ttyo->screen_cols * percent / 100;
+     start = (ps_global->ttyo->screen_cols - len)/2;
+     
+     for (i=0;i<start;i++)
+     	pc(' ');
+     for (i=start;i<start+len;i++)
+     	pc(ch);
+     gf_puts(NEWLINE,pc);
+ }
+ 
+ static void output_cert_info(X509 *cert,gf_io_t pc)
+ {
+     char    buf[256];
+     STORE_S *left,*right;
+     gf_io_t spc;
+     int i;
+         
+     left = so_get(CharStar,NULL,EDIT_ACCESS);
+     right = so_get(CharStar,NULL,EDIT_ACCESS);
+ 
+     gf_set_so_writec(&spc,left);
+ 
+     if (!cert->cert_info) {
+     	gf_puts("Couldn't find certificate info.",spc);
+ 	gf_puts(NEWLINE,spc);
+     } else {
+ 
+ 	gf_puts_uline("Subject (whose certificate it is)",spc);
+ 	gf_puts(NEWLINE, spc);
+ 
+ 	output_X509_NAME(cert->cert_info->subject,spc);
+ 	gf_puts(NEWLINE,spc);
+ 
+ 	gf_puts_uline("Serial Number",spc);
+ 	gf_puts(NEWLINE,spc);
+ 
+ 	sprintf(buf,"%d",ASN1_INTEGER_get(cert->cert_info->serialNumber));
+ 	gf_puts(buf,spc);
+ 	gf_puts(NEWLINE,spc);
+ 	gf_puts(NEWLINE,spc);
+ 
+ 	gf_puts_uline("Validity",spc);
+ 	gf_puts(NEWLINE,spc);
+     	{
+     	    BIO *mb = BIO_new_so(left);
+ 	    
+ 	    gf_puts("Not Before: ",spc);
+ 	    ASN1_UTCTIME_print(mb,cert->cert_info->validity->notBefore);
+ 	    BIO_flush(mb);
+ 	    gf_puts(NEWLINE,spc);
+ 
+ 	    gf_puts("Not After:  ",spc);
+ 	    ASN1_UTCTIME_print(mb,cert->cert_info->validity->notAfter);
+ 	    BIO_flush(mb);
+     	    
+ 	    gf_puts(NEWLINE,spc);
+ 	    gf_puts(NEWLINE,spc);
+ 	    	    
+ 	    BIO_free(mb);
+ 	}
+ 
+     }
+ 
+     gf_clear_so_writec(left);
+ 
+     gf_set_so_writec(&spc,right);
+ 
+     if (!cert->cert_info) {
+     	gf_puts("Couldn't find certificate info.",spc);
+ 	gf_puts(NEWLINE,spc);
+     } else {
+ 	gf_puts_uline("Issuer",spc);
+ 	gf_puts(NEWLINE, spc);
+ 
+ 	output_X509_NAME(cert->cert_info->issuer,spc);
+ 	gf_puts(NEWLINE,spc);
+     }
+     
+     gf_clear_so_writec(right);
+     
+     side_by_side(left,right,pc);
+ 
+     gf_puts_uline("SHA1 Fingerprint",pc);
+     gf_puts(NEWLINE,pc);
+     get_fingerprint(cert,EVP_sha1(),buf,sizeof(buf));
+     gf_puts(buf,pc);
+     gf_puts(NEWLINE,pc);
+ 
+     gf_puts_uline("MD5 Fingerprint",pc);
+     gf_puts(NEWLINE,pc);
+     get_fingerprint(cert,EVP_md5(),buf,sizeof(buf));
+     gf_puts(buf,pc);
+     gf_puts(NEWLINE,pc);
+     
+     so_give(&left);
+     so_give(&right);
+ }
+ 
+ void format_smime_info(int pass,BODY *body,int msgno,gf_io_t pc)
+ {
+     PKCS7   *p7;
+     int i;
+     
+     if (body->type==TYPEMULTIPART) {
+     	PART	*p;    
+ 
+         for (p=body->nested.part;p;p=p->next) {
+             format_smime_info(pass,&p->body,msgno,pc);
+         }
+     }
+     
+     p7 = body->sparep;
+     if (p7) {
+ 
+     	if (PKCS7_type_is_signed(p7)) {
+             STACK_OF(X509)	*signers;
+ 
+     	    switch (pass) {
+ 	    	case 1:
+     		    gf_puts("This message was cryptographically signed." NEWLINE,pc);
+     	    	    break;
+ 		case 2:
+ 		
+         	    signers = PKCS7_get0_signers(p7, NULL, 0);
+ 
+         	    if (signers) {
+ 
+     	    	    	sprintf(tmp_20k_buf,"Certificate%s used for signing",plural(sk_X509_num(signers)));
+     	    		gf_puts_uline(tmp_20k_buf,pc);
+ 			gf_puts(NEWLINE,pc);
+ 			print_separator_line(100,'-',pc);
+ 
+         		for (i=0;i<sk_X509_num(signers);i++) {
+                 	    X509	*x = sk_X509_value(signers,i);
+ 
+                 	    if (x) {
+ 				output_cert_info(x,pc);
+ 				gf_puts(NEWLINE,pc);
+ 			    }
+ 
+         		}
+ 		    }
+ 
+         	    sk_X509_free(signers);
+ 	    	    break;
+ 	    }
+     	
+ 	} else if (PKCS7_type_is_enveloped(p7)) {
+ 	
+     	    switch (pass) {
+ 	    	case 1:
+     		    gf_puts("This message was encrypted." NEWLINE,pc);
+     	    	    break;
+ 	    	case 2:
+ 		
+ 		    if (p7->d.enveloped && p7->d.enveloped->enc_data) {
+ 			X509_ALGOR *alg = p7->d.enveloped->enc_data->algorithm;
+ 			STACK_OF(PKCS7_RECIP_INFO) *ris = p7->d.enveloped->recipientinfo;
+     	    		int found = 0;
+ 
+     	    		gf_puts("The algorithm used to encrypt was ",pc);
+ 
+ 
+ 			if (alg) {
+ 			    char    *n = OBJ_nid2sn( OBJ_obj2nid(alg->algorithm ));
+ 
+ 			    gf_puts(n ? n : "<unknown>",pc);
+ 
+ 			} else gf_puts("<unknown>",pc);
+ 
+ 			gf_puts("." NEWLINE NEWLINE,pc);
+ 
+     	    	    	sprintf(tmp_20k_buf,"Certificate%s for decrypting",plural(sk_PKCS7_RECIP_INFO_num(ris)));
+     	    		gf_puts_uline(tmp_20k_buf,pc);
+ 			gf_puts(NEWLINE,pc);
+ 			print_separator_line(100,'-',pc);
+ 
+ 			for (i=0;i<sk_PKCS7_RECIP_INFO_num(ris);i++) {
+ 			    PKCS7_RECIP_INFO	*ri;
+ 			    PERSONAL_CERT    *pcert;
+ 
+ 			    ri = sk_PKCS7_RECIP_INFO_value(ris,i);
+ 			    if (!ri) continue;
+ 
+     	    		    pcert = find_certificate_matching_recip_info(ri);
+ 
+     	    		    if (pcert) {
+ 
+     	    	    	    	if (found) {
+ 				    print_separator_line(25,'*',pc);
+ 			    	    gf_puts(NEWLINE,pc);
+ 				}
+ 				found = 1;
+ 
+ 				output_cert_info(pcert->cert,pc);
+ 			    	gf_puts(NEWLINE,pc);
+ 
+ 			    }
+ 			}
+ 
+     	    		if (!found) {
+ 			    gf_puts("No certificate capable of decrypting could not be found.",pc);
+ 			}
+ 		    }
+ 		    break;
+ 	    }
+ 	
+ 	}
+     }
+ }
+ 
+ void view_writec();
+ 
+ void smime_info_screen(struct pine *ps)
+ {
+     int msgno;
+     OtherMenu     what;
+     int cmd;
+     char    backtag[64];
+     BODY    *body;
+     ENVELOPE	*env;
+     HANDLE_S	   *handles = NULL;
+     SCROLL_S	    scrollargs;
+     STORE_S 	*store = NULL;
+     int     offset = 0;
+     
+     ps->prev_screen = smime_info_screen;
+     ps->next_screen = SCREEN_FUN_NULL;
+ 
+     if(mn_total_cur(ps->msgmap) > 1L){
+ 	q_status_message(SM_ORDER | SM_DING, 0, 3,
+ 			 "Can only view one message's information at a time.");
+ 	return;
+     }
+     /* else check for existence of smime bits */
+ 
+     msgno	       = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
+     
+     env = mail_fetch_structure (ps->mail_stream,msgno,
+ 				&body,0);
+     if (!env || !body) {
+ 	q_status_message(SM_ORDER, 0, 3,
+ 			 "Can't fetch body of message.");
+ 	return;
+     }
+     
+     what	       = FirstMenu;
+ 
+     store = so_get(CharStar, NULL, EDIT_ACCESS);
+ 
+     while(ps->next_screen == SCREEN_FUN_NULL){
+ 
+     	ClearLine(1);
+ 
+ 	so_truncate(store,0);
+ 	
+ 	view_writec_init(store, &handles, HEADER_ROWS(ps),
+ 			 HEADER_ROWS(ps) + 
+ 			 ps->ttyo->screen_rows - (HEADER_ROWS(ps)
+ 						  + HEADER_ROWS(ps)));
+ 
+     	gf_puts_uline("Overview",view_writec);
+     	gf_puts(NEWLINE,view_writec);
+ 
+ 	format_smime_info(1,body,msgno,view_writec);
+ 	gf_puts(NEWLINE,view_writec);
+ 	format_smime_info(2,body,msgno,view_writec);
+ 
+ 	view_writec_destroy();
+ 
+ 
+ 	ps->next_screen = SCREEN_FUN_NULL;
+ 
+ 	memset(&scrollargs, 0, sizeof(SCROLL_S));
+ 	scrollargs.text.text	= so_text(store);
+ 	scrollargs.text.src	= CharStar;
+ 	scrollargs.text.desc	= "S/MIME information";
+ 	scrollargs.body_valid = 1;
+ 
+ 	if(offset){		/* resize?  preserve paging! */
+ 	    scrollargs.start.on		= Offset;
+ 	    scrollargs.start.loc.offset = offset;
+ 	    offset = 0L;
+ 	}
+ 
+ 	scrollargs.bar.title	= "S/MIME INFORMATION";
+ /*	scrollargs.end_scroll	= view_end_scroll; */
+ 	scrollargs.resize_exit	= 1;
+ 	scrollargs.help.text	= NULL;
+ 	scrollargs.help.title	= "HELP FOR S/MIME INFORMATION VIEW";
+ 	scrollargs.keys.menu	= &smime_info_keymenu;
+ 	scrollargs.keys.what    = what;
+ 	setbitmap(scrollargs.keys.bitmap);
+ 
+ 	if(scrolltool(&scrollargs) == MC_RESIZE)
+ 	  offset = scrollargs.start.loc.offset;
+     }
+ 
+     so_give(&store);
+ 
+ }
+ 
+ 
+ #endif /* SMIME */
diff -Ncr pine4.53/pine/smime.h pine4.53-smime/pine/smime.h
*** pine4.53/pine/smime.h	Wed Dec 31 16:00:00 1969
--- pine4.53-smime/pine/smime.h	Wed Jan 15 12:48:15 2003
***************
*** 0 ****
--- 1,17 ----
+ #ifdef SMIME
+ 
+ int is_pkcs7_body(BODY *b);
+ 
+ int fiddle_smime_message(BODY *b,int msgno,int is_new_message);
+ 
+ int encrypt_outgoing_message(METAENV *header,BODY **bodyP);
+ int sign_outgoing_message(METAENV *header,BODY **bodyP,int dont_detach);
+ int get_passphrase(void);
+ void smime_info_screen(struct pine *ps);
+ 
+ extern int g_need_passphrase;
+ 
+ extern int g_do_encrypt;
+ extern int g_do_sign;
+ 
+ #endif /* SMIME */
diff -Ncr pine4.53/pine/smkeys.c pine4.53-smime/pine/smkeys.c
*** pine4.53/pine/smkeys.c	Wed Dec 31 16:00:00 1969
--- pine4.53-smime/pine/smkeys.c	Wed Jan 15 12:48:15 2003
***************
*** 0 ****
--- 1,343 ----
+ #ifdef SMIME
+ 
+ #include "headers.h"
+ 
+ #include <openssl/err.h>
+ #include <openssl/objects.h>
+ #include <openssl/evp.h>
+ #include <openssl/x509.h>
+ #include <openssl/pkcs7.h>
+ #include <openssl/pem.h>
+ 
+ #include "smkeys.h"
+ 
+ /*---------------------------------------------------
+      Remove leading whitespace, trailing whitespace and convert 
+      to lowercase. Also remove slash characters
+ 
+    Args: s, -- The string to clean
+ 
+  Result: the cleaned string
+   ----*/
+ static char *
+ emailstrclean(string)
+      char *string;
+ {
+     char *s = string, *sc = NULL, *p = NULL;
+ 
+     for(; *s; s++){				/* single pass */
+ 	if(!isspace((unsigned char)*s)){
+ 	    p = NULL;				/* not start of blanks   */
+ 	    if(!sc)				/* first non-blank? */
+ 	      sc = string;			/* start copying */
+ 	}
+ 	else if(!p)				/* it's OK if sc == NULL */
+ 	  p = sc;				/* start of blanks? */
+ 
+ 	if(sc && *s!='/' && *s!='\\')		/* if copying, copy */
+ 	  *sc++ = isupper((unsigned char)(*s))
+ 			  ? (unsigned char)tolower((unsigned char)(*s))
+ 			  : (unsigned char)(*s);
+     }
+ 
+     if(p)					/* if ending blanks  */
+       *p = '\0';				/* tie off beginning */
+     else if(!sc)				/* never saw a non-blank */
+       *string = '\0';				/* so tie whole thing off */
+ 
+     return(string);
+ }
+ 
+ /*
+     Add a lookup for each "*.crt" file in the given directory.
+  */
+ static void add_certs_in_dir(X509_LOOKUP *lookup,const char *path)
+ {
+     char	buf[MAXPATH];
+     struct direct *d;
+     DIR	*dirp;
+     PERSONAL_CERT *result;
+ 
+     result = NULL;
+ 
+     dirp = opendir(path);
+     if (dirp) {
+ 
+         while (d=readdir(dirp)) {
+             BIO *in;
+             X509 *cert;
+ 
+             if (srchrstr(d->d_name,".crt")) {
+ 
+     	    	build_path(buf,(char*) path,d->d_name,sizeof(buf));
+ 
+     	    	X509_LOOKUP_load_file(lookup,buf,X509_FILETYPE_PEM);
+             }
+ 
+         }
+ 
+         closedir(dirp);
+     }
+ }
+ 
+ /*  Get an X509_STORE. This consists of the system
+     certs directory and any certificates in the user's
+     ~/.pine-smime/ca directory.
+  */
+ X509_STORE *get_ca_store(const char *path)
+ {
+     X509_LOOKUP	*lookup;
+     char	buf[MAXPATH];
+ 
+     X509_STORE *store;
+ 
+     store=X509_STORE_new();
+ 
+     lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
+     
+     build_path(buf,(char*) path,"ca",sizeof(buf));
+ 
+     add_certs_in_dir(lookup,buf);
+     
+ /*    X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT); */
+ 
+     lookup = X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
+ 
+     X509_LOOKUP_add_dir(lookup,SSL_CERT_DIRECTORY,X509_FILETYPE_PEM);
+ 
+     /*			X509_STORE_set_default_paths(cert_store);
+     	X509_STORE_load_locations(cert_store,NULL,"../../certs");
+     	X509_STORE_set_verify_cb_func(cert_store,verify_callback);
+     */
+ 
+     return store;
+ }
+ 
+ static EVP_PKEY *load_key(char *file, char *pass)
+ {
+     BIO *in;
+     EVP_PKEY *key;
+     if(!(in = BIO_new_file(file, "r"))) return NULL;
+     key = PEM_read_bio_PrivateKey(in, NULL,NULL,pass);
+     BIO_free(in);
+     return key;
+ }
+ 
+ char *get_x509_name_entry(const char *key,X509_NAME *name)
+ {
+     int i,c,n;
+     char    buf[256];
+     char	*id;
+ 	
+  	if (!name)
+ 		return NULL;
+  
+     c = X509_NAME_entry_count(name);
+     
+     for (i=0;i<c;i++) {
+     	X509_NAME_ENTRY *e;
+ 	
+     	e = X509_NAME_get_entry(name,i);
+     	if (!e) continue;
+ 
+     	buf[0] = 0;
+ 		id = buf;
+ 		
+         n = OBJ_obj2nid(e->object);
+         if ((n == NID_undef) || ((id=(char*) OBJ_nid2sn(n)) == NULL)) {
+             i2t_ASN1_OBJECT(buf,sizeof(buf),e->object);
+             id = buf;
+         }
+ 
+ 		if ((strucmp(id,"email")==0) || (strucmp(id,"emailAddress")==0)) {
+ 			X509_NAME_get_text_by_OBJ(name, e->object,(char*) buf,sizeof(buf)-1);
+ 			return cpystr(buf);
+ 		}
+     }
+     return NULL;
+ }
+ 
+ char *get_x509_subject_email(X509 *x)
+ {
+ 	char* result;
+ 	result = get_x509_name_entry("email",X509_get_subject_name(x));
+ 	if ( !result ) {
+ 		result = get_x509_name_entry("emailAddress",X509_get_subject_name(x));
+ 	}
+ 
+ 	return result;
+ }
+ 
+ /*  Save the certificate for the given email address in
+     ~/.pine-smime/public.
+ 
+     Should consider the security hazards in making a file with
+     the email address that has come from the certificate.
+ 
+     The argument email is destroyed.
+  */
+ 
+ void save_cert_for(const char *path,char *email,X509 *cert)
+ {
+     char    sf[MAXPATH];
+     char    sf2[MAXPATH];
+     BIO 	*tmp;
+     
+     build_path(sf,(char*) path,"public",sizeof(sf));
+ 
+     build_path(sf2,sf,emailstrclean(email),sizeof(sf2));
+     strncat(sf2,".crt",sizeof(sf2)-1-strlen(sf2));
+     sf2[sizeof(sf2)-1] = 0;
+ 
+     tmp = BIO_new_file(sf2, "w");
+     if (tmp) {
+         X509_print(tmp,cert);
+         PEM_write_bio_X509_AUX(tmp, cert);
+         BIO_free(tmp);
+        	q_status_message1(SM_ORDER,1,1,"Saved certificate for <%s>",(char*)email);
+     } else {
+        	q_status_message1(SM_ORDER,1,1,"Couldn't save certificate for <%s>",(char*)email);
+     }
+ }
+ 
+ /*
+     Try to retrieve the certificate for the given email address.
+  */
+ X509 *get_cert_for(const char *path,const char *email)
+ {
+     char	buf[MAXPATH];
+     char	buf2[MAXPATH];
+     char    	buf3[MAXPATH];
+     char    	*p;
+     X509 *cert = NULL;
+     BIO	*in;
+ 
+     strncpy(buf3,email,sizeof(buf3)-1);
+     buf3[sizeof(buf3)-1] = 0;
+     
+     /* clean it up (lowercase, space removal) */
+     emailstrclean(buf3);
+ 
+     build_path(buf,(char*)path,"public",sizeof(buf));
+     build_path(buf2,buf,buf3,sizeof(buf2));
+     strncat(buf2,".crt",sizeof(buf2)-1-strlen(buf2));
+     buf2[sizeof(buf2)-1] = 0;
+ 	
+     if((in = BIO_new_file(buf2, "r"))!=0) {
+ 
+         cert = PEM_read_bio_X509(in, NULL, NULL,NULL);
+ 
+         if (cert) {
+             /* could check email addr in cert matches */
+         }
+     }
+     BIO_free(in);
+ 
+     return cert;
+ }
+ 
+ EVP_PKEY *get_key_for(const char *path,const char *email,const char *pass)
+ {
+     char	buf[MAXPATH];
+     char	buf2[MAXPATH];
+     char    	buf3[MAXPATH];
+     char    	*p;
+     EVP_PKEY	*key = NULL;
+     BIO	*in;
+ 
+     strncpy(buf3,email,sizeof(buf3)-1);
+     buf3[sizeof(buf3)-1] = 0;
+     
+     /* clean it up (lowercase, space removal) */
+     emailstrclean(buf3);
+ 
+     build_path(buf,(char*)path,"private",sizeof(buf));
+     build_path(buf2,buf,buf3,sizeof(buf2));
+     strncat(buf2,".key",sizeof(buf2)-1-strlen(buf2));
+     buf2[sizeof(buf2)-1] = 0;
+ 
+ 	key = load_key(buf2,pass);
+ 	
+     return key;
+ }
+ 
+ /*
+     Load the user's personal certificates from
+     ~/.pine-smime/private
+  */
+ PERSONAL_CERT *get_personal_certs(const char *path)
+ {
+     char	buf[MAXPATH];
+     char	buf2[MAXPATH];
+     struct direct *d;
+     DIR	*dirp;
+     PERSONAL_CERT *result;
+ 
+     result = NULL;
+ 
+     build_path(buf,(char*) path,"private",sizeof(buf));
+ 
+     dirp = opendir(buf);
+     if (dirp) {
+ 
+         while (d=readdir(dirp)) {
+             BIO *in;
+             X509 *cert;
+ 
+             if (srchrstr(d->d_name,".key")) {
+ 
+ 				/* copy file name to temp buffer */
+ 				strcpy(buf2,d->d_name);
+ 				/* chop off ".key" trailier */
+ 				buf2[strlen(buf2)-4] = 0;
+ 				/* Look for certificate */
+ 				cert = get_cert_for(path,buf2);
+ 
+                 if (cert) {
+                     PERSONAL_CERT	*pc;
+ 
+ 					/* create a new PERSONAL_CERT, fill it in */
+ 
+                     pc = fs_get(sizeof(PERSONAL_CERT));
+                     pc->cert = cert;
+     	    		build_path(buf2,buf,d->d_name,sizeof(buf2));
+                     pc->file = cpystr(buf2);
+ 
+                     strcpy(pc->file + strlen(pc->file) - 4, ".key");
+ 
+                     /* Try to load the key with an empty password */
+                     pc->key = load_key(pc->file,"");
+ 
+                     pc->next = result;
+                     result = pc;
+                 }
+             }
+ 
+         }
+ 
+         closedir(dirp);
+     }
+ 
+     return result;
+ }
+ 
+ void personal_cert_free(PERSONAL_CERT **pcp)
+ {
+     if (pcp && *pcp) {
+ 
+         PERSONAL_CERT	*pc = *pcp;
+ 
+         fs_give((void**) &pc->file);
+ 
+         X509_free(pc->cert);
+ 
+         if (pc->key)
+             EVP_PKEY_free(pc->key);
+ 
+         personal_cert_free(&pc->next);
+ 
+         fs_give((void**) pcp);
+     }
+ }
+ 
+ #endif /* SMIME */
diff -Ncr pine4.53/pine/smkeys.h pine4.53-smime/pine/smkeys.h
*** pine4.53/pine/smkeys.h	Wed Dec 31 16:00:00 1969
--- pine4.53-smime/pine/smkeys.h	Wed Jan 15 12:48:15 2003
***************
*** 0 ****
--- 1,20 ----
+ 
+ #define PERSONAL_CERT struct personal_cert
+ 
+ PERSONAL_CERT {
+     X509    	    	*cert;
+     EVP_PKEY	    	*key;
+     char    	    	*file;
+     PERSONAL_CERT   	*next;
+ };
+ 
+ X509_STORE *get_ca_store(const char *d);
+ 
+ PERSONAL_CERT *get_personal_certs(const char *d);
+ 
+ X509 *get_cert_for(const char *path,const char *email);
+ void save_cert_for(const char *path,char *email,X509 *cert);
+ 
+ void personal_cert_free(PERSONAL_CERT **pc);
+ 
+ char *get_x509_subject_email(X509 *x);
