/***********************************************************************
 * 
 *  Copyright (C) 2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 *  Author: Greg Richardson <grichardson@novell.com>
 *  
 ***********************************************************************/

import java.io.*;
import java.util.*;

public class ServerKeystoreSetup
{
   final static int ERROR_NO_ERROR                    = 0;
   final static int ERROR_EXEC_FAILED                 = -1;
   final static int ERROR_INVALID_NUMBER_OF_PARAMS    = -2;
   final static int ERROR_BAD_INSTALL_DIR_PARAM       = -3;
   final static int ERROR_MISSING_INSTALL_DIR         = -4;
   final static int ERROR_INSTALL_DIR_NOT_A_DIR       = -5;
   final static int ERROR_BAD_PROPERTY_FILE_PARAM     = -6;
   final static int ERROR_MISSING_PROPERTIES_FILE     = -7;
   final static int ERROR_UNABLE_TO_READ_PROPERTIES   = -8;
   final static int ERROR_UNKNOWN_PARAM               = -9;
   final static int ERROR_MISSING_INSTALL_DIR_PARAM   = -10;
   final static int ERROR_REQUIRED_VALUE_MISSING      = -11;
   final static int ERROR_EXEC_INTERRUPTED            = -12;
   final static int ERROR_IO_EXCEPTION = -13;

   final static String INSTALL_DIR                 = "installdir=";
   final static String PROPERTY_FILE               = "propertyfile=";

   String      sInstallDir;
   Properties  properties;
   int rc;

   // debug stuff
   //File file;
   //FileWriter fw;

   public static void main(String[] args)
   {
      ServerKeystoreSetup p = new ServerKeystoreSetup(args);
      System.exit(p.rc);
   }

   ServerKeystoreSetup(String[] args)
   {
      rc = ERROR_NO_ERROR;
      try
      {
         // DEBUG STUFF
         //file = new File("c:\\test2.log");
         //fw = new FileWriter(file);

         // Process the input params
         if (ERROR_NO_ERROR == (rc = processArgs(args)))
         {
            // Make sure we got everything we need
            if (ERROR_NO_ERROR == (rc = findRequiredValues()))
            {
               // Make sure the server keystore has been created
               rc = createServerKeystore();
            }
         }

         // DEBUG STUFF
         log(rc);
         //fw.flush();
         //fw.close();
         // DEBUG STUFF
      }
      catch (IOException e)
      {
         rc = ERROR_IO_EXCEPTION;
      }
   }

   int processArgs(String[] argsOld)
   {
      String sProperties;
      File fileInstallDir = null;
      File fileProperties = null;
      FileInputStream fisProperties = null;
      int iOld;
      int i;
      String args[] = new String[argsOld.length];
      int iNew;

      log("Original arg count " + argsOld.length);
      for (i = 0; i < argsOld.length; i++)
      {
         log("Arg " + i + " = " + argsOld[i] + "\r\n");
      }

      // Validate the number of parameters
      if (args.length < 2)
      {
         return ERROR_INVALID_NUMBER_OF_PARAMS;
      }

      iNew = -1;
      for (iOld = 0; iOld < argsOld.length; iOld++)
      {
         if (0 <= argsOld[iOld].indexOf("="))
         {
            iNew++;
            args[iNew] = argsOld[iOld];
            for (i = iOld + 1; i < argsOld.length && (-1 == argsOld[i].indexOf("=")); i++)
            {
               args[iNew] += " " + argsOld[i];
            }
         }
      }

      log("New arg count " + args.length);
      for (i = 0; i < args.length; i++)
      {
         log("Arg " + i + " = " + args[i] + "\r\n");
      }


      for (i = 0; i <= iNew; i++)
      {
         // is this the install dir param?
         if (args[i].startsWith(INSTALL_DIR))
         {
            // Make sure it is more the the param tag
            if (args[i].length() <= INSTALL_DIR.length())
            {
               return ERROR_BAD_INSTALL_DIR_PARAM;
            }

            sInstallDir = args[i].substring(INSTALL_DIR.length()).trim();
            fileInstallDir = new File(sInstallDir);

            // Make sure the install dir can be found
            if (!fileInstallDir.exists())
            {
               return ERROR_MISSING_INSTALL_DIR;
            }

            // Make sure the install dir is a directory
            if (!fileInstallDir.isDirectory())
            {
               return ERROR_INSTALL_DIR_NOT_A_DIR;
            }
         }

         // is this the properties file param?
         else if (args[i].startsWith(PROPERTY_FILE))
         {
            // Make sure it is more the the param tag
            if (args[i].length() <= PROPERTY_FILE.length())
            {
               return ERROR_BAD_PROPERTY_FILE_PARAM;
            }

            sProperties = args[i].substring(PROPERTY_FILE.length()).trim();
            fileProperties = new File(sProperties);

            // Make sure the properties file can be found
            if (!fileProperties.exists())
            {
               return ERROR_MISSING_PROPERTIES_FILE;
            }

            // Read the properties
            try
            {
               fisProperties = new FileInputStream(fileProperties);
               properties = new Properties();
               properties.load(fisProperties);
            }
            catch (IOException ioe)
            {
               return ERROR_UNABLE_TO_READ_PROPERTIES;
            }
         }

         // Unknown parameter
         else
         {
            log(ERROR_UNKNOWN_PARAM, args[i]);
            return ERROR_UNKNOWN_PARAM;
         }
      }

      // Make sure we got an install dir
      if (null == fileInstallDir)
      {
         return ERROR_MISSING_INSTALL_DIR_PARAM;
      }

      return ERROR_NO_ERROR;
   }

   int findRequiredValues()
   {
      String[] rgsRequired = { 
         "ATS_JAVA_HOME", 
         "COMPUTERNAME"};
      int i;
      String sValue;

      for (i = 0; i < rgsRequired.length; i++)
      {
         log("look for required value: " + rgsRequired[i]);

         if (!properties.containsKey(rgsRequired[i]))
         {
            log("look for required value in envirement: " + rgsRequired[i]);
            if (null == (sValue = System.getProperty(rgsRequired[i])))
            {
               log("unable to find required value in envirement: " + rgsRequired[i]);
               return ERROR_REQUIRED_VALUE_MISSING;
            }
            log("found required value in envirement: " + rgsRequired[i] + "  =  " + sValue);
            properties.put(rgsRequired[i], sValue);
         }
         log("found required value: " + rgsRequired[i] + "  =  " + properties.get(rgsRequired[i]));
      }
      return ERROR_NO_ERROR;
   }


   int createServerKeystore()
   {
      int rc;
      String sKeytool = properties.get("ATS_JAVA_HOME") + "\\bin\\keytool.exe";
      String sHost = (String)properties.get("COMPUTERNAME");

      log("keytool = " + sKeytool);
      log("host = " + sHost);

      // Do not do anything if the server keystore has already been created
      if (keyStoreAlreadyExists())
      {
         return ERROR_NO_ERROR;
      }

      // Create the server keystore with the key that will be used for signing tokens
      if (ERROR_NO_ERROR == (rc =invokeCommand(sKeytool + " -genkey -alias signingKey -keystore " + 
         sInstallDir + "ats\\etc\\keys\\server\\jks-store -dname \"cn=casaatsd@" + sHost +
         "\" -validity 3600 -keypass secret -storepass secret")))
      {
         // Export self-signed certificate for the signing key
         if (ERROR_NO_ERROR == (rc = invokeCommand(sKeytool + " -export -keystore " +
            sInstallDir + "ats\\etc\\keys\\server\\jks-store -alias signingKey -storepass secret -keypass secret -file " +
            sInstallDir + "ats\\etc\\keys\\casaatsdSigningCert")))
         {
            // Create a key for Tomcat to do SSL communications
            rc = invokeCommand(sKeytool + " -genkey -alias tomcat -keyalg RSA -keystore " +
               sInstallDir + "ats\\etc\\keys\\server\\jks-store -dname \"cn=" +
               sHost + "\" -validity 3600 -keypass secret -storepass secret");
         }
      }
      return rc;
   }

   boolean keyStoreAlreadyExists()
   {
      File fileKeystore = new File(sInstallDir + "ats\\etc\\keys\\server\\jks-store");

      // Why is this always returning true?  exists() also always returns true.
      // log("keystore (" + sInstallDir + "ats\\etc\\keys\\server\\jks-store" + ") already exists = " + (file.isFile()));
      //    return (file.isFile());

      File fileParent = fileKeystore.getParentFile();
      String[] rgChildren = fileParent.list();
      if (null != rgChildren)
      {
         for (int i = 0; i < rgChildren.length; i++)
         {
            log("child " + i + " = " + rgChildren[i]);
            if ("jks-store".equals(rgChildren[i]))
            {
               return true;
            }
         }
      }

      return false;
   }

   int invokeCommand(String sCommand)
   {
      Process p;
      int rc;

      log("invoke command: "  + sCommand);
      Runtime runtime = Runtime.getRuntime();

      try
      {
         p = runtime.exec(sCommand);
         try
         {
            rc = p.waitFor();
            log("invoke command return code: " + rc);
         }
         catch (InterruptedException ie)
         {
            log(ERROR_EXEC_INTERRUPTED, sCommand);
            return ERROR_EXEC_INTERRUPTED;
         }
      }
      catch (IOException e)
      {
         log("Ioexception");
         return ERROR_EXEC_FAILED;
      }
      
      return ERROR_NO_ERROR;
   }

   void log(int err)
   {
      log(err, null);
   }

   void log(int err, String s)
   {
      String sMessage = "";

      switch (err)
      {
         case ERROR_NO_ERROR:
            sMessage = "No error";
            break;
         case ERROR_EXEC_FAILED:
            sMessage = "Execute command failed ";
            break;
         case ERROR_INVALID_NUMBER_OF_PARAMS:
            sMessage = "Invalid number of params";
            break;
         case ERROR_BAD_INSTALL_DIR_PARAM:
            sMessage = "Install dir parameter is bad";
            break;
         case ERROR_MISSING_INSTALL_DIR:
            sMessage = "Missing install dir";
            break;
         case ERROR_INSTALL_DIR_NOT_A_DIR:
            sMessage = "Install dir is not a dir";
            break;
         case ERROR_BAD_PROPERTY_FILE_PARAM:
            sMessage = "Invalid porperty file parameter";
            break;
         case ERROR_MISSING_PROPERTIES_FILE:
            sMessage = "Property file not found";
            break;
         case ERROR_UNABLE_TO_READ_PROPERTIES:
            sMessage = "Unable to read property file";
            break;
         case ERROR_UNKNOWN_PARAM:
            sMessage = "Unknown parameter: ";
            break;
         case ERROR_MISSING_INSTALL_DIR_PARAM:
            sMessage = "Install dir parameter is missing";
            break;
         case ERROR_REQUIRED_VALUE_MISSING:
            sMessage = "Required value is missing ";
            break;
         case ERROR_EXEC_INTERRUPTED:
            sMessage = "Execution iinterrupted: ";
            break;
         case ERROR_IO_EXCEPTION:
            sMessage = "IO Exception ";
            break;
         default:
            sMessage = "Unknown error";
            break;
      }

      if (null != s)
      {
         sMessage = sMessage + s;
      }
      log(sMessage);
   }

   void log(String s)
   {
      /*
      try
      {
         fw.write(this.getClass().getName() + ": " + s + "\r\n");
      }
      catch (IOException ioe)
      {
      }
      */
   }

}