/* ======================================================================
   Parts Copyright 2006 University of Leeds, Oxford University, University of the Highlands and Islands.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

====================================================================== */

package org.bodington.server;

import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.bodington.database.PrimaryKey;
import org.bodington.server.realm.AuthenticationException;
import org.bodington.server.realm.Authenticator;
import org.bodington.server.realm.User;
import org.bodington.server.realm.UserX509;
import org.bodington.server.resources.Resource;
import org.bodington.server.resources.ResourceSummary;
import org.bodington.server.resources.ResourceTree;
import org.bodington.server.resources.ResourceTreeManager;
import org.bodington.text.Metadatum;

/**
 * This class has the idea if a session has been authenticated.
 * @author Jon Maber
 */
public class NavigationSessionImpl extends ReusableResourceSession implements NavigationSession 
	{
    
    private static Logger log = Logger.getLogger(NavigationSessionImpl.class);
	Authenticator authenticator = null;
	String authenticator_alias = null;
    private boolean home_warned = false;
	    
	public NavigationSessionImpl()
		{
		super();
		}
    
	private Authenticator getAuthenticator( String alias )
	throws BuildingServerException
	{
	    authenticator = null;
	    String property_name = "authenticator.alias." + alias;
	    String class_name = BuildingContext.getProperty( property_name );
	    
	    if ( class_name == null )
		throw new BuildingServerException( "Unknown authenticator alias." );
	    
	    try
	    {
		Class authenticator_class;
		authenticator_class = Class.forName( class_name );
		authenticator = (Authenticator)authenticator_class.newInstance();
		authenticator_alias = alias;
		return authenticator;
		
	    }
	    catch ( ClassNotFoundException cnfe )
	    {
		authenticator = null;
		log.error( cnfe.getMessage(), cnfe );
		throw new BuildingServerException( "Unable to find the authentication code." );
	    }
	    catch ( InstantiationException ie )
	    {
		authenticator = null;
		log.error( ie.getMessage(), ie );
		throw new BuildingServerException( "Unable to create an authentication object." );
	    }
	    catch ( IllegalAccessException iae )
	    {
		authenticator = null;
		log.error( iae.getMessage(), iae );
		throw new BuildingServerException( "Unable to create an authentication object." );
	    }
	}
		
	public void clearAuthenticationCredentials()
		throws BuildingServerException
	{
	    authenticator = null;
	}
	
	public void setAuthenticationCredentials( Properties credentials, String alias )
		throws BuildingServerException
	{
	    try
	    {
		getAuthenticator( alias );
		authenticator.setAuthenticationCredentials( credentials );
	    }
	    catch ( AuthenticationException authe )
	    {
		authenticator = null;
		authenticator_alias = null;
		log.error( authe.getMessage(), authe );
		throw new BuildingServerException( "Technical problem authenticating credentials." );
	    }
	    
	}

        public void setClientX509( X509Certificate cert )
        throws BuildingServerException
        {
	    try
	    {
		InternalX509Authenticator int_authenticator = new InternalX509Authenticator();
		authenticator = int_authenticator;
                authenticator_alias = "_internal_x509_authenticator";
                int_authenticator.setClientX509( cert );
	    }
	    catch ( AuthenticationException authe )
	    {
		authenticator = null;
		authenticator_alias = null;
		log.error( authe.getMessage(), authe );
		throw new BuildingServerException( "Technical problem authenticating credentials." );
	    }
            
        }
	
	public boolean isAuthenticated()
		throws BuildingServerException
	{
	    try
	    {
		if ( authenticator == null )
		    return false;
		return authenticator.isAuthenticated();
	    }
	    catch ( AuthenticationException authe )
	    {
		log.error( authe.getMessage(), authe );
		throw new BuildingServerException( "Technical problem authenticating credentials." );
	    }
	}

	public boolean isAnonymous()
		throws BuildingServerException
	{
	    try
	    {
		if ( authenticator == null )
		    return false;
		return authenticator.isAnonymous();
	    }
	    catch ( AuthenticationException authe )
	    {		
		log.error(authe.getMessage(), authe );
		throw new BuildingServerException( "Technical problem authenticating credentials." );
	    }
	}
	
	public String getAuthenticationError()
		throws BuildingServerException
	{
	    try
	    {
		if ( authenticator == null )
		    return null;
		return authenticator.getAuthenticationError();
	    }
	    catch ( AuthenticationException authe )
	    {
		log.error( authe.getMessage(), authe );
		throw new BuildingServerException( "Technical problem authenticating credentials." );
	    }
	}
	
	public PrimaryKey getAuthenticatedUserId()
		throws BuildingServerException
	{
	    try
	    {
		if ( authenticator == null )
		    return null;
		return authenticator.getAuthenticatedUserId();
	    }
	    catch ( AuthenticationException authe )
	    {
		log.error( authe.getMessage(), authe );
		throw new BuildingServerException( "Technical problem authenticating credentials." );
	    }
	}
	
	public User getAuthenticatedUser()
		throws BuildingServerException
	{
	    PrimaryKey user_id = getAuthenticatedUserId();
	    return User.findUser( user_id );
	}
	
	public String getAuthenticatorAlias()
		throws BuildingServerException
	{
	    return authenticator_alias;
	}

	public synchronized String currentUserName()
		throws BuildingServerException
		{
			User user = getAuthenticatedUser();
			if ( user == null )
				return "null";
			return user.getName();	
		}
		
	public synchronized ResourceSummary getRootSummary()
		throws BuildingServerException
		{
			ResourceTree tree = ResourceTreeManager.getInstance();
			Resource r =  tree.findRootResource();
			if ( r == null )
				return null;
				
			ResourceSummary summary;
			
			summary = new ResourceSummary();
			summary.setResourceId( r.getResourceId() );
			summary.setHttpFacilityNo( r.getHttpFacilityNo() );
			summary.setName( r.getName() );
			summary.setTitle( r.getTitle() );
			summary.setDescription( r.getDescription() );
			summary.setIntroduction( r.getIntroduction() );
			
			return summary;
		}

		
	public synchronized Vector childSummaries( PrimaryKey r_id )
		throws BuildingServerException
		{
			
			Vector list = new Vector( 10, 10 );

			if ( r_id == null )
				throw new BuildingServerException( "No resource has been selected - can't supply any information." );
			Resource resource = Resource.findResource( r_id );
			if ( resource == null )
				throw new BuildingServerException( "Specified resource couldn't be found." );
				
			
			Enumeration enumeration = resource.findChildren();
			Resource r;
			ResourceSummary summary;
			while ( enumeration.hasMoreElements() )
				{
				r = (Resource)enumeration.nextElement();
				summary = new ResourceSummary();
				summary.setResourceId( r.getResourceId() );
				summary.setHttpFacilityNo( r.getHttpFacilityNo() );
				summary.setName( r.getName() );
				summary.setTitle( r.getTitle() );
				summary.setDescription( r.getDescription() );
				summary.setIntroduction( r.getIntroduction() );
				list.addElement( summary );
				}
			
			return list;

		}
		
		
	public Resource findResource( PrimaryKey resource_id )
		throws BuildingServerException
	    {
		if ( resource_id == null )
		    return null;
		return Resource.findResource( resource_id );
	    }
	    
	public PrimaryKey findResourceId( String[] names )
		throws BuildingServerException
	    {
		ResourceTree tree = ResourceTreeManager.getInstance();
		Resource r;
		if ( names==null || names.length == 0 )
		    r = tree.findResource( "/" );
		else
		    r = tree.findResource( names );
		if ( r==null )
		    return null;
		return r.getResourceId();
	    }

	    
	public String getUserPreferredStyleSheet()
		throws BuildingServerException
	{
	    User user = (User)BuildingContext.getContext().getUser();
	    String stylefile = "standard.css";
	    Metadatum metadatum = Metadatum.findMetadatumByIdAndName( user.getUserId(), "preference.stylesheet" );
	    if ( metadatum != null )
	    {
		stylefile=metadatum.getValue();
	    }
	    return stylefile;
	}
	    
	
	public String getUserProperty( String name )
		throws BuildingServerException
	{
	    String value=null;
	    User user = (User)BuildingContext.getContext().getUser();
	    Metadatum metadatum = Metadatum.findMetadatumByIdAndName( user.getUserId(), name );
	    if ( metadatum != null )
	    {
		value=metadatum.getValue();
	    }
	    return value;
	}
	
	public void setUserProperty( String name, String value )
		throws BuildingServerException
	{
	    if ( name == null )
		throw new BuildingServerException( "Null property name not allowed." );
	    
	    User user = (User)BuildingContext.getContext().getUser();
	    Metadatum metadatum = Metadatum.findMetadatumByIdAndName( user.getUserId(), name );
	    
	    if ( value == null )
	    {
		if ( metadatum != null )
		    metadatum.delete();
		return;
	    }
	    
	    if ( metadatum == null )
	    {
		metadatum = new Metadatum();
		metadatum.setObjectId( user.getUserId() );
		metadatum.setName( name );
	    }
	    metadatum.setValue( value );
	    metadatum.save();
	}
		    

        private class InternalX509Authenticator implements Authenticator
        {
            PrimaryKey authenticated_user_id = null;
            X509Certificate authenticated_cert = null;
            
            public void changeCredentials(Properties credentials) throws AuthenticationException
            {
                // ignore
            }
            
            public PrimaryKey getAuthenticatedUserId() throws AuthenticationException
            {
                return authenticated_user_id;
            }
            
            public String getAuthenticationError() throws AuthenticationException
            {
                return null;
            }
            
            public boolean isAnonymous() throws AuthenticationException
            {
                return false;
            }
            
            public boolean isAuthenticated() throws AuthenticationException
            {
                return authenticated_user_id != null;
            }
            
            public void setAuthenticationCredentials(Properties credentials) throws AuthenticationException
            {
                // ignore
            }
            
            public void setClientX509( X509Certificate cert )
            throws BuildingServerException, AuthenticationException
            {
                java.security.cert.X509Certificate stored_cert;
                int serial = cert.getSerialNumber().intValue();
                String dn = cert.getSubjectDN().getName();
                UserX509 user_x509;
                
                // look for certificates with same serial number
                // and DN
                Enumeration enumeration = UserX509.findUserX509s( serial, dn );
                while ( enumeration.hasMoreElements() )
                {
                    // for each certificate with same serial and dn see if it
                    // exactly matches the one the user presented.
                    user_x509 = (UserX509)enumeration.nextElement();
                    stored_cert = user_x509.getX509Certificate();
                    if ( cert.equals( stored_cert ) )
                    {
                        authenticated_user_id = user_x509.getUserId();
                        authenticated_cert = cert;
                        return;
                    }
                }

            }
        }


        public HomeContainerSession getHomeContainerSession() throws BuildingServerException
        {
            Resource resource = ResourceTreeManager.getInstance().findResource(new String[]{"users"});
            if (resource == null)
            {
                if (! home_warned )
                {
                    log.warn("Couldn't find users space for: "+ currentUserName());
                    home_warned = true;
                }
                return null;
            }
            return (HomeContainerSession) BuildingSessionManagerImpl.getSession(resource);
        }
        
        
	}


