content: /*
   * File   : $Source$
   * Date   : $Date$
   * Version: $Revision$
   *
   * This library is part of OpenCms -
   * the Open Source Content Management System
   *
   * Copyright (C) 2002 - 2009 Alkacon Software (https://www.alkacon.com)
   *
   * 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.
   *
   * For further information about Alkacon Software, please see the
   * company website: https://www.alkacon.com
   *
   * For further information about OpenCms, please see the
   * project website: https://www.opencms.org
   *
   * 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
   */
  
  package org.opencms.ui.apps.sitemanager;
  
  import org.opencms.file.CmsObject;
  import org.opencms.letsencrypt.CmsLetsEncryptConfiguration;
  import org.opencms.letsencrypt.CmsSiteConfigToLetsEncryptConfigConverter;
  import org.opencms.main.CmsLog;
  import org.opencms.main.OpenCms;
  import org.opencms.report.A_CmsReportThread;
  import org.opencms.report.I_CmsReport;
  import org.opencms.site.CmsSite;
  import org.opencms.site.CmsSiteMatcher;
  import org.opencms.ui.apps.Messages;
  import org.opencms.util.CmsStringUtil;
  
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.FilenameFilter;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.util.ArrayList;
  import java.util.LinkedList;
  import java.util.List;
  
  import org.apache.commons.io.FileUtils;
  import org.apache.commons.logging.Log;
  
  import org.antlr.stringtemplate.StringTemplate;
  
  /**
   * Executes a script file.<p>
   *
   * @since 9.0.0
   */
  public class CmsSitesWebserverThread extends A_CmsReportThread {
  
      /** The logger for this class. */
      static Log LOG = CmsLog.getLog(CmsSitesWebserverThread.class.getName());
  
      /** Constant for the "http" port. */
      private static final int PORT_HTTP = 80;
  
      /** Constant for the "https" port. */
      private static final int PORT_HTTPS = 443;
  
      /** The file path. */
      private String m_filePrefix;
  
      /** The logging directory. */
      private String m_loggingDir;
  
      /** The script path. */
      private String m_scriptPath;
  
      /** The template to be used for secure site configurations. */
      private String m_secureTemplate;
  
      /** The target path. */
      private String m_targetPath;
  
      /** The template path. */
      private String m_templatePath;
  
      /** The files that have been written. */
      private List<String> m_writtenFiles = new ArrayList<String>();
  
      /**
       * Public constructor.<p>
       *
       * @param cms the cms object
       * @param targetPath the target path
       * @param templatePath the template path
       * @param scriptPath the script path
       * @param filePrefix the filename prefix
       * @param loggingDir the logging directory
       * @param secureTemplate the secure template
       */
      public CmsSitesWebserverThread(
          CmsObject cms,
          String targetPath,
          String templatePath,
          String scriptPath,
          String filePrefix,
          String loggingDir,
          String secureTemplate) {
  
          super(cms, "write-to-webserver");
  
          if (targetPath != null) {
              m_targetPath = targetPath.endsWith(File.separator) ? targetPath : targetPath + File.separator;
          }
          m_templatePath = templatePath;
          m_scriptPath = scriptPath;
          m_filePrefix = filePrefix;
          m_loggingDir = loggingDir;
          m_secureTemplate = secureTemplate;
          initHtmlReport(cms.getRequestContext().getLocale());
      }
  
      /**
       * @see org.opencms.report.A_CmsReportThread#getReportUpdate()
       */
      @Override
      public String getReportUpdate() {
  
          return getReport().getReportUpdate();
      }
  
      /**
       * @see java.lang.Thread#run()
       */
      @Override
      public void run() {
  
          LOG.info(
              "INFO thread for run of server script started by User: "
                  + getCms().getRequestContext().getCurrentUser().getName());
          if (CmsSiteManager.isLetsEncryptConfiguredForWebserverThread()) {
              updateLetsEncrypt();
          } else {
              try {
                  deleteAllWebserverConfigs(m_filePrefix);
                  createAllWebserverConfigs();
                  executeScript();
                  LOG.info("INFO server script finished successfully");
              } catch (Exception e) {
                  LOG.error("Exception on run CmsSitesWebserverThread", e);
                  getReport().println(e);
              }
              LOG.info("INFO server script thread closed");
          }
      }
  
      /**
       * Creates the new web server configuration files from the given template file.<p>
       *
       * @throws IOException if something goes wrong
       */
      private void createAllWebserverConfigs() throws IOException {
  
          List<CmsSite> sites = OpenCms.getSiteManager().getAvailableSites(getCms(), true);
          for (CmsSite site : sites) {
              if ((site.getSiteMatcher() != null) && site.isWebserver()) {
  
                  String filename = m_targetPath
                      + m_filePrefix
                      + "_"
                      + generateWebserverConfigName(site.getSiteMatcher(), "_");
                  getReport().println(
                      Messages.get().container(Messages.RPT_CREATING_CONFIG_FOR_SITE_2, filename, site),
                      I_CmsReport.FORMAT_OK);
                  File newFile = new File(filename);
                  if (!newFile.exists()) {
                      newFile.getParentFile().mkdirs();
                      newFile.createNewFile();
                  }
  
                  String content = createConfigForSite(site, FileUtils.readFileToString(new File(m_templatePath)));
                  if (site.hasSecureServer()) {
                      content += createConfigForSite(site, FileUtils.readFileToString(new File(m_secureTemplate)));
                  }
  
                  FileUtils.writeStringToFile(newFile, content);
                  m_writtenFiles.add(newFile.getAbsolutePath());
              }
          }
      }
  
      /**
       * Performs the template handling and returns the content.<p>
       *
       * @param site the site
       * @param templateContent the configuration template content
       *
       * @return the file content for the configuration as String
       */
      private String createConfigForSite(CmsSite site, String templateContent) {
  
          StringTemplate config = new StringTemplate(templateContent);
  
          // system info
          String webappPath = OpenCms.getSystemInfo().getWebApplicationRfsPath();
          config.setAttribute("DOCUMENT_ROOT", webappPath.substring(0, webappPath.length() - 1));
          config.setAttribute("WEBAPP_NAME", OpenCms.getSystemInfo().getWebApplicationName());
          config.setAttribute("CONTEXT_PATH", OpenCms.getSystemInfo().getContextPath());
          config.setAttribute("SERVLET_PATH", OpenCms.getSystemInfo().getServletPath());
          config.setAttribute("DEFAULT_ENCODING", OpenCms.getSystemInfo().getDefaultEncoding());
          config.setAttribute("CONFIG_FILENAME", generateWebserverConfigName(site.getSiteMatcher(), "_"));
          config.setAttribute("LOGGING_DIRECTORY", m_loggingDir);
  
          // site info
          config.setAttribute("SERVER_URL", site.getUrl());
          config.setAttribute("SERVER_PROTOCOL", site.getSiteMatcher().getServerProtocol());
          config.setAttribute("SERVER_NAME", site.getSiteMatcher().getServerName());
          config.setAttribute("SERVER_PORT", site.getSiteMatcher().getServerPort());
          config.setAttribute("SERVER_NAME_WITH_PORT", generateWebserverConfigName(site.getSiteMatcher(), ":"));
          config.setAttribute("SITE_TITLE", site.getTitle());
          if (site.getErrorPage() != null) {
              config.setAttribute("ERROR_PAGE", site.getErrorPage());
          }
  
          // alias info
          if ((site.getAliases() != null) && !site.getAliases().isEmpty()) {
              config.setAttribute("ALIAS_DIRECTIVE", "ServerAlias");
              for (CmsSiteMatcher alias : site.getAliases()) {
                  config.setAttribute("SERVER_ALIASES", generateWebserverConfigName(alias, ":") + " ");
              }
          }
  
          // secure info
          if (site.hasSecureServer()) {
              if (site.getSecureUrl() != null) {
                  config.setAttribute("SECURE_URL", site.getSecureUrl());
              }
              if (site.getSecureServer() != null) {
                  config.setAttribute("SECURE_SRV_WITH_PORT", generateWebserverConfigName(site.getSecureServer(), ":"));
                  config.setAttribute("SECURE_SERVER_NAME", site.getSecureServer().getServerName());
                  config.setAttribute("SECURE_SERVER_PORT", site.getSecureServer().getServerPort());
                  config.setAttribute("SECURE_SERVER_PROTOCOL", site.getSecureServer().getServerProtocol());
              }
          }
          return config.toString();
      }
  
      /**
       * Deletes all web server's configuration files with the given prefix.<p>
       *
       * @param prefix a prefix used for the webserver configuration files
       */
      private void deleteAllWebserverConfigs(final String prefix) {
  
          File file = new File(m_targetPath);
          if (file.exists() && file.isDirectory()) {
              File[] configFiles = file.listFiles(new FilenameFilter() {
  
                  /**
                   * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
                   */
                  public boolean accept(File dir, String name) {
  
                      if (name.startsWith(prefix)) {
                          return true;
                      }
                      return false;
                  }
              });
              for (File f : configFiles) {
                  getReport().println(Messages.get().container(Messages.RPT_DELETING_FILE_1, f), I_CmsReport.FORMAT_OK);
                  f.delete();
              }
          }
      }
  
      /**
       * Executes the webserver script.<p>
       *
       * @throws IOException if something goes wrong
       * @throws InterruptedException if something goes wrong
       */
      private void executeScript() throws IOException, InterruptedException {
  
          File script = new File(m_scriptPath);
          List<String> params = new LinkedList<String>();
          params.add(script.getAbsolutePath());
          params.addAll(m_writtenFiles);
          ProcessBuilder pb = new ProcessBuilder(params.toArray(new String[params.size()]));
          pb.directory(new File(script.getParent()));
          Process pr = pb.start();
          pr.waitFor();
          BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream()));
          while (buf.ready()) {
              String line = buf.readLine();
              if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(line)) {
                  getReport().println(
                      Messages.get().container(Messages.RPT_OUTPUT_WEBSERVER_1, buf.readLine()),
                      I_CmsReport.FORMAT_OK);
              }
          }
      }
  
      /**
       * Generates the web server configuration filename for the given site.<p>
       *
       * @param macther the site matcher of the site to get the web server configuration filename for
       * @param separator for the generated file name
       *
       * @return the web server configuration filename
       */
      private String generateWebserverConfigName(CmsSiteMatcher macther, String separator) {
  
          int port = macther.getServerPort();
          String serverName = macther.getServerName();
          String portPart = ((port != PORT_HTTP) && (port != PORT_HTTPS)) ? separator + port : "";
          return serverName + portPart;
      }
  
      /**
       * Updates LetsEncrypt configuration.
       */
      private void updateLetsEncrypt() {
  
          getReport().println(Messages.get().container(Messages.RPT_STARTING_LETSENCRYPT_UPDATE_0));
  
          CmsLetsEncryptConfiguration config = OpenCms.getLetsEncryptConfig();
          if ((config == null) || !config.isValidAndEnabled()) {
              return;
          }
          CmsSiteConfigToLetsEncryptConfigConverter converter = new CmsSiteConfigToLetsEncryptConfigConverter(config);
          boolean ok = converter.run(getReport(), OpenCms.getSiteManager());
          if (ok) {
              getReport().println(
                  org.opencms.ui.apps.Messages.get().container(org.opencms.ui.apps.Messages.RPT_LETSENCRYPT_FINISHED_0),
                  I_CmsReport.FORMAT_OK);
          }
  
      }
  }
  