001/*
002 * File   : $Source$
003 * Date   : $Date$
004 * Version: $Revision$
005 *
006 * This library is part of OpenCms -
007 * the Open Source Content Management System
008 *
009 * Copyright (C) 2002 - 2009 Alkacon Software (http://www.alkacon.com)
010 *
011 * This library is free software; you can redistribute it and/or
012 * modify it under the terms of the GNU Lesser General Public
013 * License as published by the Free Software Foundation; either
014 * version 2.1 of the License, or (at your option) any later version.
015 *
016 * This library is distributed in the hope that it will be useful,
017 * but WITHOUT ANY WARRANTY; without even the implied warranty of
018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019 * Lesser General Public License for more details.
020 *
021 * For further information about Alkacon Software, please see the
022 * company website: http://www.alkacon.com
023 *
024 * For further information about OpenCms, please see the
025 * project website: http://www.opencms.org
026 *
027 * You should have received a copy of the GNU Lesser General Public
028 * License along with this library; if not, write to the Free Software
029 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
030 */
031
032package org.opencms.ui.apps.sitemanager;
033
034import org.opencms.file.CmsObject;
035import org.opencms.letsencrypt.CmsLetsEncryptConfiguration;
036import org.opencms.letsencrypt.CmsSiteConfigToLetsEncryptConfigConverter;
037import org.opencms.main.CmsLog;
038import org.opencms.main.OpenCms;
039import org.opencms.report.A_CmsReportThread;
040import org.opencms.report.I_CmsReport;
041import org.opencms.site.CmsSite;
042import org.opencms.site.CmsSiteMatcher;
043import org.opencms.ui.apps.Messages;
044import org.opencms.util.CmsStringUtil;
045
046import java.io.BufferedReader;
047import java.io.File;
048import java.io.FilenameFilter;
049import java.io.IOException;
050import java.io.InputStreamReader;
051import java.util.ArrayList;
052import java.util.LinkedList;
053import java.util.List;
054
055import org.apache.commons.io.FileUtils;
056import org.apache.commons.logging.Log;
057
058import org.antlr.stringtemplate.StringTemplate;
059
060/**
061 * Executes a script file.<p>
062 *
063 * @since 9.0.0
064 */
065public class CmsSitesWebserverThread extends A_CmsReportThread {
066
067    /** The logger for this class. */
068    static Log LOG = CmsLog.getLog(CmsSitesWebserverThread.class.getName());
069
070    /** Constant for the "http" port. */
071    private static final int PORT_HTTP = 80;
072
073    /** Constant for the "https" port. */
074    private static final int PORT_HTTPS = 443;
075
076    /** The file path. */
077    private String m_filePrefix;
078
079    /** The logging directory. */
080    private String m_loggingDir;
081
082    /** The script path. */
083    private String m_scriptPath;
084
085    /** The template to be used for secure site configurations. */
086    private String m_secureTemplate;
087
088    /** The target path. */
089    private String m_targetPath;
090
091    /** The template path. */
092    private String m_templatePath;
093
094    /** The files that have been written. */
095    private List<String> m_writtenFiles = new ArrayList<String>();
096
097    /**
098     * Public constructor.<p>
099     *
100     * @param cms the cms object
101     * @param targetPath the target path
102     * @param templatePath the template path
103     * @param scriptPath the script path
104     * @param filePrefix the filename prefix
105     * @param loggingDir the logging directory
106     * @param secureTemplate the secure template
107     */
108    public CmsSitesWebserverThread(
109        CmsObject cms,
110        String targetPath,
111        String templatePath,
112        String scriptPath,
113        String filePrefix,
114        String loggingDir,
115        String secureTemplate) {
116
117        super(cms, "write-to-webserver");
118
119        if (targetPath != null) {
120            m_targetPath = targetPath.endsWith(File.separator) ? targetPath : targetPath + File.separator;
121        }
122        m_templatePath = templatePath;
123        m_scriptPath = scriptPath;
124        m_filePrefix = filePrefix;
125        m_loggingDir = loggingDir;
126        m_secureTemplate = secureTemplate;
127        initHtmlReport(cms.getRequestContext().getLocale());
128    }
129
130    /**
131     * @see org.opencms.report.A_CmsReportThread#getReportUpdate()
132     */
133    @Override
134    public String getReportUpdate() {
135
136        return getReport().getReportUpdate();
137    }
138
139    /**
140     * @see java.lang.Thread#run()
141     */
142    @Override
143    public void run() {
144
145        LOG.info(
146            "INFO thread for run of server script started by User: "
147                + getCms().getRequestContext().getCurrentUser().getName());
148        if (CmsSiteManager.isLetsEncryptConfiguredForWebserverThread()) {
149            updateLetsEncrypt();
150        } else {
151            try {
152                deleteAllWebserverConfigs(m_filePrefix);
153                createAllWebserverConfigs();
154                executeScript();
155                LOG.info("INFO server script finished successfully");
156            } catch (Exception e) {
157                LOG.error("Exception on run CmsSitesWebserverThread", e);
158                getReport().println(e);
159            }
160            LOG.info("INFO server script thread closed");
161        }
162    }
163
164    /**
165     * Creates the new web server configuration files from the given template file.<p>
166     *
167     * @throws IOException if something goes wrong
168     */
169    private void createAllWebserverConfigs() throws IOException {
170
171        List<CmsSite> sites = OpenCms.getSiteManager().getAvailableSites(getCms(), true);
172        for (CmsSite site : sites) {
173            if ((site.getSiteMatcher() != null) && site.isWebserver()) {
174
175                String filename = m_targetPath
176                    + m_filePrefix
177                    + "_"
178                    + generateWebserverConfigName(site.getSiteMatcher(), "_");
179                getReport().println(
180                    Messages.get().container(Messages.RPT_CREATING_CONFIG_FOR_SITE_2, filename, site),
181                    I_CmsReport.FORMAT_OK);
182                File newFile = new File(filename);
183                if (!newFile.exists()) {
184                    newFile.getParentFile().mkdirs();
185                    newFile.createNewFile();
186                }
187
188                String content = createConfigForSite(site, FileUtils.readFileToString(new File(m_templatePath)));
189                if (site.hasSecureServer()) {
190                    content += createConfigForSite(site, FileUtils.readFileToString(new File(m_secureTemplate)));
191                }
192
193                FileUtils.writeStringToFile(newFile, content);
194                m_writtenFiles.add(newFile.getAbsolutePath());
195            }
196        }
197    }
198
199    /**
200     * Performs the template handling and returns the content.<p>
201     *
202     * @param site the site
203     * @param templateContent the configuration template content
204     *
205     * @return the file content for the configuration as String
206     */
207    private String createConfigForSite(CmsSite site, String templateContent) {
208
209        StringTemplate config = new StringTemplate(templateContent);
210
211        // system info
212        String webappPath = OpenCms.getSystemInfo().getWebApplicationRfsPath();
213        config.setAttribute("DOCUMENT_ROOT", webappPath.substring(0, webappPath.length() - 1));
214        config.setAttribute("WEBAPP_NAME", OpenCms.getSystemInfo().getWebApplicationName());
215        config.setAttribute("CONTEXT_PATH", OpenCms.getSystemInfo().getContextPath());
216        config.setAttribute("SERVLET_PATH", OpenCms.getSystemInfo().getServletPath());
217        config.setAttribute("DEFAULT_ENCODING", OpenCms.getSystemInfo().getDefaultEncoding());
218        config.setAttribute("CONFIG_FILENAME", generateWebserverConfigName(site.getSiteMatcher(), "_"));
219        config.setAttribute("LOGGING_DIRECTORY", m_loggingDir);
220
221        // site info
222        config.setAttribute("SERVER_URL", site.getUrl());
223        config.setAttribute("SERVER_PROTOCOL", site.getSiteMatcher().getServerProtocol());
224        config.setAttribute("SERVER_NAME", site.getSiteMatcher().getServerName());
225        config.setAttribute("SERVER_PORT", site.getSiteMatcher().getServerPort());
226        config.setAttribute("SERVER_NAME_WITH_PORT", generateWebserverConfigName(site.getSiteMatcher(), ":"));
227        config.setAttribute("SITE_TITLE", site.getTitle());
228        if (site.getErrorPage() != null) {
229            config.setAttribute("ERROR_PAGE", site.getErrorPage());
230        }
231
232        // alias info
233        if ((site.getAliases() != null) && !site.getAliases().isEmpty()) {
234            config.setAttribute("ALIAS_DIRECTIVE", "ServerAlias");
235            for (CmsSiteMatcher alias : site.getAliases()) {
236                config.setAttribute("SERVER_ALIASES", generateWebserverConfigName(alias, ":") + " ");
237            }
238        }
239
240        // secure info
241        if (site.hasSecureServer()) {
242            if (site.getSecureUrl() != null) {
243                config.setAttribute("SECURE_URL", site.getSecureUrl());
244            }
245            if (site.getSecureServer() != null) {
246                config.setAttribute("SECURE_SRV_WITH_PORT", generateWebserverConfigName(site.getSecureServer(), ":"));
247                config.setAttribute("SECURE_SERVER_NAME", site.getSecureServer().getServerName());
248                config.setAttribute("SECURE_SERVER_PORT", site.getSecureServer().getServerPort());
249                config.setAttribute("SECURE_SERVER_PROTOCOL", site.getSecureServer().getServerProtocol());
250            }
251        }
252        return config.toString();
253    }
254
255    /**
256     * Deletes all web server's configuration files with the given prefix.<p>
257     *
258     * @param prefix a prefix used for the webserver configuration files
259     */
260    private void deleteAllWebserverConfigs(final String prefix) {
261
262        File file = new File(m_targetPath);
263        if (file.exists() && file.isDirectory()) {
264            File[] configFiles = file.listFiles(new FilenameFilter() {
265
266                /**
267                 * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
268                 */
269                public boolean accept(File dir, String name) {
270
271                    if (name.startsWith(prefix)) {
272                        return true;
273                    }
274                    return false;
275                }
276            });
277            for (File f : configFiles) {
278                getReport().println(Messages.get().container(Messages.RPT_DELETING_FILE_1, f), I_CmsReport.FORMAT_OK);
279                f.delete();
280            }
281        }
282    }
283
284    /**
285     * Executes the webserver script.<p>
286     *
287     * @throws IOException if something goes wrong
288     * @throws InterruptedException if something goes wrong
289     */
290    private void executeScript() throws IOException, InterruptedException {
291
292        File script = new File(m_scriptPath);
293        List<String> params = new LinkedList<String>();
294        params.add(script.getAbsolutePath());
295        params.addAll(m_writtenFiles);
296        ProcessBuilder pb = new ProcessBuilder(params.toArray(new String[params.size()]));
297        pb.directory(new File(script.getParent()));
298        Process pr = pb.start();
299        pr.waitFor();
300        BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream()));
301        while (buf.ready()) {
302            String line = buf.readLine();
303            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(line)) {
304                getReport().println(
305                    Messages.get().container(Messages.RPT_OUTPUT_WEBSERVER_1, buf.readLine()),
306                    I_CmsReport.FORMAT_OK);
307            }
308        }
309    }
310
311    /**
312     * Generates the web server configuration filename for the given site.<p>
313     *
314     * @param macther the site matcher of the site to get the web server configuration filename for
315     * @param separator for the generated file name
316     *
317     * @return the web server configuration filename
318     */
319    private String generateWebserverConfigName(CmsSiteMatcher macther, String separator) {
320
321        int port = macther.getServerPort();
322        String serverName = macther.getServerName();
323        String portPart = ((port != PORT_HTTP) && (port != PORT_HTTPS)) ? separator + port : "";
324        return serverName + portPart;
325    }
326
327    /**
328     * Updates LetsEncrypt configuration.
329     */
330    private void updateLetsEncrypt() {
331
332        getReport().println(Messages.get().container(Messages.RPT_STARTING_LETSENCRYPT_UPDATE_0));
333
334        CmsLetsEncryptConfiguration config = OpenCms.getLetsEncryptConfig();
335        if ((config == null) || !config.isValidAndEnabled()) {
336            return;
337        }
338        CmsSiteConfigToLetsEncryptConfigConverter converter = new CmsSiteConfigToLetsEncryptConfigConverter(config);
339        boolean ok = converter.run(getReport(), OpenCms.getSiteManager());
340        if (ok) {
341            getReport().println(
342                org.opencms.ui.apps.Messages.get().container(org.opencms.ui.apps.Messages.RPT_LETSENCRYPT_FINISHED_0),
343                I_CmsReport.FORMAT_OK);
344        }
345
346    }
347}