/*
 * RHQ Management Platform
 * Copyright (C) 2005-2008 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 of the License.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.rhq.enterprise.communications.util;

import java.io.File;
import java.util.Arrays;

import mazz.i18n.Logger;

import org.rhq.enterprise.communications.command.impl.start.StartCommand;
import org.rhq.enterprise.communications.command.impl.start.StartCommandResponse;
import org.rhq.enterprise.communications.command.impl.start.server.ProcessExec;
import org.rhq.enterprise.communications.i18n.CommI18NFactory;
import org.rhq.enterprise.communications.i18n.CommI18NResourceKeys;

/**
 * Utilities to help perform security tasks.
 *
 * @author John Mazzitelli
 */
public class SecurityUtil {
    /**
     * Logger
     */
    private static final Logger LOG = CommI18NFactory.getLogger(SecurityUtil.class);

    /**
     * Prevents instantiation.
     */
    private SecurityUtil() {
    }

    /**
     * Given a JBoss/Remoting transport name (such as "sslsocket" or "http") this will return <code>true</code> if that
     * transport is considered secure via SSL.
     *
     * @param  transport the name of the transport protocol (can be the transport only or the entire locator URI)
     *
     * @return <code>true</code> if the transport protocol uses SSL to secure the data, <code>false</code> if not
     */
    public static boolean isTransportSecure(String transport) {
        return (transport.startsWith("ssl") || transport.startsWith("https"));
    }

    /**
     * Creates a keystore and places a new key in it that has the given key information. If the keystore file already
     * exists, this method does nothing. This will only create the key and keystore if the file does not yet exist.
     *
     * <p>If the key password is <code>null</code> or an empty string, it will be set to the same as the keystore
     * password. If the keystore password is <code>null</code>, an exception is thrown.</p>
     *
     * <p>If either the keystore password or key password is not at least 6 characters long, an exception is thrown.</p>
     *
     * <p>If validity is less than or equal to 0, it will default to 100 years.</p>
     *
     * @param  file_path         the path of the keystore file on the file system (if this exists already, this method
     *                           does nothing)
     * @param  key_alias         the alias name of the key that will be generated and placed in the key store
     * @param  domain_name       the domain name of the new key
     * @param  keystore_password the password of the keystore file (must not be <code>null</code>)
     * @param  key_password      the password of the key within the keystore
     * @param  key_algorithm     the algorithm used to generate the new key
     * @param  validity          the number of days the key is valid for
     *
     * @throws RuntimeException if failed to create the keystore file
     */
    public static void createKeyStore(String file_path, String key_alias, String domain_name, String keystore_password,
        String key_password, String key_algorithm, int validity) throws RuntimeException {
        File keystore = new File(file_path);

        // if the keystore already exists, don't do anything - just return immediately
        if (keystore.exists()) {
            LOG.debug(CommI18NResourceKeys.KEYSTORE_EXISTS, keystore);
            return;
        }

        // perform some input parameter validation
        if (validity <= 0) {
            validity = 36500;
        }

        if (keystore_password == null) {
            throw new RuntimeException(LOG.getMsgString(CommI18NResourceKeys.KEYSTORE_PASSWORD_NULL));
        }

        if ((key_password == null) || (key_password.length() == 0)) {
            LOG.debug(CommI18NResourceKeys.KEY_PASSWORD_UNSPECIFIED, keystore);
            key_password = keystore_password;
        }

        if (keystore_password.length() < 6) {
            throw new RuntimeException(LOG.getMsgString(CommI18NResourceKeys.KEYSTORE_PASSWORD_NOT_LONG_ENOUGH));
        }

        if (key_password.length() < 6) {
            throw new RuntimeException(LOG.getMsgString(CommI18NResourceKeys.KEY_PASSWORD_NOT_LONG_ENOUGH));
        }

        // make sure the keystore's directory exists before we ask keytool to write to it
        File keystore_dir = keystore.getParentFile();
        if (keystore_dir != null && !keystore_dir.exists()) {
            boolean success = keystore_dir.mkdirs();
            if (!success) {
                throw new RuntimeException(LOG.getMsgString(CommI18NResourceKeys.CANNOT_CREATE_KEYSTORE_DIR,
                    keystore_dir));
            }
        }

        // execute the keytool utility to create our key store
        String keytool_dir = System.getProperty("java.home") + File.separator + "bin";
        String keytool_exe = System.getProperty("os.name").toLowerCase().startsWith("windows") ? "keytool.exe"
            : "keytool";
        StartCommand keytool_cmd = new StartCommand();
        keytool_cmd.setProgramDirectory(keytool_dir);
        keytool_cmd.setProgramExecutable(keytool_exe);
        keytool_cmd.setWaitForExit(Long.valueOf(30000L));
        keytool_cmd.setCaptureOutput(Boolean.FALSE);
        keytool_cmd.setCommandInResponse(true);

        keytool_cmd.setArguments(new String[] { "-genkey", "-alias", key_alias, "-dname", domain_name, "-keystore",
            keystore.getAbsolutePath(), "-storepass", keystore_password, "-keypass", key_password, "-keyalg",
            key_algorithm, "-validity", Integer.toString(validity) });

        StartCommandResponse keytool_results = new ProcessExec().execute(keytool_cmd);

        if (!keytool_results.isSuccessful() || !keystore.exists()) {
            throw new RuntimeException(LOG.getMsgString(CommI18NResourceKeys.KEYSTORE_CREATION_FAILURE,
                keytool_results, Arrays.asList(keytool_cmd.getArguments()).toString()), keytool_results.getException());
        }

        LOG.debug(CommI18NResourceKeys.KEYSTORE_CREATED, keystore.getAbsolutePath());

        return;
    }
}