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

package com.novell.casa.authtoksvc;

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

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import org.apache.log4j.Logger;

/**
 * AuthTokenConfig Class.
 * <p>
 * This class obtains and maintains authentication token configuration.
 * 
 */
public final class AuthTokenConfig
{
   private static final Logger m_log = Logger.getLogger(AuthTokenConfig.class);

   // Well known authentication token configuration settings
   public final static String TokenLifetime = "TokenLifetime";
   public final static String LifetimeShorter = "LifetimeShorter";
   public final static String IdentityTokenType = "IdentityTokenType";

   // Default configuration values
   private final static String m_defaultTokenLifetimeValue = "3600";  // Seconds
   private final static String m_defaultLifetimeShorterValue = "5";   // Seconds
   private final static String m_defaultIdentityTokenTypeValue = "CasaIdentityToken";

   // Minimum configuration values
   private final static int m_minimumTokenLifetimeValue = 30;  // Seconds
   private final static int m_minimumLifetimeShorterValue = 5;   // Seconds
   private final static int m_minimumLifetimeShorterDifferential = 15;

   private final Map<String,String> m_tokenSettingsMap;

   /**
    * Constructor which sets default configuration values.
    */
   public AuthTokenConfig()
   {
      m_log.debug("Constructor()- Default");

      // Create a map to keep track of the token settings
      m_tokenSettingsMap = new HashMap<String, String>();

      // Set the default settings in our map
      m_tokenSettingsMap.put(TokenLifetime, m_defaultTokenLifetimeValue);
      m_tokenSettingsMap.put(LifetimeShorter, m_defaultLifetimeShorterValue);
      m_tokenSettingsMap.put(IdentityTokenType, m_defaultIdentityTokenTypeValue);
   }

   /**
    * Constructor.
    *
    * @param authTokenSettingsFileName Name of the authentication token settings file.
    * @throws Exception
    */
   public AuthTokenConfig(String authTokenSettingsFileName) throws Exception
   {
      m_log.debug("AuthTokenConfig()-");

      // Create a map to keep track of the token settings
      m_tokenSettingsMap = new HashMap<String, String>();

      FileInputStream inStream = null;
      try
      {
         // Get an input stream to read from the token settings file
         File f = new File(authTokenSettingsFileName);
         inStream = new FileInputStream(f);

         // Parse the file
         XMLReader xr = XMLReaderFactory.createXMLReader();
         SettingsFileSAXHandler handler = new SettingsFileSAXHandler(m_tokenSettingsMap);
         xr.setContentHandler(handler);
         xr.setErrorHandler(handler);

         InputSource source = new InputSource(inStream);
         xr.parse(source);

         // Do some sanity checking
         int tokenLifetime;
         try
         {
            tokenLifetime = Integer.valueOf(getSetting(TokenLifetime)).intValue();
            if (tokenLifetime < m_minimumTokenLifetimeValue)
            {
               m_log.debug("Constructor()- Configured token lifetime too small, defaulting to "
                           + Integer.toString(m_minimumTokenLifetimeValue) + " seconds");
               tokenLifetime = m_minimumTokenLifetimeValue;

               // Update the map with the new value for the setting
               m_tokenSettingsMap.put(TokenLifetime, Integer.toString(tokenLifetime));
            }
         }
         catch (NumberFormatException e)
         {
            m_log.debug("Constructor()- Invalid configured token lifetime value, defaulting to "
                        + Integer.toString(m_minimumTokenLifetimeValue) + " seconds");
            tokenLifetime = m_minimumTokenLifetimeValue;

            // Update the map with the new value for the setting
            m_tokenSettingsMap.put(TokenLifetime, Integer.toString(tokenLifetime));
         }

         int lifetimeShorter;
         try
         {
            lifetimeShorter = Integer.valueOf(getSetting(LifetimeShorter)).intValue();
            if (lifetimeShorter < m_minimumLifetimeShorterValue)
            {
               m_log.debug("Constructor()- Configured lifetime shorter too small, defaulting to "
                           + Integer.toString(m_minimumLifetimeShorterValue) + " seconds");
               lifetimeShorter = m_minimumLifetimeShorterValue;

               // Update the map with the new value for the setting
               m_tokenSettingsMap.put(LifetimeShorter, Integer.toString(lifetimeShorter));
            }
         }
         catch (NumberFormatException e)
         {
            m_log.debug("Constructor()- Invalid configured lifetime shorter value, defaulting to "
                        + Integer.toString(m_minimumLifetimeShorterValue) + " seconds");
            lifetimeShorter = m_minimumLifetimeShorterValue;

            // Update the map with the new value for the setting
            m_tokenSettingsMap.put(LifetimeShorter, Integer.toString(lifetimeShorter));
         }

         if (lifetimeShorter > tokenLifetime
             || (tokenLifetime - lifetimeShorter) < m_minimumLifetimeShorterDifferential)
         {
            m_log.debug("Constructor()- Invalid lifetime shorter value, defaulting to "
                        + Integer.toString(m_minimumLifetimeShorterValue) + " seconds");

            // Update the map with the new value for the setting
            m_tokenSettingsMap.put(LifetimeShorter, Integer.toString(m_minimumLifetimeShorterValue));
         }
      }
      catch (SAXException e)
      {
        m_log.warn("Constructor()- " + authTokenSettingsFileName + " format error, exception: " + e.toString());
        throw new Exception("AuthTokenConfig()- authtoken.settings format error", e);
      }
      catch (SecurityException e)
      {
         m_log.warn("Constructor()- SecurityException accessing " + authTokenSettingsFileName + " Exception=" + e.toString());
         throw new Exception("AuthTokenConfig()- Not able to access file", e);
      }
      catch (FileNotFoundException e)
      {
         m_log.debug("Constructor()- File " + authTokenSettingsFileName + " not found");
         throw new Exception("AuthTokenConfig()- File not found", e);
      }
      catch (IOException e)
      {
         m_log.warn("Constructor()- IOException accessing " + authTokenSettingsFileName + " Exception=" + e.toString());
         throw new Exception("AuthTokenConfig()- Read error", e);
      }
      finally
      {
         if (inStream != null)
         {
            try
            {
               inStream.close();
            }
            catch (IOException e)
            {
               // Do nothing
            }
         }
      }
   }

   /**
    * Returns the value associated with the specified setting.
    *
    * @param settingName   Name of the setting.
    * @return              Value of the specified setting.
    */
   public final String getSetting(String settingName)
   {
      // Try to find the setting in our map
      String value = m_tokenSettingsMap.get(settingName);
      if (value == null)
      {
         m_log.debug("getSetting()- Did not find setting " + settingName);

         // The setting is not in our map, check if it is one to
         // which we have defaults.
         if (settingName.equalsIgnoreCase(TokenLifetime))
         {
            value = m_defaultTokenLifetimeValue;
            m_log.debug("getSetting()- Assigning default value " + value);

            // Add the key to the map so that it can be found quicker next time
            m_tokenSettingsMap.put(TokenLifetime, m_defaultTokenLifetimeValue);
         }
         else if (settingName.equalsIgnoreCase(LifetimeShorter))
         {
            value = m_defaultLifetimeShorterValue;
            m_log.debug("getSetting()- Assigning default value " + value);

            // Add the key to the map so that it can be found quicker next time
            m_tokenSettingsMap.put(LifetimeShorter, m_defaultLifetimeShorterValue);
         }
         else if (settingName.equalsIgnoreCase(IdentityTokenType))
         {
            value = m_defaultLifetimeShorterValue;
            m_log.debug("getSetting()- Assigning default value " + value);

            // Add the key to the map so that it can be found quicker next time
            m_tokenSettingsMap.put(IdentityTokenType, m_defaultIdentityTokenTypeValue);
         }
      }
      else
      {
         m_log.debug("getSetting()- Found setting " + settingName);
         m_log.debug("getSetting()- Setting value = " + value);
      }

      return value;
   }
}