SetupActionsService.java

package sk.iway.iwcm.setup;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.springframework.ui.Model;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import sk.iway.iwcm.Constants;
import sk.iway.iwcm.DBPool;
import sk.iway.iwcm.FileTools;
import sk.iway.iwcm.InitServlet;
import sk.iway.iwcm.PageLng;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.XmlUtils;
import sk.iway.iwcm.i18n.Prop;
import sk.iway.iwcm.system.ConfDB;
import sk.iway.iwcm.system.UpdateDatabase;

/**
 * SetupAction.java - priprava formularu pre nastavenie systemu
 */
@SuppressWarnings("java:S106")
public class SetupActionsService {

	private static final String FORWARD = "/admin/setup/setup";
	private static final String SAVED = "/admin/setup/setup_saved";

	private SetupActionsService() {
		// Private constructor to hide the implicit public one.
	}

	private static boolean isHostAllowed(String serverName) {
		if ("iwcm.interway.sk".equals(serverName) || "localhost".equals(serverName)) return true;
		return false;
	}

	private static String readPoolman() {
		// nacitaj DB konfiguraciu (ak existuje)
		String systemIwcmDBName = InitServlet.getContextDbName();
		if (Tools.isEmpty(systemIwcmDBName)) systemIwcmDBName = System.getProperty("webjetDbname");
		System.out.println("systemIwcmDBName="+systemIwcmDBName);

		//aj cez <Context ... <Parameter name="webjetDbname" value="/poolman-local.xml" override="true"/> je mozne zadat cestu
		String customPoolmanPath = null;
		if (Tools.isNotEmpty(systemIwcmDBName) && systemIwcmDBName.endsWith(".xml")) {
			customPoolmanPath = systemIwcmDBName;
			systemIwcmDBName = "iwcm";
		}
		String data = DBPool.readFileContent(customPoolmanPath);

		return data;
	}

	public static String setupAction(Model model, HttpServletRequest request, HttpServletResponse response, String lng) throws IOException {

		if (InitServlet.isWebjetInitialized() || isHostAllowed(request.getServerName()) == false) {
			return null;
		}

		String data = readPoolman();

		if ((data != null && data.length()>30) && isHostAllowed(request.getServerName()) == false) {
			System.out.println("poolman allready exists");
			return null;
		}

		SetupFormBean sForm = new SetupFormBean();

		if (data != null && Tools.isNotEmpty(data)) {
			try {
				DocumentBuilderFactory b = DocumentBuilderFactory.newInstance();
				// to be compliant, completely disable DOCTYPE declaration:
				b.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
				DocumentBuilder dc = b.newDocumentBuilder();
				ByteArrayInputStream input = new ByteArrayInputStream(data.getBytes());
				Document doc = dc.parse(input);
				if (doc != null) {
					Node n = XmlUtils.getFirstChild(doc.getDocumentElement(), "datasource");
					if (n != null) {
						sForm.setDbUsername(XmlUtils.getFirstChildValue(n, "username"));
						sForm.setDbPassword(XmlUtils.getFirstChildValue(n, "password"));

						try {
							String driver = XmlUtils.getFirstChildValue(n, "driver");
							if (driver != null) {
								if (driver.contains("Oracle"))
									sForm.setDbDriver("oracle.jdbc.driver.OracleDriver");
								else if (driver.contains("jtds"))
									sForm.setDbDriver("net.sourceforge.jtds.jdbc.Driver");
								else if (driver.contains("postgresql"))
									sForm.setDbDriver("org.postgresql.Driver");
							}
							String url = XmlUtils.getFirstChildValue(n, "url");
							if (url != null) {
								String serverNameDelimiter = "://";
								int iServerName = url.indexOf(serverNameDelimiter);
								if (iServerName == -1) {
									serverNameDelimiter = "@";
									iServerName = url.indexOf(serverNameDelimiter); // oracle verzia
								}
								if (iServerName > 0) {
									int iSchemaName = url.indexOf("/", iServerName + serverNameDelimiter.length());
									if (iSchemaName > iServerName) {
										String serverName = url.substring(iServerName + serverNameDelimiter.length(),
												iSchemaName);
										int i = serverName.indexOf(":");
										if (i > 0) {
											sForm.setDbDomain(serverName.substring(0, i));
											sForm.setDbPort(serverName.substring(i + 1));
										} else {
											sForm.setDbDomain(serverName);
										}

										String schemaName = url.substring(iSchemaName + 1);
										i = schemaName.indexOf("?");
										if (i == -1)
											i = schemaName.indexOf(";");

										if (i > 0) {
											sForm.setDbName(schemaName.substring(0, i));
											sForm.setDbParameters(schemaName.substring(i + 1));
										} else {
											sForm.setDbName(schemaName);
										}

										String installName = sForm.getDbName();
										i = installName.indexOf("_web");
										if (i > 0)
											installName = installName.substring(0, i);
										sForm.setConf_installName(installName);
									}

									if (driver != null && driver.contains("Oracle")) {
										sForm.setDbParameters(sForm.getDbName());
										sForm.setDbName("");
									}

								}
							}
						} catch (Exception ex) {
							ex.printStackTrace(System.err);
						}
					}
				}
			} catch (Exception ex) {
				sk.iway.iwcm.Logger.error(ex);
			}
		}

		data = FileTools.readFileContent("/WEB-INF/web-runtime.xml");
		if (Tools.isNotEmpty(data)) {
			try {
				DocumentBuilderFactory b = DocumentBuilderFactory.newInstance();
				// to be compliant, completely disable DOCTYPE declaration:
				b.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
				DocumentBuilder dc = b.newDocumentBuilder();
				ByteArrayInputStream input = new ByteArrayInputStream(data.getBytes());
				Document doc = dc.parse(input);
				if (doc != null) {
					Vector<Node> servletNodes = XmlUtils.getChildNodes(doc.getDocumentElement(), "servlet");
					if (servletNodes != null && servletNodes.size() > 0) {
						for (Node servletNode : servletNodes) {
							Vector<Node> initParamNodes = XmlUtils.getChildNodes(servletNode, "init-param");
							if (initParamNodes != null && initParamNodes.size() > 0) {
								for (Node initParamNode : initParamNodes) {
									String paramName = XmlUtils.getFirstChildValue(initParamNode, "param-name");
									String paramValue = XmlUtils.getFirstChildValue(initParamNode, "param-value");

									if (Tools.isEmpty(paramValue))
										continue;

									if ("license".equals(paramName)) {
										sForm.setConf_license(paramValue);
									} else if ("smtpServer".equals(paramName)) {
										sForm.setConf_smtpServer(paramValue);
									}
								}
							}
						}
					}
				}
			} catch (Exception ex) {
				sk.iway.iwcm.Logger.error(ex);
			}
		}

		if (Tools.isEmpty(sForm.getConf_installName())) {
			// zdetekuj install name
			String path = Constants.getServletContext().getRealPath("/");

			String installName = "";
			if (path != null) {
				File f = new File(path);
				installName = f.getName();
				installName = Tools.replace(installName, ".sk", "");
				installName = Tools.replace(installName, ".cz", "");
				installName = Tools.replace(installName, ".com", "");
				installName = Tools.replace(installName, "www.", "");
			}
			sForm.setConf_installName(installName);
			sForm.setDbName(installName + "_web");
		}

		if(Tools.isNotEmpty(lng)) {
            PageLng.setUserLng(request, response, lng);
            request.getSession().setAttribute(Prop.SESSION_I18N_PROP_LNG, lng);
			sForm.setPageLngIndicator(lng);
    	} else {
			String oldLng = (String) request.getSession().getAttribute(Prop.SESSION_I18N_PROP_LNG);
			if(Tools.isNotEmpty(oldLng)) sForm.setPageLngIndicator(oldLng);
		}

		setModel(model, sForm, false, false);

		return FORWARD;
	}

	public static String setupSaveAction(SetupFormBean setupForm, Model model, HttpServletRequest request, HttpServletResponse response) {
		if (InitServlet.isWebjetInitialized() || isHostAllowed(request.getServerName()) == false){
			System.out.println("WebJET is allready initialized");
			return SAVED;
		}

		String data = readPoolman();
		if ((data != null && data.length() > 30) && isHostAllowed(request.getServerName()) == false) {
			System.out.println("poolman allready exists");
			return null;
		}

		//uloz DB konfiguraciu
		if (data == null || data.length() < 30)
			savePoolman(setupForm);

		//vytvor/napln databazu
		boolean dbConnectOK = false;
		String connErrMsg = "";
		Connection con = null;
		try {
			System.out.println("Checking database connection: ");

			try {
				Class.forName(setupForm.getDbDriver()).getDeclaredConstructor().newInstance();
			} catch (Exception ex) {
				connErrMsg += "Could Not Find the database Driver: " + setupForm.getDbDriver();
			}

			String userName = setupForm.getDbUsername();
			String password = setupForm.getDbPassword();
			if (Tools.isEmpty(userName)) userName = null;
			if (Tools.isEmpty(password)) password = null;
			con = DriverManager.getConnection(getDBURLString(setupForm), userName, password);
			con.close();
			dbConnectOK = true;
		}
		catch (Exception ex) {
			try {
				if (con != null) con.close();
			} catch (Exception e) {}

			String msg = ex.getMessage();

			if (msg.indexOf("Unknown database ") != -1) {
				//DB nie je vytvorena, pokus sa vytvorit (ak mas prava)

				String origDBName = setupForm.getDbName();
				setupForm.setDbName("mysql");

				try {
					if (setupForm.isDbUseSuperuser()) {
						con = DriverManager.getConnection(getDBURLString(setupForm), setupForm.getDbSuperuserUsername(), setupForm.getDbSuperuserPassword());
					} else {
						if (con != null) con.close();
						con = DriverManager.getConnection(getDBURLString(setupForm), setupForm.getDbUsername(), setupForm.getDbPassword());
					}

					PreparedStatement ps = con.prepareStatement("CREATE DATABASE " + origDBName);
					ps.execute();
					ps.close();

					con.close();

					setupForm.setDbName(origDBName);

					con = DriverManager.getConnection(getDBURLString(setupForm), setupForm.getDbUsername(), setupForm.getDbPassword());
					con.close();

					dbConnectOK = true;
				} catch (Exception e) {
					connErrMsg += msg;
					sk.iway.iwcm.Logger.error(e);
				}
				setupForm.setDbName(origDBName);
			} else if (msg.indexOf("Cannot open database requested") != -1) {
				//DB nie je vytvorena, pokus sa vytvorit (ak mas prava)

				String origDBName = setupForm.getDbName();
				setupForm.setDbName("master");

				try {
					if (setupForm.isDbUseSuperuser()) {
						if (con != null) con.close();
						con = DriverManager.getConnection(getDBURLString(setupForm), setupForm.getDbSuperuserUsername(), setupForm.getDbSuperuserPassword());
					} else {
						if (con != null) con.close();
						con = DriverManager.getConnection(getDBURLString(setupForm), setupForm.getDbUsername(), setupForm.getDbPassword());
					}

					PreparedStatement ps = con.prepareStatement("CREATE DATABASE " + origDBName);
					ps.execute();
					ps.close();

					con.close();

					setupForm.setDbName(origDBName);

					con = DriverManager.getConnection(getDBURLString(setupForm), setupForm.getDbUsername(), setupForm.getDbPassword());
					con.close();

					dbConnectOK = true;
				} catch (Exception e) {
					connErrMsg += msg;
					sk.iway.iwcm.Logger.error(e);
				}
				setupForm.setDbName(origDBName);
			} else {
				connErrMsg += msg;
				System.out.println("------ DB FAIL ------\n");
				ex.printStackTrace(System.err);
			}
		}

		System.out.println("dbConnectOK="+dbConnectOK+" driver="+setupForm.getDbDriver());

		if (dbConnectOK) {
			//resetni DBPool
			DBPool.getInstance(true);

			String dbCreateErrMsg = null;
			if ("org.mariadb.jdbc.Driver".equals(setupForm.getDbDriver())) {
				//napln databazu
				dbCreateErrMsg = UpdateDatabase.fillEmptyDatabaseMySQL();
			} else if ("oracle.jdbc.driver.OracleDriver".equals(setupForm.getDbDriver())) {
				//napln databazu
				dbCreateErrMsg = UpdateDatabase.fillEmptyDatabaseOracle();
			} else if ("org.postgresql.Driver".equals(setupForm.getDbDriver())) {
				//napln databazu
				String schema = "webjet_cms";
				if (Tools.isNotEmpty(setupForm.getDbParameters())) {
					String[] params = Tools.getTokens(setupForm.getDbParameters(), "&");
					for (String param : params) {
						if (param.startsWith("currentSchema=")) {
							schema = param.substring(14);
							break;
						}
					}
				}
				dbCreateErrMsg = UpdateDatabase.fillEmptyDatabasePgSQL(schema);
			} else {
				//	napln databazu
				dbCreateErrMsg = UpdateDatabase.fillEmptyDatabaseMSSQL();
			}

			if (Tools.isNotEmpty(dbCreateErrMsg)) {
				setModelWithErr(model, setupForm, false, null, dbCreateErrMsg);
				return FORWARD;
			}

			//uloz konfiguracne hodnoty
			Connection db_conn = null;
			try {
				db_conn = DBPool.getConnection();

				Enumeration<String> e = request.getParameterNames();
				String name;
				String value;
				while (e.hasMoreElements()) {
					name = e.nextElement();
					if (name.startsWith("conf_")) {
						value = request.getParameter(name);
						name = name.substring(5);
						if (Tools.isNotEmpty(value))
							saveConf(name, value, db_conn);
					}
				}
			} catch (Exception ex) {
				ex.printStackTrace(System.err);
			} finally {
				try {
					if (db_conn != null) db_conn.close();
				} catch (Exception ex2) {}
			}

			//nastav character coding vo web.xml a skopiruj to z runtime
			String webXML = FileTools.readFileContent("/WEB-INF/web-runtime.xml");

			if (webXML != null && webXML.length() > 100 && (data == null || data.length() < 100)) {
				webXML = Tools.replace(webXML, "<param-value>windows-1250</param-value>", "<param-value>"+setupForm.getEncoding()+"</param-value>");
				if ("net.sourceforge.jtds.jdbc.Driver".equals(setupForm.getDbDriver()))
					webXML = Tools.replace(webXML, "<param-value>TOMCAT_MYSQL</param-value>", "<param-value>RESIN_MSSQL</param-value>");

				//	prekopiruj web.xml
				FileTools.saveFileContent("/WEB-INF/web.xml", webXML);
			}

			//restartni server
			InitServlet.restart();

			//setModelForSave(model, setupForm);

			setModel(model, null, true, true);

			return SAVED;
		} else {
			setModelWithErr(model, setupForm, true, connErrMsg, null);
			return FORWARD;
		}
	}

	/**
	 * Ulozenie suboru poolman.xml
	 * @param sForm
	 */
	private static void savePoolman(SetupFormBean sForm)
	{
		StringBuilder poolman = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n")
				.append("\r\n")
				.append("<poolman>\r\n")
				.append("  <datasource>\r\n")
				.append("      <dbname>iwcm</dbname>\r\n")
				.append("      <driver>").append(sForm.getDbDriver()).append("</driver>\r\n");

		poolman.append("      <url>").append(getDBURLString(sForm)).append("</url>\r\n");

		poolman.append("\r\n")
				.append("      <username>"+sForm.getDbUsername()+"</username>\r\n")
				.append("      <password>"+sForm.getDbPassword()+"</password>\r\n")
				.append("\r\n")
				.append("      <initialConnections>2</initialConnections>\r\n")
				.append("      <minimumSize>0</minimumSize>\r\n")
				.append("      <maximumSize>60</maximumSize>\r\n")
				.append("\r\n")
				.append("      <connectionTimeout>600</connectionTimeout>\r\n")
				.append("      <userTimeout>120</userTimeout>\r\n")
				.append("\r\n")
				.append("  </datasource>\r\n")
				.append("\r\n")
				.append("</poolman>\r\n");

		FileTools.saveFileContent("/WEB-INF/classes/poolman.xml", poolman.toString());
	}

	/**
	 * Pripravi URL string pre connection (bez username a password)
	 * @param sForm
	 * @return
	 */
	private static String getDBURLString(SetupFormBean sForm) {
		String url = null;
		String port = "";
		if (Tools.isNotEmpty(sForm.getDbPort()))
			port = ":"+sForm.getDbPort();

		if ("com.mysql.jdbc.Driver".equals(sForm.getDbDriver())) {
			url = "jdbc:mysql://"+sForm.getDbDomain()+port+"/"+sForm.getDbName()+"?"+sForm.getDbParameters();
		} else if ("org.mariadb.jdbc.Driver".equals(sForm.getDbDriver())) {
			url = "jdbc:mariadb://"+sForm.getDbDomain()+port+"/"+sForm.getDbName()+"?"+sForm.getDbParameters();
		} else if ("net.sourceforge.jtds.jdbc.Driver".equals(sForm.getDbDriver())) {
			url = "jdbc:jtds:sqlserver://"+sForm.getDbDomain()+port+"/"+sForm.getDbName()+";"+sForm.getDbParameters();
		} else if ("oracle.jdbc.driver.OracleDriver".equals(sForm.getDbDriver())) {
			url = "jdbc:oracle:thin:@"+sForm.getDbDomain()+port;
			if (Tools.isNotEmpty(sForm.getDbName()))
				url += ":"+sForm.getDbName();

			if (Tools.isNotEmpty(sForm.getDbParameters()))
				url += "/"+sForm.getDbParameters();
		} else if ("org.postgresql.Driver".equals(sForm.getDbDriver())) {
			String params = "currentSchema=webjet_cms";
			if (Tools.isNotEmpty(sForm.getDbParameters())) params = sForm.getDbParameters();
			url = "jdbc:postgresql://"+sForm.getDbDomain()+port+"/"+sForm.getDbName()+"?"+params;
		}
		return(url);
	}

	/**
	 * Ulozenie hodnoty do konfiguracnej tabulky
	 * @param name
	 * @param value
	 * @param db_conn
	 * @throws SQLException
	 */
	private static void saveConf(String name, String value, Connection db_conn) throws SQLException {
		PreparedStatement ps = db_conn.prepareStatement("DELETE FROM "+ConfDB.CONF_TABLE_NAME+" WHERE name=?");
		ps.setString(1, name);
		ps.execute();
		ps.close();

		ps = db_conn.prepareStatement("INSERT INTO "+ConfDB.CONF_TABLE_NAME+" (name, value) VALUES (?, ?)");
		ps.setString(1, name);
		ps.setString(2, value);
		ps.execute();
		ps.close();
	}

	private static void setModel(Model model, SetupFormBean setupForm, Boolean disableLanguageSelect, boolean isSave) {
		//Informing FE what key to use, when creating page
		if(isSave)
			// page /admin/setup/setup
			model.addAttribute("isSetupSave", true);
		else
		// page /admin/setup/setup_saved
			model.addAttribute("isSetup", true);

		//Object that will be used in filling setup form
		if(setupForm != null) model.addAttribute("setupForm", setupForm);
		//Style file content for page
		model.addAttribute("cmpCss", FileTools.readFileContent("/components/cmp.css"));
		//If true, user will not see select to change language
		model.addAttribute("disableLng", disableLanguageSelect);
	}

	private static void setModelWithErr(Model model, SetupFormBean setupForm, Boolean conErr, String conErrMsg, String createErrMsg) {
		setModel(model, setupForm, true, false);

		//Will show con error msg in page
		model.addAttribute("dbConnFail", conErr);
		//This is content of con err msg
		model.addAttribute("dbErrMsg", conErrMsg);
		//Separe crate rr message, will be shown if != null
		model.addAttribute("dbCreateErrMsg", createErrMsg);
	}
}