/*
 ************************************************************************
 *
 * Copyright (c) 2003-2004, C&C Research Laboratories, NEC Europe Ltd.
 *
 * Copyright in this software belongs to C&C Research Laboratories,
 * Rathausallee 10, 53757 Sankt Augustin, Germany.
 *
 * 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 :           G.A. Kohring
 *  Created for Project :  GEMSS (IST-2001-37153)
 *
 ************************************************************************
 *
 */

package de.nece.ccrle.docmonitor;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLSocketFactory;


import de.nece.ccrle.security.BasicAuthenticator;
import de.nece.ccrle.security.NaiveSSLClient;
import de.nece.ccrle.security.SSLClient;
import de.nece.ccrle.security.UserStoreSSLClient;

/**
 * A class for retrieving remote documents. This class is capable of 
 * retrieving documents  on sites protected by standard HTTP authentication
 * schemes.
 *
 * @author Greg Kohring
 */
public class DocRetriever
{
    private String sslTrustLevel = null;
    private String keyStore = null;
    private String keyStorePassword = null;
    private String keyStoreType = null;

    /**
     * Creates a new document retriever.
     *
     */
    public DocRetriever( String sslTrustLevel, String keyStore, 
                        String keyStoreType, String keyStorePassword ) {
        this.sslTrustLevel = sslTrustLevel;
        this.keyStore = keyStore;
        this.keyStorePassword = keyStorePassword;
        this.keyStoreType = keyStoreType;
    }

    /**
     * Retrieves the specified document and writes it to the specified 
     * output stream.
     *
     * @param docAtt the <code>DocAttributes</code> describe the document
     *      along with any information necessary to access it.
     * @param os the <code>OutputStream</code> to which the document should
     *      be written.
     * @throws IOException if an I/O error occurs.
     * @throws UnsupportedAuthSchemeException if the required authorization
     *      scheme is not supported.
     */
    public boolean retrieve( DocAttributes docAtt, OutputStream os ) 
                throws IOException, UnsupportedAuthSchemeException {

            return retrieve( docAtt, os, -1 );
    }

    /**
     * Retrieves the specified document and writes it to the specified 
     * output stream, provided the document has been changed more recently
     * than the specified time.
     *
     * @param docAtt the <code>DocAttributes</code> describe the document
     *      along with any information necessary to access it.
     * @param os the <code>OutputStream</code> to which the document should
     *      be written.
     * @param time a <code>long</code> representing a time value afterwhich
     *      a modified document should be retrieved. In other words, if the
     *      specified document was changed more recently than
     *      <code>time</code> it will be downloaded to the specified stream,
     *      otherwise it will not.
     * @throws IOException if an I/O error occurs.
     * @throws UnsupportedAuthSchemeException if the required authorization
     *      scheme is not supported.
     */
    public boolean retrieve( DocAttributes docAtt, OutputStream os, long time ) 
                throws IOException, UnsupportedAuthSchemeException {

        //Set-up basic authentication if necessary

        String authScheme = docAtt.getAuthScheme();

        if ( authScheme.equals( "BasicAuthentication" ) ){

            BasicAuthenticator ba = new BasicAuthenticator( 
                    docAtt.getUser(), docAtt.getPassword() );
            Authenticator.setDefault( ba );

            URLConnection.setDefaultAllowUserInteraction( true );
        } else if ( !authScheme.equals( "none" ) ){
            throw new UnsupportedAuthSchemeException( 
                                        " Scheme: " + authScheme );
        }

        //Create a connector for the remote URL

        URLConnection urlConnection = null;


        try {
            urlConnection = docAtt.getURL().openConnection();
            urlConnection.connect();
        } catch ( IOException ioe ){
            if ( ioe.getMessage().indexOf( "No trusted certificate" ) == -1 ){
                throw new IOException( ioe.getLocalizedMessage() );
            } else {
                //If the protocol is HTTPS and no trusted certificate was 
                //found, then we can try again depending on our
                //trust level.
                try{
                    SSLClient sslClient = null;
                    if ( sslTrustLevel.equals( "naive" ) ){
                        sslClient = new NaiveSSLClient();
                    } else if ( sslTrustLevel.equals( "userStore" ) ){
                        sslClient = new UserStoreSSLClient(
                               keyStore, keyStoreType, keyStorePassword );
                    } else {
                        throw new IOException( ioe.getLocalizedMessage());
                    }

                    //((HttpsURLConnection) urlConnection).setSSLSocketFactory(
                     //              sslClient.getSSLSocketFactory() );
                    HttpsURLConnection.setDefaultSSLSocketFactory(
                                    sslClient.getSSLSocketFactory() );
            urlConnection = docAtt.getURL().openConnection();
            urlConnection.connect();
                } catch ( Exception e ){
                        throw new IOException( e.getLocalizedMessage() );
                }
            }
        }

        //If the protocol is HTTP or HTTPS, then check the response code to 
        //make sure there were no errors.

        if ( urlConnection instanceof HttpURLConnection ){
            HttpURLConnection httpConnection = 
                                    (HttpURLConnection) urlConnection;

            int responceCode = httpConnection.getResponseCode();
            if ( responceCode != 200 ){
                throw new IOException( "Error retrieving URL: " + 
                                    httpConnection.getResponseMessage() );
            }
        }

        //Get the modification date of the remote document.
        //Now download the document

        long remoteLastModified = urlConnection.getLastModified();

        if ( remoteLastModified > time ){
		    BufferedInputStream bis = new BufferedInputStream( 
                                            urlConnection.getInputStream() );

    		int b;
    		while ( ( b = bis.read() ) != -1 ) os.write( b );
        
		    bis.close();

            return true;
        }

        return false;
    }

}
