/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.apache.directory.studio.apacheds.configuration.editor;


import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.apache.directory.api.ldap.model.constants.LdapSecurityConstants;
import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms;
import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.server.config.beans.DirectoryServiceBean;
import org.apache.directory.server.config.beans.ExtendedOpHandlerBean;
import org.apache.directory.server.config.beans.InterceptorBean;
import org.apache.directory.server.config.beans.LdapServerBean;
import org.apache.directory.server.config.beans.SaslMechHandlerBean;
import org.apache.directory.server.config.beans.TcpTransportBean;
import org.apache.directory.server.config.beans.TransportBean;
import org.apache.directory.studio.common.ui.CommonUIUtils;
import org.apache.directory.studio.common.ui.widgets.BaseWidgetUtils;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.forms.widgets.TableWrapData;
import org.eclipse.ui.forms.widgets.TableWrapLayout;


/**
 * This class represents the General Page of the Server Configuration Editor.
 *  
 * <pre>
 * +-------------------------------------------------------------------------------+
 * | +------------------------------------+ +------------------------------------+ |
 * | | .--------------------------------. | | .--------------------------------. | |
 * | | |V LDAP/LDAPS servers            | | | |V Supported Authn Mechanisms    | | |
 * | | +--------------------------------+ | | +--------------------------------+ | |
 * | | | [X] Enabled LDAP Server        | | | | [X] Simple      [X] GSSAPI     | | |
 * | | |  Address  : [////////////////] | | | | [X] CRAM-MD5    [X] Digest-MD5 | | |
 * | | |  Port     : [/////////]        | | | | [X] NTLM                       | | |
 * | | |  nbThreads: [/////////]        | | | |   Provider : [///////////////] | | |
 * | | |  backLog  : [/////////]        | | | | [X] GSS_SPNEGO                 | | |
 * | | | [X] Enabled LDAPS Server       | | | |   Provider : [///////////////] | | |
 * | | |  Address  : [////////////////] | | | | [X] Delegated                  | | |
 * | | |  Port     : [/////////]        | | | |   Host    : [////////////////] | | |
 * | | |  nbThreads: [/////////]        | | | |   Port    : [/////]            | | |
 * | | |  backLog  : [/////////]        | | | |   Ssl/tls : [====]             | | |
 * | | +--------------------------------+ | | |     Trust : [////////////////] | | |
 * | | .--------------------------------. | | |   Base DN : [////////////////] | | |
 * | | |V Server limits                 | | | +--------------------------------+ | |
 * | | +--------------------------------+ | | .--------------------------------. | |
 * | | |    Max time limit : [////////] | | | |V SASL Settings                 | | |
 * | | |    Max size limit : [////////] | | | +--------------------------------+ | |
 * | | |    Max PDU size   : [////////] | | | | SASL Host      : [///////////] | | |
 * | | +--------------------------------+ | | | SASL Principal : [///////////] | | |
 * | | .--------------------------------. | | | Search Base DN : [///////////] | | |
 * | | |V SSL/Start TLS keystore        | | | | SASL realms    :               | | |
 * | | +--------------------------------+ | | |   +-----------------+          | | |
 * | | |  keystore : [////////] (browse)| | | |   |                 | (add)    | | |
 * | | |  password : [////////////////] | | | |   |                 | (edit)   | | |
 * | | |             [X] Show password  | | | |   |                 | (delete) | | |
 * | | +--------------------------------+ | | |   +-----------------+          | | |
 * | | .--------------------------------. | | +--------------------------------+ | |
 * | | |V SSL Advanced Settings         | | |                                    | |
 * | | +--------------------------------+ | |                                    | |
 * | | |  [X] Require Client Auth       | | |                                    | |
 * | | |    [X] Request Client Auth     | | |                                    | |
 * | | |  Ciphers suite :               | | |                                    | |
 * | | |   +--------------------------+ | | |                                    | |
 * | | |   |[X] xyz                   | | | |                                    | |
 * | | |   |[X] abc                   | | | |                                    | |
 * | | |   |[X] def                   | | | |                                    | |
 * | | |   +--------------------------+ | | |                                    | |
 * | | | Enabled protocols :            | | |                                    | |
 * | | | [X] SSLv3  [X] TLSv1           | | |                                    | |
 * | | |        [X] TLSv1.1 [X] TLSv1.2 | | |                                    | |
 * | | +--------------------------------+ | |                                    | |
 * | | .--------------------------------. | |                                    | |
 * | | |V Advanced                      | | |                                    | |
 * | | +--------------------------------+ | |                                    | |
 * | | | [X] Enable TLS                 | | |                                    | |
 * | | | [X] Enable ServerSide PWD hash | | |                                    | |
 * | | |      hashing method {========} | | |                                    | |
 * | | | Replication pinger sleep [XXX] | | |                                    | |
 * | | | Disk sync delay [XXX]          | | |                                    | |
 * | | +--------------------------------+ | |                                    | |
 * | +------------------------------------+ +------------------------------------+ |
 * +-------------------------------------------------------------------------------+
 * </pre>
 * 
 * We manage the following parameters :
 * LDAP server controls. We manage :
 * <ul>
 * <li>the address</li>
 * <li>the port</li>
 * <li>the number of dedicated threads</li>
 * <li>the backlog size</li>
 * </ul> 
 *
 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
 */
public class LdapLdapsServersPage extends ServerConfigurationEditorPage
{
    private static final int DEFAULT_NB_THREADS = 4;
    private static final int DEFAULT_BACKLOG_SIZE = 50;
    private static final String TRANSPORT_ID_LDAP = "ldap"; //$NON-NLS-1$
    public static final String TRANSPORT_ID_LDAPS = "ldaps"; //$NON-NLS-1$
    private static final String SASL_MECHANISMS_SIMPLE = "SIMPLE"; //$NON-NLS-1$
    private static final String SSL_V3 = "SSLv3";
    private static final String TLS_V1_0 = "TLSv1";
    private static final String TLS_V1_1 = "TLSv1.1";
    private static final String TLS_V1_2 = "TLSv1.2";
    private static final String START_TLS_HANDLER_ID = "starttlshandler"; //$NON-NLS-1$
    private static final String START_TLS_HANDLER_CLASS = "org.apache.directory.server.ldap.handlers.extended.StartTlsHandler"; //$NON-NLS-1$
    private static final String HASHING_PASSWORD_INTERCEPTOR_ID = "passwordHashingInterceptor"; //$NON-NLS-1$
    private static final String HASHING_PASSWORD_INTERCEPTOR_FQCN_SSHA512 = "org.apache.directory.server.core.hash.Ssha512PasswordHashingInterceptor"; //$NON-NLS-1$
    private static final String HASHING_PASSWORD_INTERCEPTOR_FQCN_SHA512 = "org.apache.directory.server.core.hash.Sha512PasswordHashingInterceptor"; //$NON-NLS-1$
    private static final String HASHING_PASSWORD_INTERCEPTOR_FQCN_SSHA384 = "org.apache.directory.server.core.hash.Ssha384PasswordHashingInterceptor"; //$NON-NLS-1$
    private static final String HASHING_PASSWORD_INTERCEPTOR_FQCN_SHA384 = "org.apache.directory.server.core.hash.Sha384PasswordHashingInterceptor"; //$NON-NLS-1$
    private static final String HASHING_PASSWORD_INTERCEPTOR_FQCN_SSHA256 = "org.apache.directory.server.core.hash.Ssha256PasswordHashingInterceptor"; //$NON-NLS-1$
    private static final String HASHING_PASSWORD_INTERCEPTOR_FQCN_SHA256 = "org.apache.directory.server.core.hash.Sha256PasswordHashingInterceptor"; //$NON-NLS-1$
    private static final String HASHING_PASSWORD_INTERCEPTOR_FQCN_CRYPT = "org.apache.directory.server.core.hash.CryptPasswordHashingInterceptor"; //$NON-NLS-1$
    private static final String HASHING_PASSWORD_INTERCEPTOR_FQCN_SMD5 = "org.apache.directory.server.core.hash.Smd5PasswordHashingInterceptor"; //$NON-NLS-1$
    private static final String HASHING_PASSWORD_INTERCEPTOR_FQCN_MD5 = "org.apache.directory.server.core.hash.Md5PasswordHashingInterceptor"; //$NON-NLS-1$
    private static final String HASHING_PASSWORD_INTERCEPTOR_FQCN_SSHA = "org.apache.directory.server.core.hash.SshaPasswordHashingInterceptor"; //$NON-NLS-1$
    private static final String HASHING_PASSWORD_INTERCEPTOR_FQCN_SHA = "org.apache.directory.server.core.hash.ShaPasswordHashingInterceptor"; //$NON-NLS-1$

    /** The Page ID*/
    public static final String ID = LdapLdapsServersPage.class.getName(); //$NON-NLS-1$

    /** The Page Title */
    private static final String TITLE = Messages.getString( "LdapLdapsServersPage.LdapLdapsServers" ); //$NON-NLS-1$

    // UI Controls
    /** 
     * LDAP server controls. We manage :
     * <ul>
     * <li>the address</li>
     * <li>the port</li>
     * <li>the number of dedicated threads</li>
     * <li>the backlog size</li>
     * </ul> 
     **/
    private Button enableLdapCheckbox;
    private Text ldapPortText;
    private Text ldapAddressText;
    private Text ldapNbThreadsText;
    private Text ldapBackLogSizeText;
    
    /** LDAPS server controls */
    private Button enableLdapsCheckbox;
    private Text ldapsPortText;
    private Text ldapsAddressText;
    private Text ldapsNbThreadsText;
    private Text ldapsBackLogSizeText;
    private Button needClientAuthCheckbox;
    private Button wantClientAuthCheckbox;
    private boolean wantClientAuthStatus;
    
    /** The CiphersSuite controls */
    private CheckboxTableViewer ciphersSuiteTableViewer;
    
    /** The EnabledProtocols controls */
    private Button sslv3Checkbox;
    private Button tlsv1_0Checkbox;
    private Button tlsv1_1Checkbox;
    private Button tlsv1_2Checkbox;
    
    /** LDAP limits */
    private Text maxTimeLimitText;
    private Text maxSizeLimitText;
    private Text maxPduSizeText;
    
    /** The supported authentication controls */
    private Button authMechSimpleCheckbox;
    private Button authMechCramMd5Checkbox;
    private Button authMechDigestMd5Checkbox;
    private Button authMechGssapiCheckbox;
    private Button authMechNtlmCheckbox;
    private Text authMechNtlmText;
    private Button authMechGssSpnegoCheckbox;
    private Text authMechGssSpnegoText;

    /** The SASL controls */
    private Text saslHostText;
    private Text saslPrincipalText;
    private Text saslSearchBaseDnText;
    private TableViewer saslRealmsTableViewer;
    private Button addSaslRealmsButton;
    private Button editSaslRealmsButton;
    private Button deleteSaslRealmsButton;
    
    /** The Advanced controls */
    private Button enableTlsCheckbox;
    private Button enableServerSidePasswordHashingCheckbox;
    private ComboViewer hashingMethodComboViewer;
    private Text keystoreFileText;
    private Button keystoreFileBrowseButton;
    private Text keystorePasswordText;
    private Button showPasswordCheckbox;
    private Text replicationPingerSleepText;
    private Text diskSynchronizationDelayText;

    // UI Controls Listeners
    /**
     * The LDAP transport checkbox listener. When checked, we enable the following 
     * widgets :
     * <ul>
     * <li>Port</li>
     * <li>Address</li>
     * <li>NbThreads</li>
     * <li>BackLog</li>
     * </ul>
     */
    private SelectionAdapter enableLdapCheckboxListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            boolean enabled = enableLdapCheckbox.getSelection();
            
            getLdapServerTransportBean().setEnabled( enabled );
            setEnabled( ldapPortText, enabled );
            setEnabled( ldapAddressText, enabled );
            setEnabled( ldapNbThreadsText, enabled );
            setEnabled( ldapBackLogSizeText, enabled );
        }
    };
    
    
    /**
     * The LDAP port modify listener
     */
    private ModifyListener ldapPortTextListener = event -> 
        {
            try
            {
                int port = Integer.parseInt( ldapPortText.getText() );
                
                getLdapServerTransportBean().setSystemPort( port );
            }
            catch ( NumberFormatException nfe1 )
            {
                System.out.println( "Wrong LDAP TCP Port : it must be an integer" );
            }
        };

    
    /**
     * The LDAP address modify listener
     */
    private ModifyListener ldapAddressTextListener = event ->
        getLdapServerTransportBean().setTransportAddress( ldapAddressText.getText() );

    
    /**
     * The LDAP nbThreads modify listener
     */
    private ModifyListener ldapNbThreadsTextListener = event ->
        {
            try
            {
                int nbThreads = Integer.parseInt( ldapNbThreadsText.getText() );
                
                getLdapServerTransportBean().setTransportNbThreads( nbThreads );
            }
            catch ( NumberFormatException nfe2 )
            {
                System.out.println( "Wrong LDAP NbThreads : it must be an integer" );
            }
        };

    
    /**
     * The LDAP BackLogSize modify listener
     */
    private ModifyListener ldapBackLogSizeTextListener = event ->
        {
            try
            {
                int backLogSize = Integer.parseInt( ldapBackLogSizeText.getText() );
                
                getLdapServerTransportBean().setTransportBackLog( backLogSize );
            }
            catch ( NumberFormatException nfe3 )
            {
                System.out.println( "Wrong LDAP BackLog size : it must be an integer" );
            }
        };
    
    
    /**
     * The LDAPS transport checkbox listener. When checked, we enable the following 
     * controls :
     * <ul>
     * <li>Port</li>
     * <li>Address</li>
     * <li>NbThreads</li>
     * <li>BackLog</li>
     * <li>needClientAuth</li>
     * <li>wantClientAuth</li>
     * <li>Cipher suite (and associated buttons)</li>
     * <li>Enabled Protocols (and associated buttons)</li>
     * </ul>
     */
    private SelectionAdapter enableLdapsCheckboxListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            boolean enabled = enableLdapsCheckbox.getSelection();
            
            getLdapsServerTransportBean().setEnabled( enabled );
            setEnabled( ldapsPortText, enabled );
            setEnabled( ldapsAddressText, enabled );
            setEnabled( ldapsNbThreadsText, enabled );
            setEnabled( ldapsBackLogSizeText, enabled );
        }
    };
    
    
    /**
     * The LDAPS port modify listener
     */
    private ModifyListener ldapsPortTextListener = event ->
        {
            try
            {
                int port = Integer.parseInt( ldapsPortText.getText() );
                
                getLdapsServerTransportBean().setSystemPort( port );
            }
            catch ( NumberFormatException nfe4 )
            {
                System.out.println( "Wrong LDAPS Port : it must be an integer" );
            }
        };
    
    
    /**
     * The LDAPS address modify listener
     */
    private ModifyListener ldapsAddressTextListener = event ->
        getLdapsServerTransportBean().setTransportAddress( ldapsAddressText.getText() );

    
    /**
     * The LDAPS nbThreads modify listener
     */
    private ModifyListener ldapsNbThreadsTextListener = event ->
        {
            try
            {
                int nbThreads = Integer.parseInt( ldapsNbThreadsText.getText() );
                
                getLdapsServerTransportBean().setTransportNbThreads( nbThreads );
            }
            catch ( NumberFormatException nfe5 )
            {
                System.out.println( "Wrong LDAPS NbThreads : it must be an integer" );
            }
        };

    
    /**
     * The LDAPS BackLogSize modify listener
     */
    private ModifyListener ldapsBackLogSizeTextListener = event ->
        {
            try
            {
                int backLogSize = Integer.parseInt( ldapsBackLogSizeText.getText() );
                
                getLdapsServerTransportBean().setTransportBackLog( backLogSize );
            }
            catch ( NumberFormatException nfe6 )
            {
                System.out.println( "Wrong LDAPS BackLog size : it must be an integer" );
            }
        };
    
    
    /**
     * As listener for the NeedClientAuth checkbox : we have to check the 
     * WantClientAuth checkbox when the NeedClientAuth is selected.
     */
    private SelectionAdapter needClientAuthListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            boolean enabled = needClientAuthCheckbox.getSelection();

            // Inject the flag in the config
            TransportBean ldapTransport = getLdapServerTransportBean();
            
            ldapTransport.setWantClientAuth( enabled );
            
            TransportBean ldapsTransport = getLdapsServerTransportBean();
            
            ldapsTransport.setWantClientAuth( enabled );

            // Turn on/off the NeedClientAuth
            if ( enabled )
            {
                wantClientAuthCheckbox.setSelection( enabled );
            }
            else
            {
                // restore the previous value
                wantClientAuthCheckbox.setSelection( wantClientAuthStatus );
            }
            
            // And disable it or enable it
            setEnabled( wantClientAuthCheckbox, !enabled );
            
            // last, 
        }
    };

    
    /**
     * As listener for the WantClientAuth checkbox
     */
    private SelectionAdapter wantClientAuthListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            boolean enabled = wantClientAuthCheckbox.getSelection();

            // Inject the flag in the config - for all the transports, as
            // it may be for SSL or startTLS - 
            TransportBean ldapTransport =  getLdapServerTransportBean();
            
            ldapTransport.setWantClientAuth( enabled );

            TransportBean ldapsTransport =  getLdapsServerTransportBean();
            
            ldapsTransport.setWantClientAuth( enabled );

            // Keep a track of the WantClientAuth flag
            wantClientAuthStatus = enabled;
        }
    };

    
    /**
     * The SASL Host modify listener
     */
    private ModifyListener saslHostTextListener = event ->
        getLdapServerBean().setLdapServerSaslHost( saslHostText.getText() );
    
    
    /**
     * The SASL principal modify listener
     */
    private ModifyListener saslPrincipalTextListener = event ->
        getLdapServerBean().setLdapServerSaslPrincipal( saslPrincipalText.getText() );

    
    /**
     * The SASL search Base DN modify listener
     */
    private ModifyListener saslSearchBaseDnTextListener = event ->
        {
            String searchBaseDnValue = saslSearchBaseDnText.getText();

            try
            {
                Dn searchBaseDn = new Dn( searchBaseDnValue );
                getLdapServerBean().setSearchBaseDn( searchBaseDn );
            }
            catch ( LdapInvalidDnException e1 )
            {
                // Stay silent
            }
        };
    
    
    /**
     * SASL realms Table change
     */
    private ISelectionChangedListener saslRealmsTableViewerSelectionChangedListener = event ->
        {
            StructuredSelection selection = ( StructuredSelection ) saslRealmsTableViewer.getSelection();

            editSaslRealmsButton.setEnabled( !selection.isEmpty() );
            deleteSaslRealmsButton.setEnabled( !selection.isEmpty() );
        };
    
    
    /**
     * SaslRealms Table double-click
     */
    private IDoubleClickListener saslRealmsTableViewerDoubleClickListener = event -> editSaslRealmsAction();
    

    /**
     * Add SASL realms button
     */
    private SelectionListener addSaslRealmsButtonListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            InputDialog dialog = new InputDialog( editSaslRealmsButton.getShell(),
                Messages.getString( "LdapLdapsServersPage.Add" ), //$NON-NLS-1$
                Messages.getString( "LdapLdapsServersPage.SaslRealms" ), //$NON-NLS-1$
                null, null );

            if ( dialog.open() == InputDialog.OK )
            {
                String newSaslRealms = dialog.getValue();

                getLdapServerBean().addSaslRealms( newSaslRealms );

                saslRealmsTableViewer.refresh();
                saslRealmsTableViewer.setSelection( new StructuredSelection( newSaslRealms ) );

                setEditorDirty();
            }
        }
    };
    
    
    /**
     * Edit SASL realms button
     */
    private SelectionListener editSaslRealmsButtonListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            editSaslRealmsAction();
        }
    };
    
    
    /**
     * Delete SASL realms button
     */
    private SelectionListener deleteSaslRealmsButtonListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            String selectedSaslRealms = getSelectedSaslRealms();

            if ( selectedSaslRealms != null )
            {
                getLdapServerBean().getLdapServerSaslRealms().remove( selectedSaslRealms );
                saslRealmsTableViewer.refresh();

                setEditorDirty();
            }
        }
    };

    
    /**
     * The AuthMech Simple checkbox listener
     */
    private SelectionAdapter authMechSimpleCheckboxListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            setEnableSupportedAuthenticationMechanism( SASL_MECHANISMS_SIMPLE, authMechSimpleCheckbox.getSelection() );
        }
    };
    
    
    /**
     * The AuthMech GSSAPI checkbox listener
     */
    private SelectionAdapter authMechGssapiCheckboxListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            setEnableSupportedAuthenticationMechanism( SupportedSaslMechanisms.GSSAPI,
                authMechGssapiCheckbox.getSelection() );
        }
    };

    
    /**
     * The AuthMech CRAM-MD5 checkbox listener
     */
    private SelectionAdapter authMechCramMd5CheckboxListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            setEnableSupportedAuthenticationMechanism( SupportedSaslMechanisms.CRAM_MD5,
                authMechCramMd5Checkbox.getSelection() );
        }
    };
    
    
    /**
     * The AuthMech Digest MD5 checkbox listener
     */
    private SelectionAdapter authMechDigestMd5CheckboxListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            setEnableSupportedAuthenticationMechanism( SupportedSaslMechanisms.DIGEST_MD5,
                authMechDigestMd5Checkbox.getSelection() );
        }
    };
    
    
    /**
     * The AuthMech GSS-SPNEGO checkbox listener
     */
    private SelectionAdapter authMechGssSpnegoCheckboxListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            setEnableSupportedAuthenticationMechanism( SupportedSaslMechanisms.GSS_SPNEGO,
                authMechGssSpnegoCheckbox.getSelection() );
            setEnabled( authMechGssSpnegoText, authMechGssSpnegoCheckbox.getSelection() );
        }
    };

    
    /**
     * The AuthMech GSS-SPNEGO text listener
     */
    private ModifyListener authMechGssSpnegoTextListener = event ->
        setNtlmMechProviderSupportedAuthenticationMechanism( SupportedSaslMechanisms.GSS_SPNEGO, authMechGssSpnegoText.getText() );
    
    
    /**
     * The AuthMech NTLM checkbox listener
     */
    private SelectionAdapter authMechNtlmCheckboxListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            setEnableSupportedAuthenticationMechanism( SupportedSaslMechanisms.NTLM,
                authMechNtlmCheckbox.getSelection() );
            setEnabled( authMechNtlmText, authMechNtlmCheckbox.getSelection() );
        }
    };
    
    
    /**
     * The AuthMech NTLM  text listener
     */
    private ModifyListener authMechNtlmTextListener = event ->
        setNtlmMechProviderSupportedAuthenticationMechanism( SupportedSaslMechanisms.NTLM, authMechNtlmText.getText() );
    
    
    /**
     * The maximum time for a SearchRequest's response
     */
    private ModifyListener maxTimeLimitTextListener = event ->
        getLdapServerBean().setLdapServerMaxTimeLimit( Integer.parseInt( maxTimeLimitText.getText() ) );
    
    
    /**
     * The maximum size for a SearchRequest's response
     */
    private ModifyListener maxSizeLimitTextListener = event ->
        getLdapServerBean().setLdapServerMaxSizeLimit( Integer.parseInt( maxSizeLimitText.getText() ) );
    
    
    /**
     * The maximum size for a request PDU
     */
    private ModifyListener maxPduSizeTextListener = event ->
        getLdapServerBean().setMaxPDUSize( Integer.parseInt( maxPduSizeText.getText() ) );
    
    
    /**
     * Tells if TLS is enabled
     */
    private SelectionAdapter enableTlsCheckboxListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            setEnableTls( enableTlsCheckbox.getSelection() );
        }


        /**
         * Enables/disables TLS.
         *
         * @param enabled the enabled state
         */
        private void setEnableTls( boolean enabled )
        {
            getTlsExtendedOpHandlerBean().setEnabled( enabled );
        }
    };
    
    
    /**
     * Tell the server to hash the passwords
     */
    private SelectionAdapter enableServerSidePasswordHashingCheckboxListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent e )
        {
            if ( enableServerSidePasswordHashingCheckbox.getSelection() )
            {
                enableHashingPasswordInterceptor();
            }
            else
            {
                disableHashingPasswordInterceptor();
            }

            setEnabled( hashingMethodComboViewer.getCombo(), enableServerSidePasswordHashingCheckbox.getSelection() );
        }


        /**
         * Enables the hashing password interceptor.
         */
        private void enableHashingPasswordInterceptor()
        {
            // Getting the hashing password interceptor
            InterceptorBean hashingPasswordInterceptor = getHashingPasswordInterceptor();

            // If we didn't found one, we need to create it
            if ( hashingPasswordInterceptor == null )
            {
                // Creating a new hashing password interceptor
                hashingPasswordInterceptor = createHashingPasswordInterceptor();
            }

            // Enabling the interceptor
            hashingPasswordInterceptor.setEnabled( true );
        }


        /**
         * Disables the hashing password interceptor.
         */
        private void disableHashingPasswordInterceptor()
        {
            // Getting the hashing password interceptor
            InterceptorBean hashingPasswordInterceptor = getHashingPasswordInterceptor();

            if ( hashingPasswordInterceptor != null )
            {
                // Disabling the interceptor
                hashingPasswordInterceptor.setEnabled( false );
            }
        }
    };
    
    
    /**
     * The list of method to use to hash the passwords
     */
    private ISelectionChangedListener hashingMethodComboViewerListener = event -> updateHashingMethod();
    
    
    /**
     * The keyStore file listener
     */
    private ModifyListener keystoreFileTextListener = event ->
        {
            String keystoreFile = keystoreFileText.getText();

            if ( ( keystoreFile == null ) || ( keystoreFile.length() == 0 ) )
            {
                getLdapServerBean().setLdapServerKeystoreFile( null );
            }
            else
            {
                getLdapServerBean().setLdapServerKeystoreFile( keystoreFile );
            }
        };
    
    
    /**
     * Let the user browse the disk to find the keystore file
     */
    private SelectionListener keystoreFileBrowseButtonSelectionListener = new SelectionAdapter()
    {
        @Override
        public void widgetSelected( SelectionEvent event )
        {
            FileDialog fileDialog = new FileDialog( keystoreFileBrowseButton.getShell(), SWT.OPEN );

            File file = new File( keystoreFileText.getText() );
            
            if ( file.isFile() )
            {
                fileDialog.setFilterPath( file.getParent() );
                fileDialog.setFileName( file.getName() );
            }
            else if ( file.isDirectory() )
            {
                fileDialog.setFilterPath( file.getPath() );
            }
            else
            {
                fileDialog.setFilterPath( null );
            }

            String returnedFileName = fileDialog.open();
            
            if ( returnedFileName != null )
            {
                keystoreFileText.setText( returnedFileName );
                setEditorDirty();
            }
        }
    };
    
    
    /**
     * The keystore password listener
     */
    private ModifyListener keystorePasswordTextListener = event ->
        {
            String keystorePassword = keystorePasswordText.getText();

            if ( ( keystorePassword == null ) || ( keystorePassword.length() == 0 ) )
            {
