/*
 * $Id: LoginAction.java,v 1.81 2007/08/08 12:14:54 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: LoginAction.java,v $
 * Revision 1.81  2007/08/08 12:14:54  marbobo
 * + License-Funktionalität komplett umgeschrieben
 * + Bugfix bzgl. Beschreibungsfelder von Entitäten
 * + Bugfix mailing
 *
 * Revision 1.80  2007/07/20 14:18:03  marbobo
 * + Max. Anzahl von Benutzern die im System gleichzeitig angemeldet sein dürfen ist jetzt konfiguriebar
 *
 * Revision 1.79  2007/02/01 14:32:21  korowa
 * Zwei Felder zu SystemParameter hinzugefügt die einem Administrator erlauben das Anlegen von neuen Benutzer oder das Ändern der Passwörter zu regeln.
 *
 * Revision 1.78  2006/11/06 12:40:08  korowa
 * bugfixing
 *
 * Revision 1.77  2006/11/06 11:11:20  korowa
 * "License akzeptieren" als Preloginaktion eingefügt
 *
 * Revision 1.76  2006/10/04 03:14:33  marbobo
 * + Vollständige Unterstützung für Templates implementiert
 * + Alles von Virtoweb auf ProCoBa umgestellt
 *
 * Revision 1.75  2006/04/29 18:39:46  marbobo
 * + Refactoriengs und eine Ausgaben vereinheitlicht
 *
 * Revision 1.74  2006/02/27 16:16:03  marbobo
 * + Systemsichten hinzugefügt
 * + SysLinks angepasst
 * + Benutzer können falls gewünscht Dokumentqueries anlegen
 *
 * Revision 1.73  2005/12/16 11:50:17  lawern
 * *** empty log message ***
 *
 * Revision 1.72  2005/11/24 19:33:38  marbobo
 * + Methoden-Signatur angepasst
 *
 * Revision 1.71  2005/11/23 14:56:14  marbobo
 * - "dynamische Gruppen zuordnen" entfernt
 *
 * Revision 1.70  2005/09/01 08:31:59  korowa
 * Es werden mehrere URL angeboten, welche direkten Zugriff auf Dateien
 *  und automatisches Anmelden als Gast erlauben.
 *
 * Revision 1.69  2005/08/24 18:44:18  marbobo
 * + Virtoweb-Start korrigiert
 *
 * Revision 1.68  2005/03/16 15:48:47  jonyp
 * getLastnameLabel for user tracking infos
 *
 * Revision 1.67  2005/03/16 15:10:13  jonyp
 * *** empty log message ***
 *
 * Revision 1.66  2005/02/20 17:00:12  marbobo
 * + Eingegebner Benutzername in Datenbankkonformer Form bringen
 *
 * Revision 1.65  2004/11/10 15:02:45  koppy
 * Projektweite Überarbeitung von Error-Logs, Stacktraces,..
 *
 * Revision 1.64  2004/11/01 15:56:31  vchris
 * + Erweiterung für LDAP Support:
 *   Verhalten der LDAP-Felder auf der Login.jsp angepasst
 *
 * Revision 1.63  2004/10/28 19:43:17  marbobo
 * + Ein bischen aufgeräumt
 * + Anpassung wegen Loginseite
 *
 * Revision 1.61  2004/10/22 15:28:44  jonyp
 * added support for ldap user
 *
 * Revision 1.60  2004/10/18 08:29:15  marbobo
 * + Bugfix: Syslinkparameters auch bei falsch eingabe der Benutzerdaten merken
 *
 * Revision 1.59  2004/10/15 15:17:16  jonyp
 * added comments for future use of ldap things
 *
 * Revision 1.1  2002/10/22 17:52:00  koppy
 * Initiale Version
 *
 */
package vkc.view.action;

import org.apache.struts.Globals;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;


import vkc.db.entities.VKCLanguage;
import vkc.db.entities.VKCLicense;
import vkc.db.entities.VKCUser;
import vkc.db.entities.VKCCategory;
import vkc.db.helper.UserHelper;
import vkc.db.serializer.VKCLanguageSerializer;
import vkc.db.serializer.VKCLicenseSerializer;
import vkc.db.serializer.VKCUserSerializer;
import vkc.db.serializer.VKCSysParameterSerializer;
import vkc.exception.DisabledUserAccountException;
import vkc.exception.MaxAllowedUsersExceededException;
import vkc.exception.PasswordMismatchException;
import vkc.exception.UnknownUserException;
import vkc.exception.DBException;
import vkc.exception.UserAccountNotEnabledException;
import vkc.misc.VKCLogManager;
import vkc.util.VKCStringUtils;
import vkc.view.PageId;
import vkc.view.WebShare;
import vkc.view.bean.UserTrackingInfo;
import vkc.view.form.LoginForm;
import vkc.view.form.SysLinkForm;

import vkc.ldap.db.helper.LdapUserHelper;
import vkc.ldap.VKCLdapUser;
import vkc.ldap.db.entities.VKCLdapServer;

import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;

import javax.naming.NamingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Klasse für die Kontrolle des Einlogvorgangs
 *
 * @author koppy
 */
public final class LoginAction extends Action {
    private static Object wrongAuthenthication = new Object();

    /**
     * Process the specified HTTP request, and create the corresponding HTTP
     * response (or forward to another web component that will create it).
     * Return an <code>ActionForward</code> instance describing where and how
     * control should be forwarded, or <code>null</code> if the response has
     * already been completed.
     *
     * @param mapping The ActionMapping used to select this instance
     * @param actionForm The optional ActionForm bean for this request (if any)
     * @param request The HTTP request we are processing
     * @param response The HTTP response we are creating
     *
     * @exception Exception if business logic throws an exception
     */
    public ActionForward execute(ActionMapping mapping, 
            					 ActionForm f,
            					 HttpServletRequest request, 
            					 HttpServletResponse response) 
    throws Exception {
        LoginForm form = (LoginForm) f;

        if (request.getParameter("action.login") != null) {
            return handleLoginAction(form, request);
        } else if (request.getParameter("action.change.ldapserver") != null) {
            return handleChangeLdapServerAction(form, request);
        }
        
        if (request.getLocale() == null) {
            VKCLanguage lang = VKCLanguageSerializer.getDefaultLanguage();
            setLocale(request, lang.getLocale());
        }

        return new ActionForward(WebShare.getLoginPage(), false);
	}
    
    private ActionForward handleChangeLdapServerAction(LoginForm form, HttpServletRequest request) throws DBException {
        // if ldapenabled und changed request, neues ldap obj laden und dann
        // die felder entsprechend setzen.
        if (form.getUseLdap()) {
			HashMap ldapServers = form.getLdapServers();
			
			if (ldapServers.size() > 0) {
				if (form.getSelectedLdapServerId() == null) {
					VKCLdapServer[] ldapServerArray = (VKCLdapServer[])(ldapServers.values().toArray(new VKCLdapServer[0]));
					form.setSelectedLdapServerId(ldapServerArray[0].getLdapServerId());
				}
				
				VKCLdapServer selectedServer = (VKCLdapServer) ldapServers.get(form.getSelectedLdapServerId());
				form.setLdapServerBaseDN(selectedServer.getBaseDN());
			}
        }

		return new ActionForward(WebShare.getLoginPage(), false);
    }

    private ActionForward handleLoginAction(LoginForm form, HttpServletRequest request) throws DBException {
        // Validate the request parameters specified by the user
        ActionErrors errors = new ActionErrors();
        HttpSession session = request.getSession();
        String username = VKCStringUtils.filterUnicodeChars(form.getUsername().trim());
        String password = form.getPassword().trim();
        VKCUser user = null;	        
		
		try {
		    if (form.getUseLdap()) {
		        // user is authenticated against an external server instance
		        // returns instance of VKCLdapUser
		        user = LdapUserHelper.authenticate(username, password, form.getSelectedLdapServerId());	        
		    } else {
                // user is authenticated internally
		        // returns instance of VKCUser
		        user = UserHelper.authenticate(username, password, request);
		    }	    
		} catch (UnknownUserException e) {
            errors.add("username", new ActionError("login.user.unknown"));
            session.setAttribute(Globals.ERROR_KEY, errors);
			delayResponse();
            
			return new ActionForward(WebShare.getLoginPage(), false);
        } catch (DisabledUserAccountException e) {
            errors.add("username", new ActionError("error.account.disabled"));
            session.setAttribute(Globals.ERROR_KEY, errors);
			delayResponse();
            
			return new ActionForward(WebShare.getLoginPage(), false);
        } catch (UserAccountNotEnabledException e) {
            errors.add("username", new ActionError("error.account.notenabled",e.getMessage()));
            session.setAttribute(Globals.ERROR_KEY, errors);
            delayResponse();
            
            return new ActionForward(WebShare.getLoginPage(), false);
        } catch (PasswordMismatchException e) {
            if (VKCSysParameterSerializer.retrieveIntValue("LOGIN", "UserLockEnabled", 1) != 0) {
                errors.add("password", new ActionError("error.password.mismatchlocked",e.getMessage()));
            }else errors.add("password", new ActionError("error.password.mismatch"));
            session.setAttribute(Globals.ERROR_KEY, errors);
			delayResponse();
            
			return new ActionForward(WebShare.getLoginPage(), false);
        } catch (NamingException e) {
            errors.add("general", new ActionError("error.general", e.getMessage()));
            session.setAttribute(Globals.ERROR_KEY, errors);
			delayResponse();
            
			return new ActionForward(WebShare.getLoginPage(), false);
        } catch (UnsupportedEncodingException e) {
            errors.add("general", new ActionError("error.general", e.getMessage()));
            session.setAttribute(Globals.ERROR_KEY, errors);
			delayResponse();
            
			return new ActionForward(WebShare.getLoginPage(), false);
        } catch (NoSuchAlgorithmException e) {
            errors.add("general", new ActionError("error.general", e.getMessage()));
            session.setAttribute(Globals.ERROR_KEY, errors);
			delayResponse();
            
			return new ActionForward(WebShare.getLoginPage(), false);
        } catch (MaxAllowedUsersExceededException e) {
            if (request.getLocale() == null) {
                VKCLanguage lang = VKCLanguageSerializer.getDefaultLanguage();
                setLocale(request, lang.getLocale());
            }

            errors.add("maxAllowedUsers", new ActionError("error.maxallowedusers.exceeded", e.getMessage()));
            session.setAttribute(Globals.ERROR_KEY, errors);
            delayResponse();
            
            return new ActionForward(WebShare.getLoginPage(), false);
        }

        VKCLicense license = VKCLicenseSerializer.retrieveActiveLicense(); 
        
        if (Integer.parseInt(WebShare.getSysParameterValue("LICENSE", "EnableLicensemanagement", "0")) != 0 &&  license != null) {
            if (!VKCLicenseSerializer.hasUserAcceptedLicense(user.getUserId(), license.getLicenseId())) {
                request.getSession().setAttribute(WebShare.TEMP_USER_KEY, user);
                
                return new ActionForward("/loginLicenseFrameset.do", false);
            }
        }

        if (!(user instanceof VKCLdapUser)) {
            // nur wenn user ein interner User ist, dann nachfolgende aktionen ausführen
        	 if (!user.isPasswordChanged()) {
	            errors.add("password.neverchanged", new ActionError("error.password.neverchanged"));
	            session.setAttribute(Globals.ERROR_KEY, errors);
	            request.getSession().setAttribute(WebShare.TEMP_USER_KEY, user);
	            
	            return new ActionForward("/loginChangePassword.do", false);
	        }
	    
       
	        
	 		//Zur aktuellen Nachrichtenseite weiterleiten
	        if (VKCSysParameterSerializer.retrieveIntValue("NEWS", "EnableNews", 0) != 0) {
		        if (user.hasUnreadNews()) {
		            request.getSession().setAttribute(WebShare.TEMP_USER_KEY, user);
		            
		            return new ActionForward("/showRecentNewsFrameset.do", false);
		        }
	        }
        }  

        // Session-Timeout setzen
        if (user.getSessionTimeout() != null && user.getSessionTimeout().intValue() != 0) {
            session.setMaxInactiveInterval(user.getSessionTimeout().intValue() * 60);
        }
        
        // Eingeloggten User in der Session speichern
        vkc.util.ServletUtils.removeSessionAttributes(session);
        Timestamp loginTime = new Timestamp(System.currentTimeMillis());
        user.setMostRecentLogin(loginTime);
        session.setAttribute(WebShare.USER_KEY, user);
        session.setAttribute(WebShare.LOCALE_KEY, user.getLanguage().getLocale());

        // Sprache wechseln
        setLocale(request, user.getLanguage().getLocale());

        String log = user.getUserDescription();
        log += " logged in (" + "userId = " +  user.getUserId();
		log += ", session timeout = " + (session.getMaxInactiveInterval() / 60) + "min)";

        // track users login time
        VKCUserSerializer.saveMostRecentLogin(user.getUserId());

        VKCLogManager.infoLog(this, log);

        UserTrackingInfo bean = new UserTrackingInfo(user.getUserId(),
                                                     user.getLastnameLabel(),
                									 request.getSession(), 
                									 request.getRemoteAddr(), 
                									 new Date(loginTime.getTime()));

        session.setAttribute(WebShare.USER_TRACKING_KEY, bean);
        session.setAttribute(WebShare.SYSTEMVIEWTYPEID_KEY, user.getSystemViewTypeId());
        
        //externer Linkaufruf
        if (form.isSysLink()) {
            return new ActionForward(form.getSysLink());
        }

        //Procoba-Start
        if (PageId.FUNCTIONAL_VIEW.equals(user.getSystemViewTypeId()) && VKCSysParameterSerializer.isProjectManagementEnabled()) {
			return new ActionForward(SysLinkForm.getProcobaFunctionalSysLink());
        }

		if (PageId.PROJECT_VIEW.equals(user.getSystemViewTypeId()) && VKCSysParameterSerializer.isProjectManagementEnabled()) {
			return new ActionForward(SysLinkForm.getProcobaProjectSysLink());
		} 

		//by default: startCategory is root
		Integer startCategoryId = new Integer(VKCCategory.ROOT_CATEGORY_ID); 
	    
		// if user has another take it...
		if (user.getHomeCategoryId() != null && user.getHomeCategoryId().intValue() > VKCCategory.ROOT_CATEGORY_ID) {
			startCategoryId = user.getHomeCategoryId();
		}
	
		return new ActionForward(SysLinkForm.getCategorySysLink(startCategoryId, SysLinkForm.LOGINTYPE_NORMAL_USER), true);
    }

    private final void delayResponse() {
		synchronized (wrongAuthenthication) {
			try {
				Thread.sleep(5000);
			} catch (InterruptedException iex) {
			}
		}
    }
}