/*************************GO-LICENSE-START*********************************
* Copyright 2014 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*************************GO-LICENSE-END***********************************/
package com.thoughtworks.go.domain.materials;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.thoughtworks.go.util.GoConstants;
import com.thoughtworks.go.util.FileUtil;
import static com.thoughtworks.go.util.ExceptionUtils.bomb;
import com.thoughtworks.go.util.command.ConsoleOutputStreamConsumer;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
public class DirectoryCleaner {
private File baseFolder;
private final ConsoleOutputStreamConsumer consumer;
private Set<File> allowed = new HashSet<>();
private Set<File> check = new HashSet<>();
public DirectoryCleaner(File baseFolder, ConsoleOutputStreamConsumer consumer) {
this.baseFolder = baseFolder;
this.consumer = consumer;
}
public void allowed(String... folders) {
allowed(Arrays.asList(folders));
}
public void allowed(List<String> allowedFolders) {
ArrayList<File> allowedDirs = convertToFiles(allowedFolders);
for (File allowedDir : allowedDirs) {
allowed(allowedDir, allowedDirs);
}
}
public void allowed(File allowedDir, List<File> allowedDirs) {
try {
if (!FileUtil.isSubdirectoryOf(baseFolder, allowedDir)) {
throw new RuntimeException(
"Cannot clean directory."
+ " Folder " + allowedDir.getAbsolutePath() + " is outside the base folder " + baseFolder);
}
} catch (IOException e) {
throw new RuntimeException(
"Cannot clean directory."
+ " Folder " + allowedDir.getAbsolutePath() + " is outside the base folder " + baseFolder);
}
allow(allowedDir, allowedDirs);
}
private ArrayList<File> convertToFiles(List<String> allowedFolders) {
ArrayList<File> allowedDirs = new ArrayList<>();
for (String folder : allowedFolders) {
allowedDirs.add(new File(baseFolder, folder));
}
return allowedDirs;
}
private void allow(File allowedDir, List<File> allowedDirs) {
allowed.add(allowedDir);
File parentDir = allowedDir.getParentFile();
if (!parentDir.equals(baseFolder) && !allowed.contains(parentDir)) {
if (!isContainedInOtherAllowedDirs(parentDir, allowedDirs)) {
check(parentDir);
}
allow(parentDir, allowedDirs);
}
}
private boolean isContainedInOtherAllowedDirs(File dir, List<File> allowedDirs) {
for (File allowedDir : allowedDirs) {
try {
if (FileUtil.isSubdirectoryOf(allowedDir, dir)) {
return true;
}
} catch (IOException e) {
throw bomb(String.format("Failed to check directory %s and %s for sandbox cleanup", allowedDir, dir), e);
}
}
return false;
}
private void check(File dir) {
check.add(dir);
}
public void clean() {
if (allowed.isEmpty()) {
return;
}
cleanFolder(baseFolder);
for (File dir : check) {
cleanFolder(dir);
}
}
private void cleanFolder(File toClean) {
File[] files = toClean.listFiles((FileFilter) DirectoryFileFilter.DIRECTORY);
if (files == null) {
return;
}
for (File file : files) {
if (!allowed.contains(file)) {
consumer.stdOutput(String.format("[%s] Deleting folder %s", GoConstants.PRODUCT_NAME, file));
FileUtil.deleteFolder(file);
} else {
consumer.stdOutput(String.format("[%s] Keeping folder %s", GoConstants.PRODUCT_NAME, file));
}
}
}
}