/* 
 * Copyright 2012 by AVM GmbH <info@avm.de>
 *
 * This software contains free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License ("License") as 
 * published by the Free Software Foundation  (version 3 of the License). 
 * This software 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 copy of the 
 * License you received along with this software for more details.
 */

package de.avm.android.tr064.net;

import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import de.avm.android.tr064.exceptions.SslCertificateException;

import android.util.Log;

/**
 * Trust manager for accepting self signed certificates
 */
public class SoapTrustManager implements X509TrustManager
{
	private static final String TAG = "SoapTrustManager";
	private X509TrustManager mTrustManager = null;
	
	public SoapTrustManager(KeyStore keyStore)
    		throws NoSuchAlgorithmException, KeyStoreException
    {
        TrustManagerFactory factory =
        	TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        factory.init(keyStore);
        TrustManager[] trustManagers = factory.getTrustManagers();
        if (trustManagers.length == 0)
            throw new NoSuchAlgorithmException("No trust manager available.");
        mTrustManager = (X509TrustManager)trustManagers[0];
    }

	public void checkClientTrusted(X509Certificate[] chain, String authType)
			throws CertificateException
	{
		Log.d(TAG, "checkClientTrusted() - authType == " + authType);
		mTrustManager.checkClientTrusted(chain, authType);
	}

	public void checkServerTrusted(X509Certificate[] chain, String authType)
			throws CertificateException
	{
		Log.d(TAG, "checkServerTrusted() - authType == " + authType);
		
		// trusted?
		CertificateException certExp = null;
		try
		{
        	mTrustManager.checkServerTrusted(chain, authType);
    		Log.d(TAG, "checkServerTrusted: trusted");
        	return;
		}
		catch(CertificateException e)
		{
			if (chain == null) throw e;
			certExp = e;
		}
		catch(Throwable e)
		{
			certExp = new CertificateException(
					"Failed to check certificate by default trust manager.", e);
			certExp.printStackTrace();
			if (chain == null) throw certExp;
		}
		
		try
		{
       	 	// not trusted by trustworthy CA -> must be valid an self signed at least
			chain[0].checkValidity();					// certificate valid?
			chain[0].verify(chain[0].getPublicKey());	// certificate self signed?
		}
		catch(Throwable e)
		{
    		Log.d(TAG, "checkServerTrusted: invalid or not self signed (" +
    				e.getClass().getName() + ")");
			throw certExp; 
		}

		// TODO reactivate certificate validation by user (now accept valid and self signed)
//		// deliver untrusted chain with exception to request user consent
//		Log.d(TAG, "checkServerTrusted: untrusted but valid and self signed");
//		throw new SslCertificateException(chain, certExp);
	}
	
	public X509Certificate[] getAcceptedIssuers()
	{
        return mTrustManager.getAcceptedIssuers();
	}
}
