/***********************************************************
Copyright (C) 2018 VeriSign, Inc.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

http://www.verisign.com/nds/naming/namestore/techdocs.html
***********************************************************/
package com.verisign.epp.interfaces;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.verisign.epp.codec.gen.EPPGreeting;
import com.verisign.epp.codec.gen.EPPLoginCmd;

import com.verisign.epp.util.EPPEnv;

/**
 * Interface that can be implemented and configured to be called by
 * {@link EPPSession} to customize the {@link EPPLoginCmd} prior to sending it.
 * Both the server's {@link EPPGreeting} and the original {@link EPPLoginCmd} is
 * provided. The adapter can modify the {@link EPPLoginCmd} that will then be
 * sent.
 */
public class EPPLoginSecLoginAdapter implements EPPLoginAdapter {

	/**
	 * Logging category
	 */
	private static Logger cat = LoggerFactory.getLogger(EPPLoginSecLoginAdapter.class);
	      

	/**
	 * Include the user agent information in the extension?
	 */
	private boolean useUserAgent;

	/**
	 * The user agent information as a string.
	 */
	private String userAgentStr;

	/**
	 * The user agent client software
	 */
	private String userAgentClient;

	/**
	 * The user agent client language
	 */
	private String userAgentLang;

	/**
	 * The user agent operating system
	 */
	private String userAgentOs;

	/**
	 * Constructor that will retrieve the useUserAgent attribute value from
	 * {@link EPPEnv}.
	 */
	public EPPLoginSecLoginAdapter() {
		this.useUserAgent = EPPEnv.isUseUserAgent();

		cat.info("useUserAgent = " + this.useUserAgent);

		if (this.useUserAgent) {
			this.setUserAgent();
		}
	}

	/**
	 * Set the user agent information used for the option
	 * &lt;loginSec:userAgent&gt; element of the extension.
	 */
	private void setUserAgent() {
		// Set the EPP SDK information
		this.userAgentClient = "Verisign EPP SDK ";
		if (EPPEnv.getSDKVersion() != null) {
			this.userAgentClient += EPPEnv.getSDKVersion();
		}
		else {
			this.userAgentClient += "undefined";
		}

		// Set the platform information
		this.userAgentStr += " (";

		// Language
		this.userAgentLang = System.getProperty("java.vm.vendor");
		this.userAgentLang += " " + System.getProperty("java.vm.name");
		this.userAgentLang += " " + System.getProperty("java.version");

		this.userAgentStr += "; ";

		// OS
		this.userAgentOs = System.getProperty("os.arch");
		this.userAgentOs += " " + System.getProperty("os.name");
		this.userAgentOs += " " + System.getProperty("os.version");

		this.userAgentStr = this.userAgentClient + " (" + this.userAgentLang + "; " + this.userAgentOs + ")";

		cat.info("userAgent = \"" + this.userAgentStr + "\"");
	}

	/**
	 * Adapt the {@link EPPLoginCmd} <code>aLoginCmd</code> parameter based on
	 * the current state of the {@link EPPLoginCmd} and the contents of the
	 * {@link EPPGreeting} <code>aGreeting</code> parameter sent by the server.
	 *
	 * @param aLoginCmd
	 *           Original {@link EPPLoginCmd} created by {@link EPPSession} that
	 *           can be adapted by the concrete <code>EPPLoginAdapter</code>.
	 * @param aGreeting
	 *           {@link EPPGreeting} returned by the server that can be used to
	 *           drive the adapting of the {@link EPPLoginCmd}.
	 *
	 * @exception EPPCommandException
	 *               Error found with adapting the login
	 */
	public void adaptLogin(EPPLoginCmd aLoginCmd, final EPPGreeting aGreeting) throws EPPCommandException {

		cat.debug("enter");

		// Password too long?
		boolean longPw = false;
		if (aLoginCmd.getPassword() != null && aLoginCmd.getPassword().length() > 16) {
			longPw = true;
		}

		// New password too long?
		boolean longNewPw = false;
		if (aLoginCmd.hasNewPassword() && aLoginCmd.getNewPassword().length() > 16) {
			longNewPw = true;
		}

		// Determine if the login security extension should be attached.
		if (this.useUserAgent || longPw || longNewPw) {

			// Determine highest version of the login security extension supported
			// by the server; otherwise throw EPPCommandException.

			// v1_0 of login security extension?
			if (aGreeting.hasExtensionService(com.verisign.epp.codec.loginsec.v1_0.EPPLoginSecExtFactory.NS)) {

				com.verisign.epp.codec.loginsec.v1_0.EPPLoginSec loginSecExt = new com.verisign.epp.codec.loginsec.v1_0.EPPLoginSec();

				// User agent?
				if (this.useUserAgent) {
					cat.debug("Setting user agent");

					loginSecExt.setUserAgent(new com.verisign.epp.codec.loginsec.v1_0.EPPLoginSecUserAgent(
					      this.userAgentClient, this.userAgentLang, this.userAgentOs));
				}

				// Override password?
				if (longPw) {
					cat.debug("Placing password in v1_0 extension");

					loginSecExt.setPassword(aLoginCmd.getPassword());
					aLoginCmd.setPassword(com.verisign.epp.codec.loginsec.v1_0.EPPLoginSec.LOGIN_SECURITY_PASSWORD);
				}

				// Override new password?
				if (longNewPw) {
					cat.debug("Placing new password in v1_0 extension");

					loginSecExt.setNewPassword(aLoginCmd.getNewPassword());
					aLoginCmd.setNewPassword(com.verisign.epp.codec.loginsec.v1_0.EPPLoginSec.LOGIN_SECURITY_PASSWORD);
				}

				// Add login security extension
				aLoginCmd.addExtension(loginSecExt);
			}
			else { // No supported login security extensions is supported by server

				if (longPw) {
					throw new EPPCommandException("Long password of length " + aLoginCmd.getPassword().length()
					      + " requires the server to support one of the client supported login security extensions.");
				}

				if (longNewPw) {
					throw new EPPCommandException("Long new password of length " + aLoginCmd.getNewPassword().length()
					      + " requires the server to support one of the client supported login security extensions.");
				}

			}

		} // end if (longPw || longNewPw)

		cat.debug("exit");
	}
}
