<commit_before>/*
 * CollabNet Subversion Edge
 * Copyright (C) 2010, CollabNet Inc. All rights reserved.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.collabnet.svnedge.console.services

import org.apache.commons.io.FileUtils
import org.codehaus.groovy.grails.commons.ConfigurationHolder

import com.collabnet.svnedge.console.ConfigUtil
import com.collabnet.svnedge.console.Repository
import com.collabnet.svnedge.console.Server
import com.collabnet.svnedge.console.ServerMode
import com.collabnet.svnedge.console.security.User
import com.collabnet.svnedge.replica.manager.Master
import com.collabnet.svnedge.teamforge.CtfServer

import grails.util.GrailsUtil

import java.util.Calendar
import com.collabnet.svnedge.console.services.LogManagementService.ApacheLogLevel

class ServerConfService {

    boolean transactional = true
    def commandLineService
    def securityService
    def networkingService
    
    private static final String DONT_EDIT = """#
#
# DO NOT EDIT THIS FILE IT WILL BE REGENERATED AUTOMATICALLY BY COLLABNET SUBVERSION
#
#
"""
    private static final String DEFAULT_SVN_ACCESS = """#
#
# DEFAULT ACCESS PERMISSIONS GRANTING ALL USERS FULL CONTROL OF ALL REPOS
#
[/]
* = rw
"""

    private static final String CTF_AUTHORIZER = """
[authz-teamforge]
"""
    
    def appHome
    private def svnPath
    def httpdUser
    def httpdGroup
    def confDirPath
    def distDirPath
    def modPythonDirPath
    def viewvcLibDirPath
    private def viewvcTemplateDirPath
    private def dataDirPath
    private def winServiceName

    def bootstrap = { config, server ->
        appHome = new File(ConfigUtil.appHome(config)).absolutePath
        confDirPath = new File(ConfigUtil.confDirPath(config)).absolutePath

        log.debug("CSVN_HOME will be " + appHome)
        log.debug("Bootstrapping serverConfService")

        log.debug("Setting up data artifacts directory...")
        bootstrapDataDirectory(ConfigUtil.appHome(config))
        bootstrapConfiguration()

        svnPath = new File(ConfigUtil.svnPath(config)).absolutePath
        viewvcLibDirPath = new File(
            ConfigUtil.viewvcLibPath(config)).absolutePath
        modPythonDirPath = new File(
            ConfigUtil.modPythonPath(config)).absolutePath
        distDirPath = new File(ConfigUtil.distDir(config)).absolutePath
        viewvcTemplateDirPath = new File(
            ConfigUtil.viewvcTemplateDir(config)).absolutePath

        // serviceName should only be set for windows
        def serviceName = ConfigUtil.serviceName(config)
        if (null != serviceName) {
            winServiceName = serviceName
        } else {
            // user and group aren't relevant on windows (yet)
            setUserAndGroup()
        }
        dataDirPath = new File(ConfigUtil.dataDirPath(config)).absolutePath
        conditionalWriteHttpdConf()
        deployPythonBindings()

        try {
            this.writeConfigFiles()
        } catch (Exception e) {
            log.error("Unable to write initial set of configuration " +
                "files", e)
        }
    }
    
    private void bootstrapConfiguration() {
        // remove unnecessary scm artifact
        switch(GrailsUtil.environment) {
            case "development":
            case "test":
                log.debug("Deleting older httpd.conf file")
                new File(confDirPath, "httpd.conf").delete()

                File scmProps = new File(server.repoParentDir,".scm.properties")
                if (scmProps.exists() && scmProps.delete()) {
                    log.debug("Removed previous .scm.properties file")
                }
                break
        }
    }

    /**
     * Bootstraps the temp-data director to data moving the data directory as 
     * discussed on artf62798
     * @param config is the configuration properties
     */
    private def bootstrapDataDirectory(appHome) {
        def tempDataDir = new File(appHome, "temp-data")
        if (tempDataDir.exists()) {
            log.info("Bootstrapping the temporary data directory...")
            def dataDir = new File(appHome, "data")
            //The data exists, it's a possible update. Copy each
            //existing ones, maintaining the version .new-1, .new-2, ...
            boolean recur = true
            def allTmpFiles = FileUtils.listFiles(tempDataDir, null, recur)
            for (fileOnTemp in allTmpFiles) {
                //only returns file names, no dirs and subdirs
                def fileOnDataPath = fileOnTemp.canonicalPath.replace(
                        "temp-data", "data")
                def fileOnDataDir = new File(fileOnDataPath)
                if (!fileOnDataDir.exists()) {
                    def parentDir = fileOnDataDir.getParentFile()
                    //create all dirs and subdirs for the file.
                    parentDir.mkdirs()
                    fileOnTemp.renameTo(fileOnDataDir)
                } else {
                    //skip if it's a directory, but rename the file.
                    if (!fileOnTemp.isDirectory()) {
                        def bpkFileOnData = new File(fileOnDataDir, ".bkp")
                        if (!bpkFileOnData.exists()) {
                            fileOnDataDir.renameTo(bpkFileOnData)
                        } else {
                            int numberOfNewFile = 1
                            //gets the name of the next "new" artifact n
                            def nextBkpFile = null;
                            while((nextBkpFile = new File(fileOnDataDir, 
                                ".bkp" + (++numberOfNewFile))).exists());
                            fileOnDataDir.renameTo(nextBkpFile)
                        }
                        // move the file from the temp dir and the data dir.
                        fileOnTemp.renameTo(fileOnDataDir)
                    }
                }
            }
            //delete the temp-data's empty dirs and subdirs
            FileUtils.deleteDirectory(tempDataDir)
        }
    }

    private boolean isWindows() {
        return null != winServiceName
    }
    
    /**
     * Will run httpd as owner of the repository directory as httpd will 
     * write to it.
     * Might want to eventually add configuration for this.
     */
    private def setUserAndGroup() {
        def server = getServer()
        def repoPath = server ? (String) server.repoParentDir :
            (String) ConfigurationHolder.config.svnedge.svn.repositoriesParentPath
        //Sometimes ls -ld output coloumns are separated by double space.
        //For ex drwxr-xr-x  7 rajeswari __cubitu 4096 May 14 01:45 data/
        String[] result = commandLineService
            .executeWithOutput("ls", "-dl", repoPath).replaceAll(" +", " ").split(" ")

        httpdUser = result.length > 2 ? result[2] : "nobody"
        httpdGroup = result.length > 3 ? result[3] : "nobody"<commit_msg>[artf4935] Fixing the bug in the previous revision, where the name of the backup file was actually created as a directory. This revision also prints a message to the user about which files were backed up, and the new updates:<commit_after>/*
 * CollabNet Subversion Edge
 * Copyright (C) 2010, CollabNet Inc. All rights reserved.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.collabnet.svnedge.console.services

import org.apache.commons.io.FileUtils
import org.codehaus.groovy.grails.commons.ConfigurationHolder

import com.collabnet.svnedge.console.ConfigUtil
import com.collabnet.svnedge.console.Repository
import com.collabnet.svnedge.console.Server
import com.collabnet.svnedge.console.ServerMode
import com.collabnet.svnedge.console.security.User
import com.collabnet.svnedge.replica.manager.Master
import com.collabnet.svnedge.teamforge.CtfServer

import grails.util.GrailsUtil

import java.util.Calendar
import com.collabnet.svnedge.console.services.LogManagementService.ApacheLogLevel

class ServerConfService {

    boolean transactional = true
    def commandLineService
    def securityService
    def networkingService
    
    private static final String DONT_EDIT = """#
#
# DO NOT EDIT THIS FILE IT WILL BE REGENERATED AUTOMATICALLY BY COLLABNET SUBVERSION
#
#
"""
    private static final String DEFAULT_SVN_ACCESS = """#
#
# DEFAULT ACCESS PERMISSIONS GRANTING ALL USERS FULL CONTROL OF ALL REPOS
#
[/]
* = rw
"""

    private static final String CTF_AUTHORIZER = """
[authz-teamforge]
"""
    
    def appHome
    private def svnPath
    def httpdUser
    def httpdGroup
    def confDirPath
    def distDirPath
    def modPythonDirPath
    def viewvcLibDirPath
    private def viewvcTemplateDirPath
    private def dataDirPath
    private def winServiceName

    def bootstrap = { config, server ->
        appHome = new File(ConfigUtil.appHome(config)).absolutePath
        confDirPath = new File(ConfigUtil.confDirPath(config)).absolutePath

        log.debug("CSVN_HOME will be " + appHome)
        log.debug("Bootstrapping serverConfService")

        log.debug("Setting up data artifacts directory...")
        bootstrapDataDirectory(ConfigUtil.appHome(config))
        bootstrapConfiguration()

        svnPath = new File(ConfigUtil.svnPath(config)).absolutePath
        viewvcLibDirPath = new File(
            ConfigUtil.viewvcLibPath(config)).absolutePath
        modPythonDirPath = new File(
            ConfigUtil.modPythonPath(config)).absolutePath
        distDirPath = new File(ConfigUtil.distDir(config)).absolutePath
        viewvcTemplateDirPath = new File(
            ConfigUtil.viewvcTemplateDir(config)).absolutePath

        // serviceName should only be set for windows
        def serviceName = ConfigUtil.serviceName(config)
        if (null != serviceName) {
            winServiceName = serviceName
        } else {
            // user and group aren't relevant on windows (yet)
            setUserAndGroup()
        }
        dataDirPath = new File(ConfigUtil.dataDirPath(config)).absolutePath
        conditionalWriteHttpdConf()
        deployPythonBindings()

        try {
            this.writeConfigFiles()
        } catch (Exception e) {
            log.error("Unable to write initial set of configuration " +
                "files", e)
        }
    }
    
    private void bootstrapConfiguration() {
        // remove unnecessary scm artifact
        switch(GrailsUtil.environment) {
            case "development":
            case "test":
                log.debug("Deleting older httpd.conf file")
                new File(confDirPath, "httpd.conf").delete()

                File scmProps = new File(server.repoParentDir,".scm.properties")
                if (scmProps.exists() && scmProps.delete()) {
                    log.debug("Removed previous .scm.properties file")
                }
                break
        }
    }

    /**
     * Bootstraps the temp-data director to data moving the data directory as 
     * discussed on artf62798
     * @param config is the configuration properties
     */
    def bootstrapDataDirectory(appHome) {
        def tempDataDir = new File(appHome, "temp-data")
        if (tempDataDir.exists()) {
            log.info("Bootstrapping the temporary data directory...")
            def dataDir = new File(appHome, "data")
            //The data exists, it's a possible update. Copy each
            //existing ones, maintaining the version .new-1, .new-2, ...
            boolean recur = true
            def allTmpFiles = FileUtils.listFiles(tempDataDir, null, recur)
            for (fileOnTemp in allTmpFiles) {
                //only returns file names, no dirs and subdirs
                def fileOnDataPath = fileOnTemp.canonicalPath.replace(
                        "temp-data", "data")
                def fileOnDataDir = new File(fileOnDataPath)
                if (!fileOnDataDir.exists()) {
                    def parentDir = fileOnDataDir.getParentFile()
                    //create all dirs and subdirs for the file.
                    parentDir.mkdirs()
                    fileOnTemp.renameTo(fileOnDataDir)
                } else {
                    //skip if it's a directory, but rename the file.
                    if (!fileOnTemp.isDirectory()) {
                        def bpkFileOnData = new File(fileOnDataDir.canonicalPath
                            + ".bkp")
                        if (!bpkFileOnData.exists()) {
                            fileOnDataDir.renameTo(bpkFileOnData)
                            log.info("Backing up \"${fileOnDataDir}\" as " + 
                                "\"${bpkFileOnData}\".")
                        } else {
                            int numberOfNewFile = 1
                            //gets the name of the next "new" artifact n
                            def nextBkpFile = null;
                            while((nextBkpFile = new File(
                                fileOnDataDir.canonicalPath + ".bkp" + 
                                (++numberOfNewFile))).exists())
                            fileOnDataDir.renameTo(nextBkpFile)
                            log.info("Backing up \"${fileOnDataDir}\" as " + 
                                "\"${nextBkpFile}\"")
                        }
                        // move the file from the temp dir and the data dir.
                        fileOnTemp.renameTo(fileOnDataDir)
                        log.info("Updating the file \"${fileOnDataDir}\"")
                    }
                }
            }
            //delete the temp-data's empty dirs and subdirs
            FileUtils.deleteDirectory(tempDataDir)
        }
    }

    private boolean isWindows() {
        return null != winServiceName
    }
    
    /**
     * Will run httpd as owner of the repository directory as httpd will 
     * write to it.
     * Might want to eventually add configuration for this.
     */
    private def setUserAndGroup() {
        def server = getServer()
        def repoPath = server ? (String) server.repoParentDir :
            (String) ConfigurationHolder.config.svnedge.svn.repositoriesParentPath
        //Sometimes ls -ld output coloumns are separated by double space.
        //For ex drwxr-xr-x  7 rajeswari __cubitu 4096 May 14 01:45 data/
        String[] result = commandLineService
            .executeWithOutput("ls", "-dl", repoPath).replaceAll(" +", " ").split(" ")

        httpdUser = result.length > 2 ? result[2] : "nobody"
        httpdGroup = result.length > 3 ? result[3] : "nobody"
{'fraction_non_alphanumeric': 0.08159633960345704, 'fraction_numerical': 0.0038129130655821047, 'mean_word_length': 2.0730326108181996, 'pattern_counts': {'":': 4, '<': 5, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3637362', 'n_tokens_mistral': 4290, 'n_tokens_neox': 4075, 'n_words': 1204}