blob: 20f750f90094aeb458abcb8b6a0484de772320db [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.maven.plugins.updatesite;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.maven.plugin.MojoExecutionException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public final class FileUtility {
private FileUtility() {
}
public static void copy(File inputFile, File outputFile) throws IOException {
if (inputFile.isDirectory()) {
ensureDirExists(outputFile);
for (File f : inputFile.listFiles()) {
copyToDir(f, outputFile);
}
return;
}
try (InputStream in = new FileInputStream(inputFile); OutputStream out = new FileOutputStream(outputFile)) {
copy(in, out);
}
}
public static void copy(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[8192];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
public static void copyToDir(File input, File toDir) throws IOException {
copyToDir(input, toDir, input.getParentFile().getAbsoluteFile().toURI());
}
public static void copyToDir(File input, File toDir, URI relPath) throws IOException {
// folder
if (input.isDirectory()) {
for (File f : input.listFiles()) {
copyToDir(f, toDir, relPath);
}
return;
}
// file
File outFile = new File(toDir.getAbsolutePath() + File.separator + relPath.relativize(input.toURI()).toString());
ensureDirExists(outFile);
try (InputStream in = new FileInputStream(input); OutputStream out = new FileOutputStream(outFile)) {
copy(in, out);
}
}
public static boolean deleteFile(File file) {
if (file.isDirectory()) {
for (File f : file.listFiles()) {
boolean success = deleteFile(f);
if (!success) {
return false;
}
}
}
return file.delete();
}
public static void compressArchive(File srcDir, File archiveFile) throws IOException {
archiveFile.delete();
try (JarOutputStream zOut = new JarOutputStream(new FileOutputStream(archiveFile))) {
addFolderToJar(srcDir, srcDir, zOut);
}
}
private static void addFolderToJar(File baseDir, File srcdir, JarOutputStream zOut) throws IOException {
if (!srcdir.exists() || !srcdir.isDirectory()) {
throw new IOException("source directory " + srcdir + " does not exist or is not a folder");
}
for (File f : srcdir.listFiles()) {
if (f.exists() && (!f.isHidden())) {
if (f.isDirectory()) {
addFolderToJar(baseDir, f, zOut);
}
else {
addFileToJar(baseDir, f, zOut);
}
}
}
}
private static void addFileToJar(File baseDir, File src, JarOutputStream zOut) throws IOException {
String name = src.getAbsolutePath();
String prefix = baseDir.getAbsolutePath();
if (prefix.endsWith("/") || prefix.endsWith("\\")) {
prefix = prefix.substring(0, prefix.length() - 1);
}
name = name.substring(prefix.length() + 1);
name = name.replace('\\', '/');
long timestamp = src.lastModified();
byte[] data = readFile(src);
addFileToJar(name, data, timestamp, zOut);
}
private static void addFileToJar(String name, byte[] data, long timestamp, JarOutputStream zOut) throws IOException {
ZipEntry entry = new ZipEntry(name);
entry.setTime(timestamp);
zOut.putNextEntry(entry);
zOut.write(data);
zOut.closeEntry();
}
public static byte[] readFile(File source) throws IOException {
return Files.readAllBytes(source.toPath());
}
@SuppressWarnings({"squid:S1166", "squid:S1141"})
public static void writeDOM(Document doc, File file) throws MojoExecutionException {
try {
TransformerFactory tf = TransformerFactory.newInstance();
try {
tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
}
catch (TransformerConfigurationException e) {
// nop
}
try {
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
}
catch (IllegalArgumentException e) {
// nop
}
try {
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
}
catch (IllegalArgumentException e) {
// nop
}
Transformer transformer = tf.newTransformer();
transformer.transform(new DOMSource(doc), new StreamResult(file));
}
catch (TransformerConfigurationException e) {
throw new MojoExecutionException("Could not write XML file ", e);
}
catch (TransformerException e) {
throw new MojoExecutionException("Could not write XML file ", e);
}
}
public static void ensureDirExists(File dir) throws IOException {
if (dir == null) {
return;
}
if (!dir.isDirectory()) {
dir = dir.getParentFile();
}
if (!dir.exists() && !dir.mkdirs()) {
throw new IOException("Unable to create directory '" + dir.getAbsolutePath() + "'.");
}
}
public static void extractArchive(File archiveFile, File targetDir) throws IOException {
File destinationDir = targetDir.getCanonicalFile();
Path destinationPath = destinationDir.toPath();
destinationDir.mkdirs();
destinationDir.setLastModified(archiveFile.lastModified());
String localFile = destinationDir.getName();
try (JarFile jar = new JarFile(archiveFile)) {
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry file = entries.nextElement();
String name = file.getName();
if (name.startsWith(localFile)) {
name = name.substring(localFile.length());
}
while (name.startsWith("/") || name.startsWith("\\")) {
name = name.substring(1);
}
File f = new File(destinationDir, name).getCanonicalFile();
if (!f.toPath().startsWith(destinationPath)) {
// security check (see https://github.com/snyk/zip-slip-vulnerability)
throw new IllegalArgumentException("Entry is outside of the target dir: " + name);
}
if (file.isDirectory()) { // if its a directory, create it
ensureDirExists(f);
}
else {
ensureDirExists(f.getParentFile());
try (InputStream is = jar.getInputStream(file); FileOutputStream fos = new FileOutputStream(f)) {
copy(is, fos);
}
}
if (file.getTime() >= 0) {
f.setLastModified(file.getTime());
}
}
}
}
public static Document readDOM(File xmlFile) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilder builder = createDocumentBuilder();
Document doc = builder.parse(xmlFile);
return doc;
}
/**
* Creates a new {@link DocumentBuilder} to create a DOM of an XML file.<br>
* Use {@link DocumentBuilder#parse()} to create a new {@link Document}.
*
* @return The created builder. All external entities are disabled to prevent XXE.
* @throws ParserConfigurationException
* if a {@link DocumentBuilder} cannot be created which satisfies the configuration requested.
*/
@SuppressWarnings("squid:S1166")
public static DocumentBuilder createDocumentBuilder() throws ParserConfigurationException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
Map<String, Boolean> features = new HashMap<>(5);
features.put("http://apache.org/xml/features/disallow-doctype-decl", Boolean.TRUE);
features.put("http://xml.org/sax/features/external-general-entities", Boolean.FALSE);
features.put("http://xml.org/sax/features/external-parameter-entities", Boolean.FALSE);
features.put("http://apache.org/xml/features/nonvalidating/load-external-dtd", Boolean.FALSE);
features.put(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
for (Entry<String, Boolean> a : features.entrySet()) {
String feature = a.getKey();
boolean enabled = a.getValue().booleanValue();
try {
dbf.setFeature(feature, enabled);
}
catch (ParserConfigurationException e) {
// nop
}
}
return dbf.newDocumentBuilder();
}
}