package lu.tudor.santec.gecamed.esante.gui.webservice;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.UUID;

import lu.tudor.santec.gecamed.core.gui.GECAMedIconNames;
import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedBaseDialogImpl;
import lu.tudor.santec.gecamed.core.utils.GECAMedUtils;
import lu.tudor.santec.gecamed.esante.ejb.entity.beans.Dsp;
import lu.tudor.santec.gecamed.esante.ejb.entity.beans.ESanteProperty;
import lu.tudor.santec.gecamed.esante.ejb.session.beans.CDAManagerBean;
import lu.tudor.santec.gecamed.esante.gui.data.Configuration;
import lu.tudor.santec.gecamed.esante.gui.dialogs.ESanteDialog;
import lu.tudor.santec.gecamed.esante.gui.dialogs.LoginDialog;
import lu.tudor.santec.gecamed.esante.gui.luxtrust.AuthenticationSignature;
import lu.tudor.santec.gecamed.esante.gui.luxtrust.UserInactivityMonitor;
import lu.tudor.santec.gecamed.esante.gui.tab.ESanteTab;
import lu.tudor.santec.gecamed.esante.gui.utils.ESanteGuiUtils;
import lu.tudor.santec.gecamed.esante.utils.ESanteUtils;
import lu.tudor.santec.gecamed.esante.utils.exceptions.SendingStoppedException;
import lu.tudor.santec.gecamed.esante.utils.exceptions.SmartCardException;
import lu.tudor.santec.gecamed.patient.gui.PatientManagerModule;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringEscapeUtils;

/**
 * @author jens.ferring(at)tudor.lu
 * 
 *         Based on infomed client sources.
 * 
 * @version <br>
 *          $Log: Security.java,v $
 *          Revision 1.72  2014-02-12 12:17:02  ferring
 *          short and long eHealth IDs introduced
 *
 *          Revision 1.71  2014-02-06 14:30:18  ferring
 *          SendingStoppedException handling changed and additional WebserviceException type handled
 *
 *          Revision 1.70  2014-02-04 10:08:29  ferring
 *          eSante ID management completed
 *          Only those documents will be shown, that are retrieved by the RSQ
 *
 *          Revision 1.69  2014-01-31 16:29:43  donak
 *          Added error dialog when document cannot be uploaded due to invalid access privileges
 *          Fixed bug that prevented saml assertions from being renewed after they exceeded in cache
 *          Fixed bug that prevented documents from being uploaded (gecamed id has not been written to template due to renaming of placeholder)
 *          SMART UPLOAD (TM) feature: Upload option is added to context menu dependent on dsp access permissions and upload success probability calculations
 *          Upload support for images added
 *
 *          Revision 1.68  2014-01-31 14:57:43  ferring
 *          Don't ask for the presence password, if you already have one
 *
 *          Revision 1.67  2014-01-31 10:39:36  ferring
 *          duplicate asking for presence password removed
 *
 *          Revision 1.66  2014-01-31 10:19:02  ferring
 *          unneeded code removed
 *
 *          Revision 1.65  2014-01-30 15:43:51  donak
 *          Added function isPresencePasswordAllowed() that indicates if the user should provide a presence password
 *          Added function isAllowedToAccessOwnDocuments() that indicates if the user has access to his own documents for the current dsp
 *
 *          Revision 1.64  2014-01-30 14:20:43  donak
 *          applied valid rules for determining if user is blacklisted
 *          No presence password is asked for blacklisted user but a dsp assertion is obtained as the user still has access to his own documents
 *
 *          Revision 1.63  2014-01-30 11:58:25  ferring
 *          replace all occurrences instead of only the first
 *
 *          Revision 1.62  2014-01-29 16:57:50  donak
 *          Renamed references to HPD to eHealth in respect to the request of the eHealth Agency
 *          Removed template DSP-11-12 as it is not needed and was created only for internal testing purposes
 *
 *          Revision 1.61  2014-01-29 14:20:13  donak
 *          fixed bug that might result in a missing user assertion under rare conditions
 *
 *          Revision 1.60  2014-01-29 12:47:19  ferring
 *          update priveleges
 *
 *          Revision 1.59  2014-01-28 17:53:12  donak
 *          Practice eHealth id is now set automatically
 *          Document format is now determined automatically
 *          Text of eHealth id input dialog has been changed DSP id --> eHealth ID
 *          eHealth id support link is now obtained from db
 *
 *          Revision 1.58  2014-01-28 14:10:32  ferring
 *          App name and version taken from system properties
 *
 *          Revision 1.57  2014-01-28 13:31:09  donak
 *          Removed debug output and TODOs
 *
 *          Revision 1.56  2014-01-28 13:28:12  donak
 *          Activated DSP-SAML assertion request via user SAML token - code.
 *
 *          Revision 1.55  2014-01-28 10:20:12  donak
 *          Added another hack to bypass erroneous dsp assertion request with saml token till sqli fixed the bug in the platform software
 *          Save user info from smartcard to db for displaying it in the config panel
 *
 *          Revision 1.54  2014-01-27 13:13:47  donak
 *          * Properties are now saved in db in respect to the following contexts:
 *            - specific to a GECAMed user
 *            - specific to a GECAMed physician
 *            - specific to an eSanté plattform user
 *            - general (independend of GECAMed user, physician, eSanté platform user)
 *          * Improved authentication handling. A signed authentication request is now only done for user authentication. for dsp authentication requests the provided saml assertion is used. (authentication speed up). ATTENTION: This fix is currently disabled till a bug in the eSanté platform has been fixed.
 *          * Naming of message loggings had been adapted to the naming in the connection kit (e.g. DSP-10, DSP-22)
 *          * Changed behavior for handling of dsps for which physician has insufficient access permissions. If physician does not want to provide presence password, a DSP-11 is sent instead of a DSP-12 to allow the physician to at least access the documents he is author of.
 *
 *          Revision 1.53  2014-01-23 15:04:06  ferring
 *          logout button added
 *
 *          Revision 1.52  2014-01-22 14:39:16  ferring
 *          Login information will only be reseted, if the user has no valid SAML assertion
 *
 *          Revision 1.51  2014-01-21 06:28:24  ferring
 *          TODO removed
 *
 *          Revision 1.50  2014-01-20 12:38:44  ferring
 *          UserInactivityMonitor will be initialised when used, because it takes quite a time and it a wait-dialog needs to be shown, while doing that.
 *
 *          Revision 1.49  2014-01-17 12:01:03  ferring
 *          DSP info is refreshed, after disconnect
 *
 *          Revision 1.48  2014-01-17 11:12:52  ferring
 *          print info changed
 *
 *          Revision 1.45  2014-01-16 12:28:44  ferring
 *          Changes on eSanté platform applied and logging added
 *
 *          Revision 1.44  2014-01-13 09:31:18  ferring
 *          SmartCard error 143363 caught and shown
 *
 *          Revision 1.43  2014-01-10 13:43:12  ferring
 *          purge privileges when pulling out the luxtrust card or when
 *
 *          Revision 1.42  2014-01-10 13:17:17  ferring
 *          Mandate caching and DSP info renewing fixed
 *
 *          Revision 1.41  2014-01-10 08:22:58  ferring
 *          tranlsations added
 *
 *          Revision 1.40  2014-01-09 17:31:23  ferring
 *          Mandate will be renewed after presence password and EHR status is now visible
 *
 *          Revision 1.39  2014-01-09 16:44:33  ferring
 *          Error handling for SAML assertions changed
 *
 *          Revision 1.38  2014-01-09 14:29:49  ferring
 *          wait cursor removed after action
 *
 *          Revision 1.37  2014-01-08 17:46:20  donak
 *          Improved card reader feedback delay when inserting smartcard
 *          Solved issue with storing UserAssertion in table DSP
 *          Added handling for closed/deleted dsps and blacklisted users
 *          Created class DspPrivileges for convenient handling of dsp access rights. The privileges are cached and renewed with each dp saml assertion request. Cached privileges should be used for displaying dsp state and mandate on screen
 *          Fixed exception that was occasionally thrown at smartcard handling in login dialog (hopefully)
 *
 *          Revision 1.36  2014-01-07 13:19:44  donak
 *          Adding initial version of pdf preview dialog
 *          Several bug fixes
 *
 *          Revision 1.35  2014-01-06 12:32:20  ferring
 *          typing error corrected
 *
 *          Revision 1.34  2014-01-02 15:07:58  donak
 *          Implemented authentication workflow: user-saml request --> check ehr access permissions --> dsp saml request --> actual webservice request
 *          All saml assertions independent of type are cached.
 * <br>
 *          Revision 1.33 2013-12-31 17:53:53 donak <br>
 *          Very hacky quickfix - however there seems to be the wrong url provided by the agency. Have to check this next year - Happy new year everyone! <br>
 * <br>
 *          Revision 1.32 2013-12-31 17:07:38 donak <br>
 *          Fixed bug of eSanté Connection Kit specification <br>
 *          Added mechanism to request mandate before final saml assertion is requested. DOES NOT WORK COMPLETELY, YET. <br>
 * <br>
 *          Revision 1.31 2013-12-30 10:03:00 donak <br>
 *          Removed unneeded templates that have accidently been committed after cleaning up imports in the sources. <br>
 *          Changed the names of authentication templates to clarify a little more, which authentication method is used. <br>
 * <br>
 *          Revision 1.30 2013-12-27 18:08:14 donak <br>
 *          Cleanup of imports <br>
 * <br>
 *          Revision 1.29 2013-12-24 17:33:15 donak <br>
 *          Changed exception handling to focus assertion related exceptions to gerValidSamlAssertionForPatient() <br>
 *          Included exception handling for SmartCardExceptions <br>
 *          Fixed bug in UserInactivityMonitor which caused inactive sessions <br>
 *          Partially rewrote SamlAssertion to ease and centralize logic <br>
 * <br>
 *          Revision 1.28 2013-12-23 18:07:36 donak <br>
 *          Stabilized smartcard login - using now the LuxTrust API and should thus be easily extendible to signing server usage. <br>
 * <br>
 *          Revision 1.27 2013-12-17 13:23:26 ferring <br>
 *          *** empty log message *** <br>
 * <br>
 *          Revision 1.26 2013-12-17 13:22:21 ferring <br>
 *          replacing improved <br>
 * <br>
 *          Revision 1.25 2013-12-13 16:17:08 donak <br>
 *          LuxTrust authentication completed <br>
 * <br>
 *          Revision 1.24 2013-12-13 13:17:24 donak <br>
 *          Integratoin of LuxTrust authentication - so far it is disabled <br>
 * <br>
 *          Revision 1.23 2013-12-13 12:31:43 ferring <br>
 *          Exception handling changed <br>
 * <br>
 *          Revision 1.22 2013-12-11 08:12:26 ferring <br>
 *          error message handling changed <br>
 * <br>
 *          Revision 1.21 2013-12-10 11:59:27 ferring <br>
 *          webservice error handling improved <br>
 * <br>
 *          Revision 1.20 2013-12-09 17:15:37 donak <br>
 *          Bug-fixing for upload to eSanté platform <br>
 * <br>
 *          Revision 1.19 2013-11-29 09:10:52 ferring <br>
 *          first changes for eSanté test platform <br>
 * <br>
 *          Revision 1.18 2013-11-27 07:57:49 ferring <br>
 *          No Exception thrown, when connection is canceled by user <br>
 * <br>
 *          Revision 1.17 2013-11-26 08:16:23 ferring <br>
 *          Option added for temporary downloads of CDA documents <br>
 *          Property names changed <br>
 * <br>
 *          Revision 1.16 2013-11-19 17:34:21 ferring <br>
 *          MIME response data extraction changed <br>
 * <br>
 *          Revision 1.15 2013-11-18 15:36:58 ferring <br>
 *          Restructured methods with SoapSender and DocumentWorker restructured. <br>
 *          Checking the IPID, if it changes and refreshing the data. <br>
 * <br>
 *          Revision 1.14 2013-11-12 12:48:22 donak <br>
 *          Document upload: <br>
 *          * conversion to pdf/a using open office has been moved to the server. OpenOffice 4 has to be located in the jboss work directory. ATTENTION: it
 *          still has to be evaluated if the license agreement dialog occurs when instance is started the first time on the server. <br>
 *          * If document contains a description, the first forty characters of the description followed by three dots will be used as title instead of the
 *          filename <br>
 *          * Upload of incident type letters has been fixed <br>
 *          * upload for docx files has been added <br>
 * <br>
 *          Upload parameters: <br>
 *          * database does now support storage of user dependent properties <br>
 *          * The system will automatically remember the last chosen values for confidentiality, facility type, and speciality and propose them as default when
 *          the next document will be uploaded. <br>
 * <br>
 *          Inactivity Monitor: <br>
 *          * the event mouse wheel scrolling is now taken into account for resetting the logoff timer <br>
 *          * the logoff delay is now stored in the database. If the database does not contain this parameter, it will be created <br>
 * <br>
 *          General: <br>
 *          * Optimized incident entry bean handling. Caching will now avoid copying the binary content and the generated pdf content of an incident entry as
 *          these elements should only be loaded when needed. Now it should be save to re-implement a proper getBinaryContent() handling. <br>
 * <br>
 *          Revision 1.13 2013-10-29 10:04:08 donak <br>
 *          Fixed: Unlinked patient records possessed the possibility to upload documents to the DSP, which is impossible <br>
 *          Fixed: User will now be informed if client time is out of sync with the server and thus the saml assertion is denied <br>
 * <br>
 *          Revision 1.12 2013-10-24 15:14:49 ferring <br>
 *          commit tracking added <br>
 */
public class Security {
	/* ======================================== */
	// CONSTANTS
	/* ======================================== */
	

	
	
	/* ======================================== */
	// MEMBERS
	/* ======================================== */

	private static HashMap<String, SAMLAssertion> assertions = new HashMap<String, SAMLAssertion>();
	private static HashMap<String, DspPrivileges> dspPrivileges = new HashMap<String, DspPrivileges>();
	private static UserInactivityMonitor inactivityMonitor;

	/* ======================================== */
	// CLASS BODY
	/* ======================================== */

	/**
	 * Requests a general saml assertion that authenticates the user for the eSanté platform. This saml assertion is used for actions that are not specific to
	 * the content of a specific patient dsp (btw. with dsp the French acronym of EHR is meant). <br/>
	 * <br/>
	 * If there is already a cached assertion, it will be reused. Otherwise a new one is requested from the eSanté platform.<br/>
	 * The request of a new saml assertion (so called authentication request) requires user interactivity, meaning the user has to enter his credentials
	 * 
	 * @param sender
	 *            Bean containing configuration data for soap communication and template filling
	 * @param withResponseTag
	 *            Flag that indicates if the function should only return the real saml assertion (<b>false</b>) or the whole response message (<b>true</b>)
	 * @return The user saml assertion
	 * @throws SmartCardException
	 *             If a problem in relation to the smartcard occurred
	 * @throws SendingStoppedException
	 *             If the user cancelled the action
	 * @throws Exception
	 *             If it was not possible to obtain a valid saml assertion. As this might have many different reasons (e.g. invalid credentials, insufficient
	 *             permissions, no access to the server, blocked smartcard), please check the exception message for further details.
	 */
	public static String getUserSamlAssertion(SoapSender sender, boolean withResponseTag) throws SmartCardException, Exception {

		
		// try to find an valid existing user saml assertion
		SAMLAssertion assertion = getCachedAssertion(ESanteProperty.USER_ASSERTION_NAME);
		if (assertion != null) {
			return assertion.getAssertionContent(withResponseTag);
		}
//		MainFrame.getInstance().setWaitCursor(true);
		return getValidSamlAssertion(sender, withResponseTag, null);
	}

	/**
	 * Requests a saml assertion for a specific patient. If there is already a cached assertion, it will be reused. Otherwise a new one is requested from the
	 * eSanté platform.<br/>
	 * The request of a new saml assertion (so called authentication request) requires user interactivity, meaning the user has to enter his credentials and if
	 * necessary (insufficient access permissions) also the presence password of the patient
	 * 
	 * @param sender
	 *            Bean containing configuration data for soap communication and template filling
	 * @param withResponseTag
	 *            Flag that indicates if the function should only return the real saml assertion (<b>false</b>) or the whole response message (<b>true</b>)
	 * @return The saml assertion for accessing the dsp of the patient
	 * @throws SmartCardException
	 *             If a problem in relation to the smartcard occurred
	 * @throws SendingStoppedException
	 *             If the user cancelled the action
	 * @throws Exception
	 *             If it was not possible to obtain a valid saml assertion. As this might have many different reasons (e.g. invalid credentials, insufficient
	 *             permissions, no access to the server, blocked smartcard), please check the exception message for further details.
	 */
	public static String getDspSAMLAssertion(SoapSender sender, boolean withResponseTag) throws SmartCardException, SendingStoppedException, Exception {
		// obtain the dsp oid
		String dspId = sender.getDsp().getDspOid();
		if ((dspId == null)) {
			throw new Exception("Unable to obtain a SAML assertion for a specific DSP without the DSP id");
		}

		// 1. try to find an valid existing assertion related to this patient
		// but cache must only be used, if the user does not provide a presence password in order to allow the change of the mandate (via presence password) at
		// a later point of time
		if (sender.getPresencePassword() == null) {
			SAMLAssertion assertion = getCachedAssertion(dspId);
			if (assertion != null) {
				return assertion.getAssertionContent(withResponseTag);
			}
		}
		
		// and also the access privileges the physician has for the required dsp
		DspPrivileges privileges = getDspAccessPrivileges(sender, dspId);

		String errorMessage = null;
		
		// check if a presence password is required.
		if (sender.getPresencePassword() == null
				&& privileges.isPresencePasswordAllowed()) {
			// open a dialog to request the presence password from the user
			sender.setPresencePassword(ESanteDialog.showInputMessageDialog(
					MainFrame.getInstance(),
					Translatrix.getTranslationString("esante.exceptions.notAuthorized"),
					sender.getPresencePassword() == null 
							? Translatrix.getTranslationString("esante.exceptions.notAuthorized.enterPresenceCode") 
							: Translatrix.getTranslationString("esante.exceptions.notAuthorized.reenterPresenceCode"), 
					sender.getPresencePassword()));
			
			if (sender.getPresencePassword() == null && !privileges.isAuthorized()) {
				/* user pressed cancel button and you don't have the privileges 
				 * to access the DSP
				 */
				throw new SendingStoppedException(true);
			}
//		} else if (privileges.isOnBlacklist()) {
//			errorMessage = Translatrix.getTranslationString("esante.accessRights.notAuthorized.messageBlacklist");
		} else if (!privileges.isAuthorized()) {
			String ehrStateTranslation;
			if (privileges.getEhrState() == DspPrivileges.DSP_STATE_CLOSED) {
				ehrStateTranslation = Translatrix.getTranslationString("esante.accessRights.dspState.closed");
			} else if (privileges.getEhrState() == DspPrivileges.DSP_STATE_DELETED) {
				ehrStateTranslation = Translatrix.getTranslationString("esante.accessRights.dspState.deleted");
			} else {
				ehrStateTranslation = Translatrix.getTranslationString("esante.accessRights.dspState.unknown");
			}

			errorMessage = Translatrix.getTranslationString("esante.accessRights.notAuthorized.messageDspNotAvailable", 
					new String[] { ehrStateTranslation });
		}
		
		if (errorMessage != null) {
			ESanteDialog.showMessageDialog(MainFrame.getInstance(), Translatrix.getTranslationString("esante.accessRights.notAuthorized.title"), errorMessage,
					ESanteDialog.OK_BUTTON_MODE);
			throw new SendingStoppedException(false);
		}
		
		String samlAssertion = getValidSamlAssertion(sender, withResponseTag, dspId);
		
		// if a the presence password had been included in the call,
		if (sender.getPresencePassword() != null) {
			// the permission has changed. thus request the new permissions from the server
			getDspAccessPrivileges(sender, dspId);
			// and explicitely delete the presence password afterwards
			sender.setPresencePassword(null);
		}
		
		return samlAssertion;
	}

	/**
	 * Requests a saml assertion that is either specific to a certain patient dsp or a general user assertion. Either of them is needed for different webservice
	 * calls.
	 * 
	 * @param sender
	 *            Bean containing configuration data for soap communication and template filling
	 * @param withResponseTag
	 *            Flag that indicates if the function should only return the real saml assertion (<b>false</b>) or the whole response message (<b>true</b>)
	 * @param dspId
	 *            The id of the dsp for which the assertion should be requested. If this parameter is null, a general user saml assertion will be requested.
	 * @return The saml assertion (either dsp specific or user)
	 * @throws SmartCardException
	 *             If a problem in relation to the smartcard occurred
	 * @throws Exception
	 *             If it was not possible to obtain a valid saml assertion. As this might have many different reasons (e.g. invalid credentials, insufficient
	 *             permissions, no access to the server, blocked smartcard), please check the exception message for further details.
	 */
	private static String getValidSamlAssertion(SoapSender sender, boolean withResponseTag, String dspId) throws SmartCardException, SendingStoppedException, Exception {
		SAMLAssertion assertion = null;

		while (true) {
			// ask the user for his credentials, if necessary
			Configuration config = LoginDialog.getConfiguration();

			try {
				MainFrame.getInstance().setWaitCursor(true);
				// dependent on the login method that has been chosen by the user the corresponding template has to be filled
			//	String template = SOAPUtilities.getTemplateContent((dspId!=null)?WebserviceConstants.TEMPLATE_DSP_11_12:WebserviceConstants.TEMPLATE_DSP_10_11_12);
				String template = SOAPUtilities.getTemplateContent(WebserviceConstants.TEMPLATE_DSP_10_11_12);
				// set a unique id to the request
				template = template.replace(WebserviceConstants.PLACEHOLDER_AUTHOR_ID, "_" + UUID.randomUUID().toString());

				// add a tag with the eSanté DSP id, if known (otherwise this will become a general access saml assertion)
				template = SOAPUtilities.replaceInRequestTemplate(template, WebserviceConstants.PLACEHOLDER_PATIENT_ID,
						(dspId != null) ? WebserviceConstants.PH_TEMP_PATIENT_ID : null, dspId);

				// indicate in the XDS server endpoint
				template = template.replace(WebserviceConstants.PLACEHOLDER_CONSUMER_SERVICE, (dspId != null) ? config.getServiceUrl(ESanteProperty.PROP_SERVICE_ITI_41) : config.getServiceUrl(ESanteProperty.PROP_SERVICE_DSP_11_12));

				// determine the service endpoint, this request has to be sent to. The endpoint depends on the authentication method and if a user or dsp
				// assertion is required
				String serviceEndpoint = (dspId != null) ? config.getServiceUrl(ESanteProperty.PROP_SERVICE_DSP_11_12): 
							((config.getLoginMethod() == Configuration.LOGIN_SMARTCARD) ? config.getServiceUrl(ESanteProperty.PROP_SERVICE_DSP_10_X509) : 
								config.getServiceUrl(ESanteProperty.PROP_SERVICE_DSP_10_LP));

				// indicate in the request which service should be used
				template = template.replace(WebserviceConstants.PLACEHOLDER_DESTINATION, serviceEndpoint);

				// add a tag with the presence password if the user has not the appropriate mandate for accessing the patient dsp
				String value = sender.getPresencePassword();
				template = SOAPUtilities.replaceInRequestTemplate(template, WebserviceConstants.PLACEHOLDER_PRESENCE_PASSWORD,
						(value != null) ? WebserviceConstants.PH_TEMP_PRESENCE_PASSWORD : null, value);

				// define the creation date of the request
				String creationDate = SOAPUtilities.getFormattedDate(SOAPUtilities.DATE_OPTION_CREATED);
				template = template.replace(WebserviceConstants.PLACEHOLDER_SUBMISSION_TIME, creationDate);
				// define the validity delay for the saml assertion (usually 1h from now)
				String validityStart = SOAPUtilities.getFormattedDate(SOAPUtilities.DATE_OPTION_NOT_BEFORE);
				template = template.replaceAll(WebserviceConstants.PLACEHOLDER_NOT_BEFORE, validityStart);
				String validityEnd = SOAPUtilities.getFormattedDate(SOAPUtilities.DATE_OPTION_NOT_AFTER);
				template = template.replaceAll(WebserviceConstants.PLACEHOLDER_NOT_AFTER, validityEnd);

				// author type and id
				template = template.replace(WebserviceConstants.PLACEHOLDER_AUTHOR_TYPE, "3"); // doctor
				// template = template.replace(WebserviceConstants.PLACEHOLDER_AUTHOR_ID, getInactivityMonitor().getOwnerIdentification()[1]); // id
				// obtain the eHealth Id of the user
				String userEHealthId = ESanteGuiUtils.getUserEHealthId();
				String userEHealthIdShort = ESanteUtils.getShortEHealthId(userEHealthId);
				if (userEHealthId == null) {
//					throw new Exception("Unable to authenticate without a valid user eHealth id.");
					throw new SendingStoppedException(true);
				}
				
				
				// obtain the eHealth Id of the practice
				String practiceEHealthId = ESanteGuiUtils.getPracticeEHealthId(userEHealthId);
				if (practiceEHealthId == null) {
					throw new Exception("Unable to authenticate without a valid practice id.");
				}
				String practiceEHealthIdShort = ESanteUtils.getShortEHealthId(practiceEHealthId);
				
				// finally the GECAMed id is the last element needed for building valid OIDs
				String gecamedId = XDS.getGecamedApplicationId();
				if (gecamedId == null) {
					throw new Exception("The GECAMed id is missing and needs to be configured in the database. Please contact the support for further assistence.");
				}
				template = template.replace(WebserviceConstants.PLACEHOLDER_USER_EHEALTH_ID_LONG, userEHealthId); // eHealth id of the user
				template = template.replace(WebserviceConstants.PLACEHOLDER_USER_EHEALTH_ID_SHORT, userEHealthIdShort);
				template = template.replace(WebserviceConstants.PLACEHOLDER_PRACTICE_EHEALTH_ID_LONG, practiceEHealthId); // and of the practice
				template = template.replace(WebserviceConstants.PLACEHOLDER_PRACTICE_EHEALTH_ID_SHORT, practiceEHealthIdShort);
				template = template.replace(WebserviceConstants.PLACEHOLDER_APP_ID, gecamedId); // and also the id that identifies the gecamed application
				template = template.replace(WebserviceConstants.PLACEHOLDER_APP_NAME, SOAPUtilities.APP_NAME); // add the name of the application that is sending the request
				template = template.replace(WebserviceConstants.PLACEHOLDER_APP_VERSION, SOAPUtilities.APP_VERSION); // and also its version

				// now fill the security header. This depends on the authentication type, respectively if there is already a user saml assertion.
				// the first case should always be valid for dsp saml assertion requests as at this point of time there must already be a user saml assertion				
				if (dspId != null) {
					// add authentication reference to the template
					if (config.getLoginMethod() == Configuration.LOGIN_SMARTCARD){
						// indicate a reference to the authentication via smartcard
						template = template.replaceAll(WebserviceConstants.PLACEHOLDER_AUTHN_CONTEXT_CLASS_REF, WebserviceConstants.AUTHENTICATION_CLASS_X509);							
					} else{
						// indicate a reference to the authentication via login and password
						template = template.replaceAll(WebserviceConstants.PLACEHOLDER_AUTHN_CONTEXT_CLASS_REF, WebserviceConstants.AUTHENTICATION_CLASS_LP);			
					}
					// add the security header for using a preexisting user saml assertion for authentication
					template = template.replace(WebserviceConstants.PLACEHOLDER_SECURITY_HEADER, SOAPUtilities.getTemplateContent(WebserviceConstants.TEMPLATE_SECURITY_HEADER_ASSERTION));

					// and add this user saml assertion to this header
					template = template.replace(WebserviceConstants.PLACEHOLDER_SAML_ASSERTION, getUserSamlAssertion(sender, false));
					
				} else if (config.getLoginMethod() == Configuration.LOGIN_SMARTCARD) {
					// indicate a reference to the authentication via smartcard
					template = template.replaceAll(WebserviceConstants.PLACEHOLDER_AUTHN_CONTEXT_CLASS_REF, WebserviceConstants.AUTHENTICATION_CLASS_X509);							
					// add the security header that allows an authentication via X509 certificate
					template = template.replace(WebserviceConstants.PLACEHOLDER_SECURITY_HEADER,SOAPUtilities.getTemplateContent(WebserviceConstants.TEMPLATE_SECURITY_HEADER_X509));
					// as the X509 header requires the specification of a validity delay, it also has to be specified in the security header
					template = template.replaceAll(WebserviceConstants.PLACEHOLDER_NOT_BEFORE, validityStart);
					template = template.replaceAll(WebserviceConstants.PLACEHOLDER_NOT_AFTER, validityEnd);
					
					// Template is filled, no sign the request
					AuthenticationSignature signer = new AuthenticationSignature(getInactivityMonitor());
					template = signer.createAuthRequest(template, config.getPassword());
					
				} else if (config.getLoginMethod() == Configuration.LOGIN_USERNAME_PASSWORD) {
					// indicate a reference to the authentication via login and password
					template = template.replaceAll(WebserviceConstants.PLACEHOLDER_AUTHN_CONTEXT_CLASS_REF, WebserviceConstants.AUTHENTICATION_CLASS_LP);			
					// add the security header that allows an authentication via username and password
					template = template.replace(WebserviceConstants.PLACEHOLDER_SECURITY_HEADER,SOAPUtilities.getTemplateContent(WebserviceConstants.TEMPLATE_SECURITY_HEADER_LP));
					// set user login
					template = template.replace(WebserviceConstants.PLACEHOLDER_LOGIN, "user:" + config.getLogin());
					// calculate NOnce
					String nonce = Security.encodeBase64(java.util.UUID.randomUUID().toString());
					template = template.replace(WebserviceConstants.PLACEHOLDER_NONCE, nonce);
					// calculate created
					String created = SOAPUtilities.getFormattedDate(SOAPUtilities.DATE_OPTION_CREATED);
					template = template.replace(WebserviceConstants.PLACEHOLDER_CREATED, created);
					// calculate password
					String password = Security.wsSecurityPasswordDigest(nonce, created, config.getSha1Password());
					template = template.replace(WebserviceConstants.PLACEHOLDER_PASSWORD, password);
				} else {
					throw new Exception("The used login method is currently not supported");
				}

				// inform the sender bean about to where the request has to be sent
				sender.setWebserviceUrl(serviceEndpoint);
				
				// for each query a log file will be created. The prefix determines the type of the query
				if (dspId != null) {
					// DSP-11 is with dsp id and DSP-12 is with dsp id and presence password
					sender.resetSender((sender.getPresencePassword() == null) ? "DSP-11" : "DSP-12");
				} else {
					// just a general authentication request (should be changed to XUA profile, which unfortunately is not yet suported by the platform)
					sender.resetSender("DSP-10");
				}
				// finally fire the query up
				SOAPUtilities.callSOAP(template, sender);

			} catch (WebserviceException e) {
				String	reason;
				switch (e.getErrorType()) {
					case WebserviceException.TYPE_INVALID_LOGIN_DATA:
	
						LoginDialog.incorrectLoginData();
						sender.clear();
						continue;
						
					case WebserviceException.TYPE_PROFESSINOAL_NOT_FOUND:
						// delete wrong created practice ID if it was set for the same user
						ESanteGuiUtils.deletePracticeIDifMatchesEhealthUserID();
						// delete wrong esante properties
						ESanteGuiUtils.deleteEsanteUser(config.getAuthorId());
						
						// and show dialog.
					case WebserviceException.TYPE_INVALID_AUTHENTIFICATION:
						String	message	= e.getDetails();
						
						if (GECAMedUtils.isEmpty(message))
							message = e.getMessage();
						
//						if (e.getDetails() == null)
//							reason	= "";
//						else if (e.getDetails().startsWith("The certificate is unknown"))
//							reason	= Translatrix.getTranslationString("esante.exceptions.invalidAuthentification.reason.unknownCertificate");
//						else
//							reason	= "";
						
						// This should be the only possible reason for this type of error
						reason	= Translatrix.getTranslationString("esante.exceptions.invalidAuthentification.reason.unknownCertificate");
						
						ESanteDialog.showMessageDialog(MainFrame.getInstance(),
								Translatrix.getTranslationString("esante.exceptions.invalidAuthentification.title"), 
								Translatrix.getTranslationString("esante.exceptions.invalidAuthentification.message", 
										new String[] { message, reason }),
								ESanteDialog.OK_BUTTON_MODE, GECAMedModule.getScaledIcon(GECAMedIconNames.WARNING, 32));
						getInactivityMonitor().deactivateMonitor();
						UserInactivityMonitor.logout();
						sender.clear();
						throw new SendingStoppedException(false);
					case WebserviceException.TYPE_EMR_NOT_FOUND:
						
						message	= e.getDetails();
						
						if (GECAMedUtils.isEmpty(message))
							message = e.getMessage();
						
						// This should be the only possible reason for this type of error
						reason	= Translatrix.getTranslationString("esante.exceptions.dspNotAccessible.reason.closedOrDeleted");
						
						ESanteDialog.showMessageDialog(MainFrame.getInstance(),
								Translatrix.getTranslationString("esante.exceptions.dspNotAccessible.title"), 
								Translatrix.getTranslationString("esante.exceptions.dspNotAccessible.message", 
										new String[] { message, reason }),
								ESanteDialog.OK_BUTTON_MODE, GECAMedModule.getScaledIcon(GECAMedIconNames.WARNING, 32));
						getInactivityMonitor().deactivateMonitor();
						UserInactivityMonitor.logout();
						sender.clear();
						throw new SendingStoppedException(false);
	
					case WebserviceException.TYPE_NOT_AUTHORIZED:
						
						throw new SendingStoppedException(false);
						
					case WebserviceException.TYPE_INVALID_PRESENCE_PASS:
	
						sender.setPresencePassword(ESanteDialog.showInputMessageDialog(
								MainFrame.getInstance(), 
								Translatrix.getTranslationString("esante.exceptions.notAuthorized"),
//								sender.getPresencePassword() == null ? Translatrix.getTranslationString("esante.exceptions.notAuthorized.enterPresenceCode") : 
								Translatrix.getTranslationString("esante.exceptions.notAuthorized.reenterPresenceCode"), 
								sender.getPresencePassword()));
						
						if (sender.getPresencePassword() != null)
						{
							sender.clear();
							continue;
						}
						else
						{
							throw new SendingStoppedException(true);
						}
	
					case WebserviceException.TYPE_MANDATE_ALREADY_EXISTS:
	
						sender.setPresencePassword(null);
						sender.clear();
						continue;
						
					case WebserviceException.TYPE_INVALID_VALUE_IN_REQUEST:
					case WebserviceException.TYPE_INVALID_EHEALTH_ID:
						
						
						if (e.getErrorType() == WebserviceException.TYPE_INVALID_EHEALTH_ID)
							reason	= Translatrix.getTranslationString("esante.exceptions.invalidRequest.wrongEHealthIdReason");
						else
							reason	= Translatrix.getTranslationString("esante.exceptions.invalidRequest.invalidValueInRequestReason");
						
						ESanteDialog.showMessageDialog(
								MainFrame.getInstance(), 
								Translatrix.getTranslationString("esante.exceptions.invalidRequest.invalidValueInRequestTitle"), 
								Translatrix.getTranslationString("esante.exceptions.invalidRequest.invalidValueInRequestMessage", 
										new String[] { reason }), 
								ESanteDialog.OK_BUTTON_MODE, 
								GECAMedModule.getBigIcon(GECAMedIconNames.WARNING));
						UserInactivityMonitor.logout();
						throw new SendingStoppedException(false);
						
					default:
						// the following code generates a generic error dialog for any webservice exception w/o particular treatment (e.g. because it is unknown or unnecessary)
						String errorCode = e.getCode();
						String errorMessage = e.getMessage();
						String errorDetails = e.getDetails();
						
						String errorText = "<html>"+errorMessage+" (error code: "+errorCode+")";
						if(!errorDetails.isEmpty()){
							errorText+=":<br/><br/>"+errorDetails;
						}
						errorText+= "</html>";
				
						ESanteDialog.showMessageDialog(
								MainFrame.getInstance(), 
								Translatrix.getTranslationString("esante.exceptions.genericRequestError.title"),errorText, 
								ESanteDialog.OK_BUTTON_MODE, 
								GECAMedModule.getBigIcon(GECAMedIconNames.ERROR));
						UserInactivityMonitor.logout();
						throw new SendingStoppedException(false);
				}
			} catch (SmartCardException e) {
				// user logged in with invalid smartcard pin
				if (e.getCategory() == SmartCardException.ERROR_CATEGORY_INVALID_PASSWORD) {
					LoginDialog.incorrectLoginData();
					sender.clear();
					continue;
				} else if (e.getCategory() == SmartCardException.ERROR_CATEGORY_SMARTCARD_BLOCKED) {
					// Inform the user that his smartcard has been blocked
					ESanteDialog.showMessageDialog(MainFrame.getInstance(), Translatrix.getTranslationString("esante.loginDialog.incorrectLoginTitle"),
							e.getMessage(), GECAMedBaseDialogImpl.OK_BUTTON_MODE, GECAMedModule.getBigIcon(GECAMedIconNames.WARNING));
					// and end processing
					throw new SendingStoppedException(false);
				} else if (e.getCategory() == SmartCardException.ERROR_CATEGORY_UNKNOWN) {
					getInactivityMonitor().deactivateMonitor();
					LoginDialog.resetConfiguration();
					sender.clear();
					continue;
				}
				// forward the exception to the calling function
				throw new Exception(e);
			}
			finally
			{
				MainFrame.getInstance().setWaitCursor(false);
			}

			sender.checkThreadStopped();

			// no response means timeout ==> trouble with service or connection
			if((sender.getResponseMessage()==null)||(sender.getResponseMessage().length()==0)){
				ESanteDialog.showMessageDialog(
						MainFrame.getInstance(), 
						Translatrix.getTranslationString("esante.exceptions.connectionError"), 
						Translatrix.getTranslationString("esante.exceptions.connectionError.noResponseFromService"), 
						ESanteDialog.OK_BUTTON_MODE, 
						GECAMedModule.getBigIcon(GECAMedIconNames.WARNING));
				UserInactivityMonitor.logout();
				throw new SendingStoppedException(false);
			}
			
			// extract the saml assertion from the response of the server.
			assertion = new SAMLAssertion(sender.getResponseMessage());

			// check, if the DSP existed when getting the patient data for the first time
			String realIpid = assertion.getIpid();
			if (!(assertion.isUserAssertion() || realIpid.equals(sender.getDsp().getDspOid()))) {
				// the DSP did not exist. Thus link the GECAMed patient record with the new eSanté DSP
				Dsp dsp = sender.getDsp();
				dsp.setDspOid(realIpid);
				GECAMedModule.getCurrentPatient().setIdLuxembourg(realIpid);
				CDAManagerBean.getInstance().updateDspOid(dsp.getId(), dsp.getPatientId(), dsp.getDspOid());
				PatientManagerModule.getInstance().eSanteIdChanged(dsp.getDspOid(), dsp.getPatientId());
			}
			
			// cache the assertion for further usage and enable watchdog, if cache was empty before
			cacheAssertion(dspId, assertion);
			
			return assertion.getAssertionContent(withResponseTag);
		}
	}
	
	
	public static void clearDspAssertion (String dspOid)
	{
		cacheAssertion(dspOid, null);
	}
	

	/**
	 * Requests the access privileges for a specific DSP from the eSanté platform.
	 * 
	 * @param sender
	 *            Bean containing configuration data for soap communication and template fillinger
	 * @param dspId
	 *            The id of the eSanté DSP for which the mandate is requested.
	 * @return The access privileges of the user for the provided dsp.
	 * @throws SmartCardException
	 *             Caused by the underlying user saml request
	 * @throws Exception
	 *             caused by the underlying user saml request
	 */
	private static DspPrivileges getDspAccessPrivileges(SoapSender sender, String dspId) throws SmartCardException, Exception {

		DspPrivileges privileges;	// = getCachedDspPrivileges(dspId);
		
		
		// create the template for the ehr permission request
		String ehrRightsRequest = SOAPUtilities.getTemplateContent(WebserviceConstants.TEMPLATE_DSP_22);

		ehrRightsRequest = ehrRightsRequest.replace(WebserviceConstants.PLACEHOLDER_SAML_ASSERTION, getUserSamlAssertion(sender, false));
		ehrRightsRequest = ehrRightsRequest.replace(WebserviceConstants.PLACEHOLDER_PATIENT_ID, StringEscapeUtils.escapeXml(dspId));

		Configuration config = LoginDialog.getConfiguration();

		// request the ehr access rights from the webservice
		sender.setWebserviceUrl(config.getServiceUrl(ESanteProperty.PROP_SERVICE_DSP_22));
		sender.resetSender("DSP-22");
		SOAPUtilities.callSOAP(ehrRightsRequest, sender);
		
		sender.checkThreadStopped();
		// create a bean containing the dsp access privileges
		privileges = new DspPrivileges( sender.getResponseMessage());
		// and add them to the cache. They are cached each time this function is called as they might have changed since the last caching.
		cacheDspPriviliges(sender.getDsp(), privileges);
		
		return privileges;
	}


	/**
	 * Adds a new saml assertion to the cache and enables the user inactivity monitoring if it was not already active (this is the case if the cache has been
	 * purged before)
	 * 
	 * @param patientESanteId
	 *            The eSanté patient id, the assertion belongs to. If this parameter is null, the assertion will be assumed as a user saml assertion that is not
	 *            specific to any dsp.
	 * @param assertion
	 *            The saml assertion for the patient. If this parameter is null, the corresponding key will be deleted from cache, resulting in the removal of
	 *            any pre-existing assertion with this key from cache.
	 */
	private static void cacheAssertion(String patientESanteId, SAMLAssertion assertion) {
		if (patientESanteId == null) {
			patientESanteId = ESanteProperty.USER_ASSERTION_NAME;
		}

		if (assertion != null) {
			// cache the assertion
			assertions.put(patientESanteId, assertion);
			// if the inactivity monitor is not already running (the cache was empty before the current addition)
			if (!UserInactivityMonitor.isActive()) {
				// activate the inactivity monitor
				getInactivityMonitor().activateMonitor();
			}
			if (ESanteProperty.USER_ASSERTION_NAME.equals(patientESanteId)) {
				ESanteTab.connectionStatusChanged(ESanteTab.CONNECTION_STATUS_LOGGED_IN);
				// after successful login suppress the info message of the login dialog
				LoginDialog.displayExpirationMessage(false);
				try {
					// assure that no password is cached
					LoginDialog.getConfiguration().setClearPassword("");
				} catch (Exception e) {
				}
			}
		} else {
			// if only the dsp id has been provided, any assertion existing under this id will be removed from cache
			assertions.remove(patientESanteId);
			if (ESanteProperty.USER_ASSERTION_NAME.equals(patientESanteId)) {
				ESanteTab.connectionStatusChanged(ESanteTab.CONNECTION_STATUS_LOGGED_OUT);
				LoginDialog.displayExpirationMessage(false);
			}

		}
	}
	
	/**
	 * Adds a new DSP access state to the cache
	 * 
	 * @param patientESanteId
	 *            The eSanté patient id, the privileges belongs.
	 * @param privileges
	 *            The privileges for accessing the patient's DSP
	 */
	private static void cacheDspPriviliges (Dsp dsp, DspPrivileges privileges) {
		// cache the assertion
		dspPrivileges.put(dsp.getDspOid(), privileges);
		// display the new access permissions on screen
		printDspInfo(dsp);
	}
	
	
	/* ======================================== */
	// HELP METHODS
	/* ======================================== */

	/**
	 * Tries to obtain a cached saml assertion that is still valid. If a saml assertion that corresponds to the provided DSP OID is found in cache but is not
	 * valid (meaning not yet or not anymore), it is removed from cache.
	 * 
	 * @param dspOid
	 *            The OID of the electronic health record on the eSanté platform for which the assertion is requested
	 * @return The cached saml assertion or null if no <b>valid</b> saml assertion has been found
	 */
	private static SAMLAssertion getCachedAssertion(String dspOid) {
		// Try to find the desired assertion in the cache
		SAMLAssertion assertion = assertions.get(dspOid);
		// and check if it is still valid
		if ((assertion != null) && !assertion.isValid()) {
			assertions.remove(dspOid);
			assertion = null;
			// When the user assertion is expired, the user will be informed by a message and has to log in again
			if(ESanteProperty.USER_ASSERTION_NAME.equals(dspOid)){
				LoginDialog.displayExpirationMessage(true);
				LoginDialog.resetConfiguration();
			}
		}

		return assertion;
	}
	/**
	 * Provides cached privileges for accessing a specific patient DSP from the cache
	 * 
	 * @param dspOid
	 *            The OID of the electronic health record on the eSanté platform for which the privileges are requested
	 * @return The cached privileges or null if none have been found for the provided dsp oid
	 */
	public static DspPrivileges getCachedDspPrivileges(String dspOid) {
		return dspPrivileges.get(dspOid);
	}
	
	/**
	 * Purges all stored assertions. This is used when the user plugged out his LuxTrust card or exceeded a certain period of inactivity
	 */
	public static void purgeAllAssertions() {
		assertions.clear();
		ESanteTab.connectionStatusChanged(ESanteTab.CONNECTION_STATUS_LOGGED_OUT);
	}
	
	public static boolean hasValidSamlAssertion ()
	{
		return getCachedAssertion(ESanteProperty.USER_ASSERTION_NAME) != null;
	}
	
	/**
	 * Purges all stored DSP privileges. This is used when the user plugged out his LuxTrust card or exceeded a certain period of inactivity
	 */
	public static void purgeAllPrivileges () {
		dspPrivileges.clear();
		
		for (ESanteTab tab : ESanteTab.getESanteTabs())
		{
			if (tab.getCdaHandler() != null
					&& tab.getCdaHandler().getDsp() != null)
				printDspInfo(tab.getCdaHandler().getDsp());
		}
	}
	
	/**
	 * Checks if the user is still connected to the dsp.
	 * 
	 * @return True, if the connection is still established, false otherwise
	 */
	public static boolean isConnected() {
		return Security.getCachedAssertion(ESanteProperty.USER_ASSERTION_NAME) != null;
	}
	
	/**
	 * Updates the info panel that displays access information for the EHR of the user
	 * 
	 * @param dsp
	 *            The id of the EHR (dsp) of which the info should be displayed in the panel
	 */
	private static void printDspInfo (Dsp dsp)
	{
		// show the new information
		if (dsp.getPatientId() != null)
		{
			ESanteTab tab = ESanteTab.getESanteTab(dsp.getPatientId());
			if (tab != null)
				tab.getDocumentListPanel().printDspInfo(dsp);
		}
	}
	
	public static String sha1(byte[] input) throws NoSuchAlgorithmException {
		MessageDigest mDigest = MessageDigest.getInstance("SHA1");
		byte[] result = mDigest.digest(input);
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < result.length; i++) {
			sb.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1));
		}

		return sb.toString();
	}

	public static byte[] sha1AsBytes(byte[] input) throws NoSuchAlgorithmException {
		MessageDigest mDigest = MessageDigest.getInstance("SHA1");
		return mDigest.digest(input);
	}

	public static String encodeBase64(String input) {
		return encodeBase64(input.getBytes());
	}

	public static String encodeBase64(byte[] input) {
		return new String(Base64.encodeBase64(input));
	}

	public static String decodeBase64(String input) {
		return new String(Base64.decodeBase64(input));
	}

	public static byte[] decodeBase64AsBytes(String input) {
		return Base64.decodeBase64(input);
	}

	public static String wsSecurityPasswordDigest(String Nonce, String created, String SHA1_user_password) throws UnsupportedEncodingException,
			NoSuchAlgorithmException, DecoderException {
		// created the input password
		byte[] base64Array = Base64.encodeBase64(Hex.decodeHex(SHA1_user_password.toCharArray()));
		String inputPassword = "{sha}" + new String(base64Array, "UTF-8");

		// create the array which contains the bytes of all params
		byte[] b1 = Base64.decodeBase64(Nonce);
		byte[] b2 = created.getBytes();
		byte[] b3 = inputPassword.getBytes();
		byte[] b4 = new byte[b1.length + b2.length + b3.length];

		int offset = 0;
		System.arraycopy(b1, 0, b4, offset, b1.length);
		offset += b1.length;
		System.arraycopy(b2, 0, b4, offset, b2.length);
		offset += b2.length;
		System.arraycopy(b3, 0, b4, offset, b3.length);
		offset += b3.length;

		base64Array = Base64.encodeBase64(Security.sha1AsBytes(b4));
		String finalPassword = new String(base64Array, "UTF-8");

		return finalPassword;
	}

	/**
	 * Provides an instance of the inactivity monitor
	 * 
	 * @return Reference to the watchdog
	 */
	public static synchronized UserInactivityMonitor getInactivityMonitor() {

		if (inactivityMonitor == null) {
			inactivityMonitor = new UserInactivityMonitor();
		}

		return inactivityMonitor;
	}
}
