//////////////////////////////////////////////////////////////////////////// 
// 
// Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1993, 1994, 1995.
// Unpublished work.  All Rights Reserved.
// 
// The software contained on this media is the property of the
// DSTC Pty Ltd.  Use of this software is strictly in accordance
// with the license agreement in the accompanying LICENSE.DOC 
// file. If your distribution of this software does not contain 
// a LICENSE.DOC file then you have no rights to use this 
// software in any manner and should contact DSTC at the address 
// below to determine an appropriate licensing arrangement.
// 
//      DSTC Pty Ltd
//      Level 7, GP South
//      University of Queensland
//      St Lucia, 4072
//      Australia
//      Tel: +61 7 3365 4310
//      Fax: +61 7 3365 4311
//      Email: jcsi@dstc.qut.edu.au
// 
// This software is being provided "AS IS" without warranty of
// any kind.  In no event shall DSTC Pty Ltd be liable for
// damage of any kind arising out of or in connection with
// the use or performance of this software.
// 
//////////////////////////////////////////////////////////////////////////// 

package com.dstc.security.smime;

import java.util.Set;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Date;
import java.io.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import java.security.*;
import java.security.spec.*;
import java.security.cert.*;
import com.dstc.security.smime.*;
import com.dstc.security.provider.OID;
import com.dstc.security.asn1.Asn1;
import com.dstc.security.asn1.OctetString;
import com.dstc.security.asn1.Asn1Exception;
import com.dstc.security.x509.ExtensionFactory;
import com.dstc.security.x509.Base64OutputStream;
import com.dstc.security.x509.extns.SubjectAltName;
import com.dstc.security.cms.*;
import com.dstc.security.pkcs8.*;

/**
 * A simple text-based S/MIME client which exercises the S/MIME library.
 * 
 * <p> This can be run in interative and non-interactive mode. Interactive
 * mode is entered into by not passing parameters on the command line,
 * <pre>
 *      java com.dstc.security.smime.SecMail
 * </pre>
 *
 * <p> Non-interactive or Transformer mode is entered into by passing
 * either two or three parameters. A usage message is obtained by
 * <pre>
 *      java com.dstc.security.smime.SecMail --help
 * </pre>
 * SecMail in Transformer mode can sign and/or encrypt any MIME message,
 * and decode (decrypt and/or verify) any S/MIME message, provided the
 * certificate cache is properly set up. A current limitation, soon to
 * be removed, is that multiple recipients are not supported.
 *
 * <p>Configuration is through a properties file pointed to by
 * the System Property
 * <pre>
 *      jcs.smime.conf
 * </pre>
 * or, by default,
 * <pre>
 *      .javasecurity/smime.properties
 * </pre>
 *
 * @author Ming Yung
 */
public class SecMail 
{
  public static void main(String args[])
  {
    if (args.length != 0)
    {
      transform(args);
      return;
    }

    try
    {
      Transformer smail = new Transformer();

      InputStreamReader isr = new InputStreamReader(System.in);
      BufferedReader in = new BufferedReader(isr);

      while (true)
      {
        System.err.print("\n(C)ompose or (D)ecode an S/MIME message, " +
                         "(S)etup or (Q)uit? ");
        
        String resp = in.readLine();
        if (resp.equalsIgnoreCase("Q"))
        {
          smail.saveReceiverCerts();
          smail.saveTrustedCerts();
          return;
        }
        else if (resp.equalsIgnoreCase("C"))
        {
          MimeMessage msg = SecMail.composeMessage();
  
          System.err.print("\n(E)ncrypt, (S)ign or Sign (A)nd Encrypt " +
                           "the message? ");
          
          resp = in.readLine();
          if (resp.equalsIgnoreCase("E"))
          {
            msg = smail.encrypt(msg);
          }
          else if (resp.equalsIgnoreCase("S"))
          {
            msg = smail.sign(msg);
          }
          else if (resp.equalsIgnoreCase("A"))
          {
            msg = smail.sign(msg);
            msg = smail.encrypt(msg);
          }
          else
            break;
  
          System.err.print("\n(S)end if off or (O)utput to a file? ");
          
          resp = in.readLine();
          if (resp.equalsIgnoreCase("S"))
          {
            Transport.send(msg);
          }
          else
          {
            System.err.print("\nEnter file name: ");
          
            msg.writeTo(new FileOutputStream(in.readLine()));
          }
        }
        else if (resp.equalsIgnoreCase("D"))
        {
          System.err.print("\nFile to decode? ");
          resp = in.readLine();
          FileInputStream infile = new FileInputStream(resp);

          MimeMessage msg 
            = new MimeMessage(Session.getDefaultInstance(null, null), infile);
          infile.close();

          msg = smail.decode(msg);
          if (msg.getContentType().startsWith("text/plain"))
          {
            System.out.println("");
            msg.writeTo(System.out);
            System.out.println("");
          }
          else
          {
            System.err.print("\nFile to save message? ");
            resp = in.readLine();
            FileOutputStream outfile = new FileOutputStream(resp);
            msg.writeTo(outfile);
            outfile.flush();
            outfile.close();
          }
        }
        else if (resp.equalsIgnoreCase("S"))
        {
          System.err.print("\nEnter trusted cert to add: ");
          resp = in.readLine();
          FileInputStream file = new FileInputStream(resp);

          smail.addTrustedCert(file);
          smail.saveTrustedCerts();
        }
      }
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }

  public static void transform(String args[])
  {
    try
    {
      switch (args.length)
      {
        case (2):
          if (args[0].equals("-d"))
          {
            Transformer.decode(new FileInputStream(args[1]), System.out);
          }
          else if (args[0].equals("-e"))
          {
             Transformer.encrypt(new FileInputStream(args[1]), System.out);
          }
          else if (args[0].equals("-s"))
          {
             Transformer.sign(new FileInputStream(args[1]), System.out);
          }
          break;

        case (3):
          if (args[0].equals("-d"))
          {
            Transformer.decode(new FileInputStream(args[1]), 
                               new FileOutputStream(args[2]));          
          }
          else if (args[0].equals("-e"))
          {
             Transformer.encrypt(new FileInputStream(args[1]),
                     new FileOutputStream(args[2]));
          }
          else if (args[0].equals("-s"))
          {
             Transformer.sign(new FileInputStream(args[1]), 
                              new FileOutputStream(args[2]));
          }
          break;

        default:
          printUsage();
      }
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }

  private static void printUsage()
  {
    StringBuffer sb = new StringBuffer("Usage: ");
    sb.append("java com.dstc.security.smime.SecMail ");
    sb.append("[-d | -e | -s] <in file> <out file>\n\n");
    sb.append("where:\n");
    sb.append("  -d    Decodes input message\n");
    sb.append("  -e    Encrypts input message\n");
    sb.append("  -s    Signs input message\n\n");
    sb.append("If <out file> is unspecified output will be sent to stdout\n");
    sb.append("If no parameters are passed interactive mode is entered\n");
    System.out.println(sb.toString());
  }

  /**
   * Implements a UNIX Mail-like interface for composing a text message
   * The message is stored in a buffer.
   */
  public static MimeMessage composeMessage()
  {
    try
    {
      System.out.println("\nCompose message now, finishing off with " + 
                         "a . on a separate line\n");
  
      MimeMessage msg 
        = new MimeMessage(Session.getDefaultInstance(null, null));

      InputStreamReader isr = new InputStreamReader(System.in);
      BufferedReader in = new BufferedReader(isr);

      System.err.print("From: ");
      String from = in.readLine();

      System.err.print("To: ");
      String to = in.readLine();

      System.err.print("Subject: ");
      String subject = in.readLine();

      StringBuffer sb = new StringBuffer();
      String line;
      while (!(line = in.readLine()).equals("."))
      {
        sb.append(line);
        sb.append("\r\n");
      }

      byte[] msgBuffer = sb.toString().getBytes();

      InternetHeaders hdrs = new InternetHeaders();
      hdrs.addHeader("Content-Type", "text/plain; charset=us-ascii");
      hdrs.addHeader("Content-Transfer-Encoding", "7bit");
      MimeBodyPart part = new MimeBodyPart(hdrs, msgBuffer);

      msg.setContent(part.getContent(), "text/plain");
      msg.setHeader("Content-Type", "text/plain; charset=us-ascii");
      msg.setHeader("Content-Transfer-Encoding", "7bit");

      msg.setFrom(new InternetAddress(from));
  
      InternetAddress[] address = {new InternetAddress(to)};
      msg.setRecipients(Message.RecipientType.TO, address);
  
      msg.setSubject(subject);
      msg.setSentDate(new Date());
  
      return msg;
    }
    catch (Exception e)
    {
      e.printStackTrace();
      return null;
    }
  }
}
