/***********************************************************************
 *
 *  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.
 *
 *  Authors: Juan Carlos Luciani <jluciani@novell.com>
 *           Greg Richardson <grichardson@novell.com>
 *
 ***********************************************************************/

import com.sun.org.apache.xerces.internal.parsers.DOMParser;
import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.util.zip.ZipFile;

import org.xml.sax.InputSource;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NamedNodeMap;

/**
 * Configure Class.
 * <p>
 * This class readies the Authentication Token Service after it has been
 * installed.
 */
public class Configure
{
   // Other constants
   final static String SERVER_KEY_STORE_RELATIVE_PATH = "\\etc\\keys\\server\\jks-store";
   final static String CLIENT_KEY_STORE_RELATIVE_PATH = "\\etc\\keys\\trusted-ats-jks-store";
   final static String SIGNING_CERT_RELATIVE_PATH = "\\etc\\keys\\localSigningCert";

   // Configured properties
   Properties  m_properties;

   // Completion code
   int m_rc;

   // Template replace strings
   String[] m_rgsSearchFor;
   String[] m_rgsReplaceWith;

   /**
    * Constructor.
    *
    * @param properties Configuration properties.
    */
   Configure(Properties  properties)
   {
      m_rc = AtsConfigurator.ERROR_NO_ERROR;

      AtsConfigurator.log("Configure()- Start");

      try
      {
         // Save the properties
         m_properties = properties;

         // Process the properties
         if (AtsConfigurator.ERROR_NO_ERROR == (m_rc = processProperties()))
         {
            // Create the server keystore
            if (AtsConfigurator.ERROR_NO_ERROR == (m_rc = createServerKeystore()))
            {
               // Create the client keystore
               if (AtsConfigurator.ERROR_NO_ERROR == (m_rc = createClientKeystore()))
               {
                  // Setup the relocatable files
                  if (AtsConfigurator.ERROR_NO_ERROR == (m_rc = setupRelocatableFiles()))
                  {
                     // Update the tomcat server.xml
                     if (AtsConfigurator.ERROR_NO_ERROR == (m_rc = updateTomcatServerXML()))
                     {
                        // Setup the War file
                        if (AtsConfigurator.ERROR_NO_ERROR == (m_rc = createWarFileFromTemplate()))
                        {
                           // Create the ATS Service
                           m_rc = createATSService();
                        }
                     }
                  }
               }
            }
         }
      }
      catch (Exception e)
      {
         m_rc = AtsConfigurator.ERROR_EXCEPTION;
         AtsConfigurator.log("Exception" + e.getMessage());
      }

      AtsConfigurator.log("Configure()- End, rc= ", m_rc);
   }

   /**
    * Process properties.
    *
    * @return     Return code.
    */
   int processProperties()
   {
      int rc = AtsConfigurator.ERROR_NO_ERROR;

      AtsConfigurator.log("Configure.processProperties()- Start");

      // Setup replace template key/values arrays
      int i = 0;
      Enumeration e = m_properties.propertyNames();

      m_rgsSearchFor = new String[m_properties.size()];
      m_rgsReplaceWith = new String[m_properties.size()];

      while (e.hasMoreElements())
      {
         String key = (String) e.nextElement();
         String value = (String) m_properties.get(key);
         m_rgsSearchFor[i] = key;
         m_rgsReplaceWith[i] = value;
         i++;
      }

      AtsConfigurator.log("Configure.processProperties()- End, rc= ", rc);

      return rc;
   }

   /**
    * Check if the keystore already exists
    *
    * @param keyStoreRelPath  Relative path to the keystore
    * @return  true if the keystore exists otherwise false.
    */
   boolean keyStoreAlreadyExists(String keyStoreRelPath)
   {
      boolean retValue = false;

      AtsConfigurator.log("Configure.keyStoreAlreadyExists()- Start, relPath = " + keyStoreRelPath);

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

      AtsConfigurator.log("Configure.keyStoreAlreadyExists()- End, retValue = " + retValue );

      return retValue;
   }

   /**
    * Create the server keystore.
    *
    * @return     Return code.
    */
   int createServerKeystore()
   {
      int rc = AtsConfigurator.ERROR_NO_ERROR;

      AtsConfigurator.log("Configure.createServerKeystore()- Start");

      // Proceed only if the server keystore has not been created
      if (!keyStoreAlreadyExists(SERVER_KEY_STORE_RELATIVE_PATH))
      {
         String keytoolPath = m_properties.get(AtsConfigurator.JAVA_HOME_PROPERTY) + "\\bin\\keytool";
         String installDir = (String) m_properties.get(AtsConfigurator.INSTALL_DIR_PROPERTY);

         // Allocate and setup commandArray to create signing key
         String[] commandArray = new String[14];
         commandArray[0] = keytoolPath;
         commandArray[1] = "-genkey";
         commandArray[2] = "-alias"; commandArray[3] = "signingKey";
         commandArray[4] = "-keypass"; commandArray[5] = "secret";
         commandArray[6] = "-keystore"; commandArray[7] = installDir + SERVER_KEY_STORE_RELATIVE_PATH;
         commandArray[8] = "-storepass"; commandArray[9] = "secret";
         commandArray[10] = "-dname"; commandArray[11] = "cn=casaatsd@" + m_properties.get("COMPUTERNAME");
         commandArray[12] = "-validity"; commandArray[13] = "3600";

         // Create the signing key
         if (AtsConfigurator.ERROR_NO_ERROR == (rc =AtsConfigurator.invokeExternalCommand(commandArray)))
         {
            // Allocate and setup commandArray to export the signing certificate
            commandArray = new String[12];
            commandArray[0] = keytoolPath;
            commandArray[1] = "-export";
            commandArray[2] = "-alias"; commandArray[3] = "signingKey";
            commandArray[4] = "-keypass"; commandArray[5] = "secret";
            commandArray[6] = "-keystore"; commandArray[7] = installDir + SERVER_KEY_STORE_RELATIVE_PATH;
            commandArray[8] = "-storepass"; commandArray[9] = "secret";
            commandArray[10] = "-file"; commandArray[11] = installDir + SIGNING_CERT_RELATIVE_PATH;

            // Export self-signed certificate for the signing key
            if (AtsConfigurator.ERROR_NO_ERROR == (rc = AtsConfigurator.invokeExternalCommand(commandArray)))
            {
               // Allocate and setup commandArray to create key for Tomcat to do SSL communications
               commandArray = new String[14];
               commandArray[0] = keytoolPath;
               commandArray[1] = "-genkey";
               commandArray[2] = "-alias"; commandArray[3] = "tomcat";
               commandArray[4] = "-keypass"; commandArray[5] = "secret";
               commandArray[6] = "-keystore"; commandArray[7] = installDir + SERVER_KEY_STORE_RELATIVE_PATH;
               commandArray[8] = "-storepass"; commandArray[9] = "secret";
               commandArray[10] = "-dname"; commandArray[11] = "cn=" + m_properties.get("COMPUTERNAME");
               commandArray[12] = "-keyalg"; commandArray[13] = "RSA";

               // Create a key for Tomcat to do SSL communications
               rc = AtsConfigurator.invokeExternalCommand(commandArray);
            }
         }
      }

      AtsConfigurator.log("Configure.createServerKeystore()- End, rc= ", rc);

      return rc;
   }

   /**
    * Create the client keystore.
    *
    * @return     Return code.
    */
   int createClientKeystore()
   {
      int rc = AtsConfigurator.ERROR_NO_ERROR;

      AtsConfigurator.log("Configure.createClientKeystore()- Start");

      // Proceed only if the client keystore has not been created
      if (!keyStoreAlreadyExists(CLIENT_KEY_STORE_RELATIVE_PATH))
      {
         String keytoolPath = m_properties.get(AtsConfigurator.JAVA_HOME_PROPERTY) + "\\bin\\keytool";
         String installDir = (String) m_properties.get(AtsConfigurator.INSTALL_DIR_PROPERTY);

         // Allocate and setup commandArray to export the signing certificate
         String[] commandArray = new String[13];
         commandArray[0] = keytoolPath;
         commandArray[1] = "-import";
         commandArray[2] = "-noprompt";
         commandArray[3] = "-alias"; commandArray[4] = "signingCert";
         commandArray[5] = "-keypass"; commandArray[6] = "secret";
         commandArray[7] = "-keystore"; commandArray[8] = installDir + CLIENT_KEY_STORE_RELATIVE_PATH;
         commandArray[9] = "-storepass"; commandArray[10] = "secret";
         commandArray[11] = "-file"; commandArray[12] = installDir + SIGNING_CERT_RELATIVE_PATH;

         // Import the servers certificate in the client keystore
         rc = AtsConfigurator.invokeExternalCommand(commandArray);
      }

      AtsConfigurator.log("Configure.createClientKeystore()- End, rc= ", rc);

      return rc;
   }

   /**
    * Create file from template.
    *
    * @param fileTemplate  Template file.
    * @param fileOutput    Output file path.
    * @param escapePathCharsInReplaceString     Escape Windows Path Chars in Replace String before using.
    * @param useUnixPathCharsInReplaceString    Replace Windows Path Chars with "//" in Replace String before using.
    * @return     Return code.
    */
   int createFileFromTemplate(File fileTemplate, File fileOutput, boolean escapePathCharsInReplaceString, boolean useUnixPathCharsInReplaceString)
   {
      int rc = AtsConfigurator.ERROR_NO_ERROR;
      LineNumberReader lnr = null;
      FileWriter fwOutput = null;

      AtsConfigurator.log("Configure.createFileFromTemplate()- Start, template = " + fileTemplate);

      try
      {
         // Open the files
         lnr = new LineNumberReader(new FileReader(fileTemplate));
         try
         {
            fwOutput = new FileWriter(fileOutput);
            try
            {
               // For each line of text in the template file...
               String sLineTemplate;
               while (null != (sLineTemplate = lnr.readLine()))
               {
                  String sLineOutput = sLineTemplate;

                  // For each term to be replaced...
                  for (int i = 0; i < m_rgsSearchFor.length; i++)
                  {
                     // Replace all instances of the term on the line
                     int iSearchFor;
                     while (-1 != (iSearchFor = sLineOutput.indexOf(m_rgsSearchFor[i])))
                     {
                        // Check if we must escape path characters in replace string
                        String replaceString;
                        if (useUnixPathCharsInReplaceString)
                        {
                           replaceString = m_rgsReplaceWith[i].replace("\\", "//");
                        }
                        else
                        {
                           if (escapePathCharsInReplaceString)
                              replaceString = m_rgsReplaceWith[i].replace("\\", "\\\\");
                           else
                              replaceString = m_rgsReplaceWith[i];
                        }

                        AtsConfigurator.log("replacing " + m_rgsSearchFor[i] + " at position " + iSearchFor + " with " + replaceString);
                        sLineOutput = sLineOutput.substring(0, iSearchFor) + replaceString +
                           sLineOutput.substring(iSearchFor + m_rgsSearchFor[i].length());
                     }
                  }
                  try
                  {
                     fwOutput.write(sLineOutput + "\r\n");
                  }
                  catch (Exception e)
                  {
                     rc = AtsConfigurator.ERROR_EXCEPTION;
                  }
               }

               // Clean up
               fwOutput.flush();
            }
            catch (Exception e)
            {
               rc = AtsConfigurator.ERROR_OUTPUT_COPY_FAILED;
            }
         }
         catch (Exception e)
         {
            rc = AtsConfigurator.ERROR_FILEWRITER_CREATE_FAILED;
         }
      }
      catch (Exception e)
      {
         rc = AtsConfigurator.ERROR_UNABLE_TO_OPEN_TEMPLATE;
      }
      finally
      {
         try
         {
            if (lnr != null)
               lnr.close();

            if (fwOutput != null)
               fwOutput.close();
         }
         catch (Exception e)
         {
            // Do nothing
         }
      }

      AtsConfigurator.log("Configure.createFileFromTemplate()- End, rc= ", rc);

      return rc;
   }

   /**
    * Setup all of the needed relocatable files.
    *
    * @return     Return code.
    */
   int setupRelocatableFiles()
   {
      int rc;

      AtsConfigurator.log("Configure.setupRelocatableFiles()- Start");

      String installDir = (String) m_properties.get(AtsConfigurator.INSTALL_DIR_PROPERTY);
      if (AtsConfigurator.ERROR_NO_ERROR == (rc = createFileFromTemplate(new File(installDir + "\\etc\\svc\\templates\\server-sun.xml"),
                                                         new File(installDir + "\\catalinabase\\conf\\server.xml"),
                                                         false, false)))
         if (AtsConfigurator.ERROR_NO_ERROR == (rc = createFileFromTemplate(new File(installDir + "\\etc\\svc\\templates\\startup.bat"),
                                                            new File(installDir + "\\bin\\startup.bat"),
                                                            false, false)))
            if (AtsConfigurator.ERROR_NO_ERROR == (rc = createFileFromTemplate(new File(installDir + "\\etc\\svc\\templates\\shutdown.bat"),
                                                               new File(installDir + "\\bin\\shutdown.bat"),
                                                               false, false)))
               if (AtsConfigurator.ERROR_NO_ERROR == (rc = createFileFromTemplate(new File(installDir + "\\etc\\svc\\templates\\CasaAuthPolicyEditor.bat"),
                                                                  new File(installDir + "\\bin\\CasaAuthPolicyEditor.bat"),
                                                                  false, false)))
                  if (AtsConfigurator.ERROR_NO_ERROR == (rc = createFileFromTemplate(new File(installDir + "\\etc\\svc\\templates\\CasaAuthTokenSettingsEditor.bat"),
                                                                     new File(installDir + "\\bin\\CasaAuthTokenSettingsEditor.bat"),
                                                                     false, false)))
                     if (AtsConfigurator.ERROR_NO_ERROR == (rc = createFileFromTemplate(new File(installDir + "\\etc\\svc\\templates\\CasaIdenTokenSettingsEditor.bat"),
                                                                        new File(installDir + "\\bin\\CasaIdenTokenSettingsEditor.bat"),
                                                                        false, false)))
                        if (AtsConfigurator.ERROR_NO_ERROR == (rc = createFileFromTemplate(new File(installDir + "\\etc\\svc\\templates\\CasaSvcSettingsEditor.bat"),
                                                                           new File(installDir + "\\bin\\CasaSvcSettingsEditor.bat"),
                                                                           false, false)))
                           if (AtsConfigurator.ERROR_NO_ERROR == (rc = createFileFromTemplate(new File(installDir + "\\etc\\svc\\templates\\svc.settings"),
                                                                              new File(installDir + "\\etc\\svc\\svc.settings"),
                                                                              false, false)))
                              if (AtsConfigurator.ERROR_NO_ERROR == (rc = createFileFromTemplate(new File(installDir + "\\etc\\svc\\templates\\jaas.conf"),
                                                                                 new File(installDir + "\\etc\\svc\\jaas.conf"),
                                                                                 false, false)))
                                 if (AtsConfigurator.ERROR_NO_ERROR == (rc = createFileFromTemplate(new File(installDir + "\\etc\\svc\\templates\\svc_casa_crypto.properties"),
                                                                                    new File(installDir + "\\etc\\svc\\casa_crypto.properties"),
                                                                                    false, true)))
                                    if (AtsConfigurator.ERROR_NO_ERROR == (rc = createFileFromTemplate(new File(installDir + "\\etc\\svc\\templates\\client_casa_crypto.properties"),
                                                                                       new File(installDir + "\\etc\\keys\\client\\casa_crypto.properties"),
                                                                                       false, true)))
                                       rc = createFileFromTemplate(new File(installDir + "\\etc\\svc\\templates\\log4j.properties"),
                                                                   new File(installDir + "\\etc\\svc\\log4j.properties"),
                                                                   true, false);

      AtsConfigurator.log("Configure.setupRelocatableFiles()- End, rc= ", rc);

      return rc;
   }

   /**
    * Find first child node with matching node name.
    *
    * @param parentNode    Parent node.
    * @param nodeName      Name of node.
    * @return              Child node found or null.
    */
   public static Node findChildNode(Node parentNode, String nodeName)
   {
      Node retNode = null;
      Node childNode;
      Node nextNode = parentNode.getFirstChild();
      while ((childNode = nextNode) != null)
      {
         // Set next before we change anything
         nextNode = childNode.getNextSibling();

         // Handle child by node type
         if (childNode.getNodeType() == Node.ELEMENT_NODE)
         {
            // Check if this is the element node wanted
            if (childNode.getNodeName().equalsIgnoreCase(nodeName))
            {
               // Found the node
               retNode = childNode;
               break;
            }
         }
      }

      return retNode;
   }

   /**
    * Update the Setup all of the needed relocatable files.
    *
    * @return     Return code.
    */
   int updateTomcatServerXML()
   {
      int rc = AtsConfigurator.ERROR_NO_ERROR;

      AtsConfigurator.log("Configure.updateTomcatServerXML()- Start");

      // Check if a keystore path property was specified
      String keystorePath = (String) m_properties.get(AtsConfigurator.KEYSTORE_PATH_PROPERTY);
      if (keystorePath != null)
      {
         AtsConfigurator.log("Need to update server.xml");

         // The keystore path property was specified. The keystore pass and keystore algorithms
         // should also be specified.
         String keystorePass = (String) m_properties.get(AtsConfigurator.KEYSTORE_PASS_PROPERTY);
         String keystoreAlg = (String) m_properties.get(AtsConfigurator.KEYSTORE_ALG_PROPERTY);
         if (keystorePass != null && keystoreAlg != null)
         {
            // We have all of the information necessary to update the connector in the server.xml
            // file, proceed.
            FileInputStream inStream = null;
            FileOutputStream outStream = null;
            try
            {
               String installDir = (String) m_properties.get(AtsConfigurator.INSTALL_DIR_PROPERTY);
               File serverXmlFile = new File(installDir + "\\catalinabase\\conf\\server.xml");
               inStream = new FileInputStream(serverXmlFile);

               // Pull the server.xml file into a DOM
               DOMParser parser = new DOMParser();
               parser.parse(new InputSource(inStream));
               Document doc = parser.getDocument();

               Node docElement = doc.getDocumentElement();
               if (docElement != null)
               {
                  Node serviceElement = findChildNode(docElement, "service");
                  if (serviceElement != null)
                  {
                     Node connectorElement = findChildNode(serviceElement, "connector");
                     if (connectorElement != null)
                     {
                        NamedNodeMap connectorAttributesMap = connectorElement.getAttributes();
                        if (connectorAttributesMap != null)
                        {
                           int   attributeSetFailures = 0;

                           Node keystoreFileAttributeNode = connectorAttributesMap.getNamedItem("keystoreFile");
                           if (keystoreFileAttributeNode != null)
                           {
                              keystoreFileAttributeNode.setNodeValue(keystorePath);
                           }
                           else
                           {
                              AtsConfigurator.log("keystoreFile attribute not configured for connector");
                              attributeSetFailures ++;
                              rc = AtsConfigurator.ERROR_INVALID_TOMCAT_SERVER_XML_FORMAT;
                           }

                           Node keystorePassAttributeNode = connectorAttributesMap.getNamedItem("keystorePass");
                           if (keystorePassAttributeNode != null)
                           {
                              keystorePassAttributeNode.setNodeValue(keystorePass);
                           }
                           else
                           {
                              AtsConfigurator.log("keystorePass attribute not configured for connector");
                              attributeSetFailures ++;
                              rc = AtsConfigurator.ERROR_INVALID_TOMCAT_SERVER_XML_FORMAT;
                           }

                           Node algorithmAttributeNode = connectorAttributesMap.getNamedItem("algorithm");
                           if (algorithmAttributeNode != null)
                           {
                              algorithmAttributeNode.setNodeValue(keystoreAlg);
                           }
                           else
                           {
                              AtsConfigurator.log("algorithm attribute not configured for connector");
                              attributeSetFailures ++;
                              rc = AtsConfigurator.ERROR_INVALID_TOMCAT_SERVER_XML_FORMAT;
                           }

                           if (attributeSetFailures == 0)
                           {
                              outStream = new FileOutputStream(serverXmlFile);
                              OutputFormat format = new OutputFormat(doc);
                              XMLSerializer serializer = new XMLSerializer(outStream, format);
                              serializer.serialize(doc.getDocumentElement());
                           }
                        }
                        else
                        {
                           AtsConfigurator.log("Failed to obtain connector attributes map");
                           rc = AtsConfigurator.ERROR_INVALID_TOMCAT_SERVER_XML_FORMAT;
                        }
                     }
                     else
                     {
                        AtsConfigurator.log("Failed to obtain connector node");
                        rc = AtsConfigurator.ERROR_INVALID_TOMCAT_SERVER_XML_FORMAT;
                     }
                  }
                  else
                  {
                     AtsConfigurator.log("Failed to obtain service node");
                     rc = AtsConfigurator.ERROR_INVALID_TOMCAT_SERVER_XML_FORMAT;
                  }
               }
               else
               {
                  AtsConfigurator.log("Failed to obtain document element");
                  rc = AtsConfigurator.ERROR_INVALID_TOMCAT_SERVER_XML_FORMAT;
               }
            }
            catch (Exception e)
            {
               AtsConfigurator.log("Exception: " + e.getMessage());
               rc = AtsConfigurator.ERROR_EXCEPTION;
            }
            finally
            {
               try
               {
                  // Close input stream if necessary
                  if (inStream != null)
                     inStream.close();

                  // Flush and close output stream if necessary
                  if (outStream != null)
                  {
                     outStream.flush();
                     outStream.close();
                  }
               }
               catch (Exception e)
               {
                  // Do nothing
               }
            }
         }
         else
         {
            AtsConfigurator.log("Not all keystore related properties specified");
            rc = AtsConfigurator.ERROR_MISSING_KEYSTORE_PROPERTY;
         }
      }

      AtsConfigurator.log("Configure.updateTomcatServerXML()- End, rc= ", rc);

      return rc;
   }

   /**
    * Move files from Jar to Jar.
    *
    * @param josDst  Destination Jar Output stream.
    * @param jfSrc   Source Jar.
    * @return     Return code.
    */
   int moveFilesFromJarToJar(ZipOutputStream josDst, ZipFile jfSrc)
   {
      int rc = AtsConfigurator.ERROR_NO_ERROR;

      AtsConfigurator.log("Configure.moveFilesFromJarToJar()- Start");

      try
      {
         Enumeration         entries = jfSrc.entries();
         while (entries.hasMoreElements())
         {
            ZipEntry jeLoop = (ZipEntry)entries.nextElement();

            // Skip WEB-INF/classes/cypto.properties
            if (jeLoop.getName().equalsIgnoreCase("WEB-INF/classes/casa_crypto.properties"))
            {
               AtsConfigurator.log("skipping: " + "WEB-INF/classes/casa_crypto.properties");
            }

            // Move the rest of the files over
            else
            {
               AtsConfigurator.log("Transferring jar file: " + jeLoop.getName());

               // Create the input stream
               InputStream isLoop = jfSrc.getInputStream(jeLoop);

               // Set up the output stream
               ZipEntry zeIndex = new ZipEntry(jeLoop.getName());
               josDst.putNextEntry( zeIndex);

               // Transfer the file contents
               int bRead;
               byte [] b = new byte[4096];
               while (-1 != (bRead = isLoop.read(b)))
               {
                  josDst.write(b, 0, bRead);
               }

               // all done
               josDst.closeEntry();
               isLoop.close();
            }
         }
      }
      catch (Exception e)
      {
         rc = AtsConfigurator.ERROR_JAR_COPY_FAILED;
      }

      AtsConfigurator.log("Configure.moveFilesFromJarToJar()- End, rc= ", rc);

      return rc;
   }

   /**
    * Add file to Jar.
    *
    * @param sFilename  Path to file.
    * @param sName      Zip entry name.
    * @param josJarNew  Jar output stream.
    * @return     Return code.
    */
   int addFileToJar(String sFilename, String sName, ZipOutputStream josJarNew)
   {
      int rc = AtsConfigurator.ERROR_NO_ERROR;

      AtsConfigurator.log("Configure.addFileToJar()- Start");

      try
      {
         // Create the input stream
         FileInputStream  fis = new FileInputStream(sFilename);

         // Set up the output stream
         ZipEntry je = new ZipEntry(sName);
         josJarNew.putNextEntry(je);

         // Tansfer the contents of the file
         int bRead;
         byte [] b = new byte[4096];
         while (-1 != (bRead = fis.read(b)))
         {
            josJarNew.write(b, 0, bRead);
         }

         // All done
         fis.close();
         josJarNew.closeEntry();
      }
      catch (Exception e)
      {
         AtsConfigurator.log(e.toString());
         rc = AtsConfigurator.ERROR_ADD_FILE_TO_JAR_FAILED;
      }

      AtsConfigurator.log("Configure.addFileToJar()- End, rc= ", rc);

      return rc;
   }

   /**
    * Create War file from template.
    *
    * @return     Return code.
    */
   int createWarFileFromTemplate()
   {
      String            sWarTemplate   = m_properties.get(AtsConfigurator.INSTALL_DIR_PROPERTY) + "\\etc\\svc\\templates\\CasaAuthTokenSvc.war";
      String            sWarNew        = m_properties.get(AtsConfigurator.INSTALL_DIR_PROPERTY) + "\\catalinabase\\webapps\\CasaAuthTokenSvc.war";
      File              fileWarTemplate   = new File(sWarTemplate);
      File              fileWarNew     = new File(sWarNew);
      FileOutputStream  fosWarNew      = null;
      ZipOutputStream   josWarNew      = null;
      ZipFile           jfTemplate     = null;
      int               rc             = AtsConfigurator.ERROR_NO_ERROR;

      AtsConfigurator.log("Configure.createWarFileFromTemplate()- Start");

      // Ensure that the war template exists
      AtsConfigurator.log("looking for war template: " + sWarTemplate);
      if (!fileWarTemplate.exists())
      {
         rc = AtsConfigurator.ERROR_WAR_TEMPLATE_FILE_MISSING;
      }

      if (rc == AtsConfigurator.ERROR_NO_ERROR)
      {
         // Ensure that the new jar name does not exist
         if (fileWarNew.exists())
         {
            AtsConfigurator.log("war file already exists: " + sWarNew);
            if (!fileWarNew.delete())
            {
               AtsConfigurator.log("could not delete war file: " + sWarNew);
               rc = AtsConfigurator.ERROR_NEW_JAR_CANNOT_BE_REPLACED;
            }
         }
      }

      if (rc == AtsConfigurator.ERROR_NO_ERROR)
      {
         // Create/open the new jar
         try
         {
            AtsConfigurator.log("create new war file output stream: " + sWarNew);
            fosWarNew = new FileOutputStream(fileWarNew);
            josWarNew = new ZipOutputStream(fosWarNew);
         }
         catch (IOException ioe0)
         {
            AtsConfigurator.log(ioe0.toString());
            rc = AtsConfigurator.ERROR_CREATE_WAR_FOS_FAILED;
         }
      }

      if (rc == AtsConfigurator.ERROR_NO_ERROR)
      {
         // Open the old jar
         try
         {
            jfTemplate = new ZipFile(sWarTemplate);
         }
         catch (IOException ioe1)
         {
            rc = AtsConfigurator.ERROR_OPEN_JAR_TEMPLATE_FAILED;
         }
      }

      if (rc == AtsConfigurator.ERROR_NO_ERROR)
      {
         // Transfer the files
         rc = moveFilesFromJarToJar(josWarNew, jfTemplate);
      }

      // Add the files
      if (rc == AtsConfigurator.ERROR_NO_ERROR)
      {
         int i;
         String [] rgsFilesToAdd = new String[1];
         rgsFilesToAdd[0] = m_properties.get(AtsConfigurator.INSTALL_DIR_PROPERTY) + "\\etc\\svc\\casa_crypto.properties";
         String [] rgsNames = new String[1];
         rgsNames[0] = "WEB-INF/classes/casa_crypto.properties";
         for (i = 0; i < rgsFilesToAdd.length; i++)
         {
            AtsConfigurator.log("Adding file: " + rgsFilesToAdd[i] + " with name " + rgsNames[i]);
            rc = addFileToJar(rgsFilesToAdd[i], rgsNames[i], josWarNew);
         }
      }

      // Close up
      if (null != josWarNew)
      {
         try
         {
            josWarNew.flush();
         }
         catch (IOException ioe2)
         {
            rc = AtsConfigurator.ERROR_JOS_FLUSH_FAILED;
         }
         try
         {
            josWarNew.close();
         }
         catch (IOException ioe3)
         {
            rc = AtsConfigurator.ERROR_JOS_CLOSE_FAILED;
         }
         try
         {
            fosWarNew.close();
         }
         catch (IOException ioe4)
         {
            rc = AtsConfigurator.ERROR_FOS_CLOSE_FAILED;
         }
      }
      if (null != jfTemplate)
      {
         try
         {
            jfTemplate.close();
         }
         catch (IOException ioe5)
         {
            rc = AtsConfigurator.ERROR_JFTEMPLATE_CLOSE_FAILED;
         }
      }

      AtsConfigurator.log("Configure.createWarFileFromTemplate()- End, rc= ", rc);

      return rc;
   }

   /**
    * Create ATS Service.
    *
    * @return     Return code.
    */
   int createATSService()
   {
      int rc = AtsConfigurator.ERROR_NO_ERROR;

      AtsConfigurator.log("Configure.createATSService()- Start");

      String installDir = (String) m_properties.get(AtsConfigurator.INSTALL_DIR_PROPERTY);
      String sDirCatalinaBase = installDir + "/catalinabase";
      String sDirCatalinaHome = (String) m_properties.get(AtsConfigurator.TOMCAT_HOME_PROPERTY);
      String sFileJaasConf = installDir + "\\etc\\svc\\jaas.conf";
      String sDirConfig  = installDir + "/etc/svc";
      String sDirJavaHome = (String) m_properties.get(AtsConfigurator.JAVA_HOME_PROPERTY);
      String sLogPrefix = "casa-auth-token-svc";
      String sDirLogs = sDirCatalinaBase + "/logs";

      // Determine the path to the Tomcat5 executable
      String sExe = (String) m_properties.get(AtsConfigurator.TOMCAT5_PROPERTY);
      if (sExe == null)
         sExe = m_properties.get(AtsConfigurator.TOMCAT_HOME_PROPERTY) + "\\bin\\tomcat5.exe";

      /*
      * Note that in the following code we do not bother to check the return of the invokeExternalCommand
      * call. This is because I have found that some versions of tomcat5.exe do not always return success
      * even though they should.
      */
      String[] commandArray = {
              sExe, "//IS//CasaAuthTokenService", "--StartClass", "org.apache.catalina.startup.Bootstrap", "--StopClass", "org.apache.catalina.startup.Bootstrap", "--StartParams", "start", "--StopParams", "stop", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray);

      String[] commandArray1 = {
              sExe, "//US//CasaAuthTokenService", "--Startup", "auto", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray1);

      String[] commandArray2 = {
         sExe, "//US//CasaAuthTokenService", "--JvmOptions", "\"-Dcatalina.base=" + sDirCatalinaBase + "\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray2);

      String[] commandArray3 = {
         sExe, "//US//CasaAuthTokenService", "++JvmOptions", "\"-Dcatalina.home=" + sDirCatalinaHome + "\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray3);

      String[] commandArray4 = {
         sExe, "//US//CasaAuthTokenService", "++JvmOptions", "\"-Djava.endorsed.dirs=" + sDirCatalinaHome + "/common/endorsed\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray4);

      String[] commandArray5 = {
         sExe, "//US//CasaAuthTokenService", "++JvmOptions", "\"-Djava.security.auth.login.config=" + sFileJaasConf + "\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray5);

      String[] commandArray6 = {
         sExe, "//US//CasaAuthTokenService", "++JvmOptions", "\"-Dcom.novell.casa.authtoksvc.config=" + sDirConfig + "\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray6);

      String[] commandArray7 = {
         sExe, "//US//CasaAuthTokenService", "++JvmOptions", "\"-Dlog4j.configuration=file:" + installDir + "/etc/svc/log4j.properties" + "\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray7);

      String[] commandArray8 = {
         sExe, "//US//CasaAuthTokenService", "++JvmOptions", "\"-Djava.io.tmpdir=" + sDirCatalinaBase+ "/temp\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray8);

      String[] commandArray9 = {
         sExe, "//US//CasaAuthTokenService", "--StartMode", "jvm", "--StopMode", "jvm", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray9);

      String[] commandArray10 = {
         sExe, "//US//CasaAuthTokenService", "--JvmMs", "128", "--JvmMx", "512", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray10);

      String[] commandArray11 = {
         sExe, "//US//CasaAuthTokenService", "--Classpath", "\"" + sDirCatalinaHome + "/bin/bootstrap.jar\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray11);

      String[] commandArray12 = {
         sExe, "//US//CasaAuthTokenService", "--Jvm", "\"" + sDirJavaHome + "/jre/bin/server/jvm.dll\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray12);

      String[] commandArray13 = {
         sExe, "//US//CasaAuthTokenService", "--LogPath", "\"" + sDirLogs + "\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray13);

      String[] commandArray14 = {
         sExe, "//US//CasaAuthTokenService", "--LogPrefix", "\"" + sLogPrefix + "_service.log\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray14);

      String[] commandArray15 = {
         sExe, "//US//CasaAuthTokenService", "--StdOutput", "\"" + sDirLogs + File.separator + sLogPrefix + "_stdout.log\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray15);

      String[] commandArray16 = {
         sExe, "//US//CasaAuthTokenService", "--StdError", "\"" + sDirLogs + File.separator + sLogPrefix + "_stderr.log\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray16);

      String[] commandArray17 = {
         sExe, "//US//CasaAuthTokenService", "--DisplayName", "CasaAuthTokenSvc", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray17);

      String[] commandArray18 = {
         sExe, "//US//CasaAuthTokenService", "--Description", "\"Casa Authentication Token Service\"", "--LogPath", AtsConfigurator.m_logFolderPath, "--LogPrefix", "AtsSvcInstall.log"
      };
      AtsConfigurator.invokeExternalCommand(commandArray18);

      AtsConfigurator.log("Configure.createATSService()- End, rc= ", rc);

      return rc;
   }
}
