/////////////////////////////////////////////////////////////////////////
//
// © Institute for Software Science, University of Vienna, 2004
//
// Copyright in this software belongs to Institute for Software Science, 
// University of Vienna, Nordbergstrasse 15/C/3, 1090 Vienna, Austria
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
//	Created By :		Nikolay Terziev
//	Created Date :		2004/02/10
//	Created for Project:	GEMSS
//
/////////////////////////////////////////////////////////////////////////
//
// Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//	Last commit info:	$Author: niki $
//					$Date: 2004/29/06 15:20:12 $
//					$Revision: 1.4 $
//
/////////////////////////////////////////////////////////////////////////
package at.ac.univie.iss.discovery.ssl; 

// import com.sun.net.ssl.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;

import javax.net.SocketFactory;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.HttpsURLConnection;
import java.util.Properties;
import java.util.Hashtable;

import java.util.logging.Logger;
import java.util.logging.Level;

import org.apache.axis.components.net.JSSESocketFactory;


public class DiscoverySSLSocketFactory extends JSSESocketFactory {

    private SSLSocketFactory factory;
	
    private static Logger logger = Logger.getLogger(
            DiscoverySSLSocketFactory.class.getName());
    
    private final String discoverySecurityProperties = new String(
            "discovery.security.properties");
    
    private String keystoreFile = null; 
    private String keystorePwd = null; 
    private String keystoreType = null;
    private String keyAlias = null; 
    private String keyAliasPassword = null;
	
    private String truststoreFile = null; 
    private String truststorePwd = null; 
    private String truststoreType = null;

    public DiscoverySSLSocketFactory(Hashtable attributes) {
        super(attributes); 
        
        init();
		
        try {
            if ((keystoreFile == null) || (keystorePwd == null)     || (keystoreType == null)  ||
                (keyAlias == null)     || (keyAliasPassword == null)|| (truststoreFile == null)||
                (truststorePwd == null)|| (truststoreType == null)){
                 
                 throw new Exception ("One or more  properties in the " + discoverySecurityProperties +
                      " file are not specified ");
             }
            
            SSLContext sslContext = SSLContext.getInstance("SSL");
			
            KeyStore keyStore = initKeyStore(keystoreType, keystoreFile,
                    keystorePwd);
			
            KeyStore trustStore = initKeyStore(truststoreType, truststoreFile,
                    truststorePwd);
			
            KeyManager keyManagers[] = getKeyManagers(keyStore);
			
            TrustManager trustManagers[] = getTrustManagers(trustStore);
			
            sslContext.init(keyManagers, trustManagers, new SecureRandom());
			
            factory = (SSLSocketFactory) sslContext.getSocketFactory();
        } catch (Exception e) {
            logger.log(java.util.logging.Level.SEVERE, e.toString()); 
        }
    }

    /**
     * Initialize the SSLSocketFactory
     * @throws IOException
     */ 
    protected void initFactory() throws IOException {
        sslFactory = (SSLSocketFactory) (factory);
    }

    private KeyStore initKeyStore(String type, String file, String password) throws IOException, GeneralSecurityException {

        KeyStore ks = KeyStore.getInstance(type);

        BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream(file));

        ks.load(bis, new String(password).toCharArray());
        bis.close();

        return ks;
    }	
	
    private KeyManager[] getKeyManagers(KeyStore ks) throws GeneralSecurityException {

        String defaultAlg = KeyManagerFactory.getDefaultAlgorithm();
        KeyManagerFactory kmFact = KeyManagerFactory.getInstance(defaultAlg);

        kmFact.init(ks, keyAliasPassword.toCharArray());

        KeyManager[] kms = kmFact.getKeyManagers();

        return kms;
    }

    private TrustManager[] getTrustManagers(KeyStore ks) throws GeneralSecurityException {

        String defaultAlg = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmFact = TrustManagerFactory.getInstance(defaultAlg);

        tmFact.init(ks);

        TrustManager[] tms = tmFact.getTrustManagers();

        return tms;
    }
    
    private void init() {
       
        Properties prop = new Properties();
        
        try {
            
            String propertiesFile = System.getProperty("gemss.discovery.config")
                    + File.separatorChar + discoverySecurityProperties;
			
            logger.log(java.util.logging.Level.FINE,
                    "System property (gemss.discovery.config): "
                    + propertiesFile);

            if (propertiesFile == null) {
				
                // properties file given by constant (wihtout gemss.home)
				
                propertiesFile = new String(
                        "config" + File.separatorChar
                        + discoverySecurityProperties);
                logger.log(java.util.logging.Level.FINE,
                        "Trying properties file: " + propertiesFile);
				
                if (!new File(propertiesFile).exists()) {
				
                    // properties file given by constant (wihtout gemss.home)
	
                    propertiesFile = new String(
                            System.getProperty("gemss.home")
                                    + File.separatorChar + "config"
                                    + File.separatorChar
                                    + discoverySecurityProperties);
                    logger.log(java.util.logging.Level.FINE,
                            "Trying properties file: " + propertiesFile);
					
                    if (!new File(propertiesFile).exists()) {
	
                        // if properties file is away ?!? -> throw exception			
	
                        throw new java.io.IOException(
                                "properties file missing/not found/whatever!");
                    } else {
                        logger.log(java.util.logging.Level.CONFIG,
                                "properties file (" + propertiesFile
                                + ") found!");
                    }					
                } else {
                    logger.log(java.util.logging.Level.CONFIG,
                            "properties file (" + propertiesFile + ") found!");
                }			
            } else {
				
                // if properties file given by system property (relative to exec dir or absolute)

                if (!new File(propertiesFile).exists()) {
					
                    // if endpoint file	given by system property (relative to gemss home dir)

                    propertiesFile = new String(
                            System.getProperty("gemss.home")
                                    + File.separatorChar + "config"
                                    + File.separatorChar
                                    + discoverySecurityProperties);
                    logger.log(java.util.logging.Level.FINE,
                            "Trying properties file: " + propertiesFile);
					
                    if (!new File(propertiesFile).exists()) {
				
                        // if properties file is away ?!? -> throw exception			
	
                        throw new java.io.IOException(
                                "properties file given by system property ("
                                        + propertiesFile
                                        + ") missing/not found/whatever!");
                    } else {
                        logger.log(java.util.logging.Level.CONFIG,
                                "properties file (" + propertiesFile
                                + ") found!");
                    }
                } else {
                    logger.log(java.util.logging.Level.CONFIG,
                            "properties file (" + propertiesFile + ") found!");
                }
            }

            prop.load(new java.io.FileInputStream(propertiesFile));
            
            java.util.Enumeration en = prop.elements(); 
                       
            keystoreType = prop.getProperty("ssl.keyStoreType");
            keystoreFile = prop.getProperty("ssl.keyStore");
            keystorePwd = prop.getProperty("ssl.keyStorePassword"); 
            keyAlias = prop.getProperty("ssl.keyAlias");
            keyAliasPassword = prop.getProperty("ssl.keyPassword");
            
            truststoreType = prop.getProperty("ssl.trustStoreType");
            truststoreFile = prop.getProperty("ssl.trustStore");
            truststorePwd = prop.getProperty("ssl.trustStorePassword"); 
            
            
            if (keystoreType == null){
              throw new Exception ("The ssl.keyStore property in the " + discoverySecurityProperties +
                      " file is not specified ");
            } 
            if (keystoreFile == null){
              throw new Exception ("The ssl.keyStorePassword property in the " + discoverySecurityProperties +
                      " file is not specified ");
            } 
            if (keystorePwd == null){
              throw new Exception ("The ssl.keyStoreType property in the " + discoverySecurityProperties +
                      " file is not specified ");
            } 
            if (keyAlias == null){
              throw new Exception ("The ssl.keyAlias property in the " + discoverySecurityProperties +
                      " file is not specified ");
            } 
            if (keyAliasPassword == null){
              throw new Exception ("The ssl.keyPassword property in the " + discoverySecurityProperties +
                      " file is not specified ");
            } 
            if (truststoreType == null){
              throw new Exception ("The ssl.trustStoreType property in the " + discoverySecurityProperties +
                      " file is not specified ");
            } 
            if (truststoreFile == null){
              throw new Exception ("The ssl.trustStore property in the " + discoverySecurityProperties +
                      " file is not specified ");
            } 
            if (truststorePwd == null){
              throw new Exception ("The ssl.trustStorePassword property in the " + discoverySecurityProperties +
                      " file is not specified ");
            } 
        }catch (java.io.IOException ioe) { 
            
            logger.log(java.util.logging.Level.SEVERE, ioe.toString()); 
        }catch (Exception ex) { 
            
            logger.log(java.util.logging.Level.SEVERE, ex.toString()); 
        }
    
    }
   
}

