diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionHandler.java index a2126115f..8f4669bbf 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionHandler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionHandler.java @@ -86,7 +86,7 @@ public class SuppressionHandler extends DefaultHandler { /** * The current node text being extracted from the element. */ - private StringBuffer currentText; + private StringBuilder currentText; /** * Handles the start element event. @@ -100,7 +100,7 @@ public class SuppressionHandler extends DefaultHandler { @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { currentAttributes = attributes; - currentText = new StringBuffer(); + currentText = new StringBuilder(); if (SUPPRESS.equals(qName)) { rule = new SuppressionRule(); final String base = currentAttributes.getValue("base"); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomHandler.java index 116032b20..36e3c630a 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomHandler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomHandler.java @@ -100,7 +100,7 @@ public class PomHandler extends DefaultHandler { /** * The current node text being extracted from the element. */ - private StringBuffer currentText; + private StringBuilder currentText; /** * Handles the start element event. @@ -113,7 +113,7 @@ public class PomHandler extends DefaultHandler { */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - currentText = new StringBuffer(); + currentText = new StringBuilder(); stack.push(qName); if (LICENSE.equals(qName)) { license = new License(); diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java new file mode 100644 index 000000000..7015cdcc6 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant; + +import java.io.Serializable; +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; +import org.xml.sax.Locator; + +/** + * Stores the location of a piece of text within a file (file name, + * line number and column number). Note that the column number is + * currently ignored. + * + */ +public class Location implements Serializable { + private static final long serialVersionUID = 1L; + + /** Name of the file. */ + private final String fileName; + /** Line number within the file. */ + private final int lineNumber; + /** Column number within the file. */ + private final int columnNumber; + + /** Location to use when one is needed but no information is available */ + public static final Location UNKNOWN_LOCATION = new Location(); + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Creates an "unknown" location. + */ + private Location() { + this(null, 0, 0); + } + + /** + * Creates a location consisting of a file name but no line number or + * column number. + * + * @param fileName The name of the file. May be null, + * in which case the location is equivalent to + * {@link #UNKNOWN_LOCATION UNKNOWN_LOCATION}. + */ + public Location(String fileName) { + this(fileName, 0, 0); + } + + /** + * Creates a location from the SAX locator using the system ID as + * the filename. + * + * @param loc Must not be null. + * + * @since Ant 1.6 + */ + public Location(Locator loc) { + this(loc.getSystemId(), loc.getLineNumber(), loc.getColumnNumber()); + } + + /** + * Creates a location consisting of a file name, line number and + * column number. + * + * @param fileName The name of the file. May be null, + * in which case the location is equivalent to + * {@link #UNKNOWN_LOCATION UNKNOWN_LOCATION}. + * + * @param lineNumber Line number within the file. Use 0 for unknown + * positions within a file. + * @param columnNumber Column number within the line. + */ + public Location(String fileName, int lineNumber, int columnNumber) { + if (fileName != null && fileName.startsWith("file:")) { + this.fileName = FILE_UTILS.fromURI(fileName); + } else { + this.fileName = fileName; + } + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + } + + /** + * @return the filename portion of the location + * @since Ant 1.6 + */ + public String getFileName() { + return fileName; + } + + /** + * @return the line number + * @since Ant 1.6 + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * @return the column number + * @since Ant 1.7 + */ + public int getColumnNumber() { + return columnNumber; + } + + /** + * Returns the file name, line number, a colon and a trailing space. + * An error message can be appended easily. For unknown locations, an + * empty string is returned. + * + * @return a String of the form "fileName:lineNumber: " + * if both file name and line number are known, + * "fileName: " if only the file name is known, + * and the empty string for unknown locations. + */ + public String toString() { + final StringBuilder buf = new StringBuilder(); + + if (fileName != null) { + buf.append(fileName); + + if (lineNumber != 0) { + buf.append(':').append(lineNumber); + } + + buf.append(": "); + } + + return buf.toString(); + } + + /** + * Equality operation. + * @param other the object to compare to. + * @return true if the other object contains the same information + * as this object. + * @since Ant 1.6.3 + */ + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null) { + return false; + } + if (!(other.getClass() == getClass())) { + return false; + } + return toString().equals(other.toString()); + } + + /** + * Hash operation. + * @return a hash code value for this location. + * @since Ant 1.6.3 + */ + public int hashCode() { + return toString().hashCode(); + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java new file mode 100644 index 000000000..e4f91baea --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java @@ -0,0 +1,530 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.launch; + +import java.net.MalformedURLException; +import java.net.URL; +import java.io.File; +import java.io.FilenameFilter; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.Locale; + +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; + +// CheckStyle:LineLengthCheck OFF - urls are long! +/** + * The Locator is a utility class which is used to find certain items + * in the environment. + * + * It is used at boot time in the launcher, and cannot make use of any of Ant's other classes. + * + * This is a surprisingly brittle piece of code, and has had lots of bugs filed against it. + * {@link running ant off a network share can cause Ant to fail} + * {@link use File.toURI().toURL().toExternalForm()} + * {@link Locator implementation not encoding URI strings properly: spaces in paths} + * It also breaks Eclipse 3.3 Betas + * {@link Exception if installation path has spaces} + * + * Be very careful when making changes to this class, as a break will upset a lot of people. + * @since Ant 1.6 + */ +// CheckStyle:LineLengthCheck ON - urls are long! +public final class Locator { + + private static final int NIBBLE = 4; + private static final int NIBBLE_MASK = 0xF; + + private static final int ASCII_SIZE = 128; + + private static final int BYTE_SIZE = 256; + + private static final int WORD = 16; + + private static final int SPACE = 0x20; + private static final int DEL = 0x7F; + + /** + * encoding used to represent URIs + */ + public static final String URI_ENCODING = "UTF-8"; + // stolen from org.apache.xerces.impl.XMLEntityManager#getUserDir() + // of the Xerces-J team + // which ASCII characters need to be escaped + private static boolean[] gNeedEscaping = new boolean[ASCII_SIZE]; + // the first hex character if a character needs to be escaped + private static char[] gAfterEscaping1 = new char[ASCII_SIZE]; + // the second hex character if a character needs to be escaped + private static char[] gAfterEscaping2 = new char[ASCII_SIZE]; + private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + /** Error string used when an invalid uri is seen */ + public static final String ERROR_NOT_FILE_URI + = "Can only handle valid file: URIs, not "; + + // initialize the above 3 arrays + static { + for (int i = 0; i < SPACE; i++) { + gNeedEscaping[i] = true; + gAfterEscaping1[i] = gHexChs[i >> NIBBLE]; + gAfterEscaping2[i] = gHexChs[i & NIBBLE_MASK]; + } + gNeedEscaping[DEL] = true; + gAfterEscaping1[DEL] = '7'; + gAfterEscaping2[DEL] = 'F'; + char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}', + '|', '\\', '^', '~', '[', ']', '`'}; + int len = escChs.length; + char ch; + for (int i = 0; i < len; i++) { + ch = escChs[i]; + gNeedEscaping[ch] = true; + gAfterEscaping1[ch] = gHexChs[ch >> NIBBLE]; + gAfterEscaping2[ch] = gHexChs[ch & NIBBLE_MASK]; + } + } + /** + * Not instantiable + */ + private Locator() { + } + + /** + * Find the directory or jar file the class has been loaded from. + * + * @param c the class whose location is required. + * @return the file or jar with the class or null if we cannot + * determine the location. + * + * @since Ant 1.6 + */ + public static File getClassSource(Class c) { + String classResource = c.getName().replace('.', '/') + ".class"; + return getResourceSource(c.getClassLoader(), classResource); + } + + /** + * Find the directory or jar a given resource has been loaded from. + * + * @param c the classloader to be consulted for the source. + * @param resource the resource whose location is required. + * + * @return the file with the resource source or null if + * we cannot determine the location. + * + * @since Ant 1.6 + */ + public static File getResourceSource(ClassLoader c, String resource) { + if (c == null) { + c = Locator.class.getClassLoader(); + } + URL url = null; + if (c == null) { + url = ClassLoader.getSystemResource(resource); + } else { + url = c.getResource(resource); + } + if (url != null) { + String u = url.toString(); + try { + if (u.startsWith("jar:file:")) { + return new File(fromJarURI(u)); + } else if (u.startsWith("file:")) { + int tail = u.indexOf(resource); + String dirName = u.substring(0, tail); + return new File(fromURI(dirName)); + } + } catch (IllegalArgumentException e) { + //unable to determine the URI for reasons unknown. + return null; + } + } + return null; + } + + + + /** + * Constructs a file path from a file: URI. + * + *

Will be an absolute path if the given URI is absolute.

+ * + *

Prior to Java 1.4, + * swallows '%' that are not followed by two characters.

+ * + * See dt-sysid + * which makes some mention of how + * characters not supported by URI Reference syntax should be escaped. + * + * @param uri the URI designating a file in the local filesystem. + * @return the local file system path for the file. + * @throws IllegalArgumentException if the URI is malformed or not a legal file: URL + * @since Ant 1.6 + */ + public static String fromURI(String uri) { + return fromURIJava13(uri); + // #buzilla8031: first try Java 1.4. + // TODO should use java.net.URI now that we can rely on 1.4... + // but check for UNC-related regressions, e.g. #42275 + // (and remember that \\server\share\file -> file:////server/share/file + // rather than -> file://server/share/file as it should; + // fixed only in JDK 7's java.nio.file.Path.toUri) + // return fromUriJava14(uri); + } + + /** + * Java1.4+ code to extract the path from the URI. + * @param uri + * @return null if a conversion was not possible + */ + /* currently unused: + private static String fromUriJava14(String uri) { + // Also check for properly formed URIs. Ant formerly recommended using + // nonsense URIs such as "file:./foo.xml" in XML includes. You shouldn't + // do that (just "foo.xml" is correct) but for compatibility we special-case + // things when the path is not absolute, and fall back to the old parsing behavior. + if (uri.startsWith("file:/")) { + try { + File f = new File(URI.create(encodeURI(uri))); + //bug #42227 forgot to decode before returning + return decodeUri(f.getAbsolutePath()); + } catch (IllegalArgumentException e) { + // Bad URI, pass this on. + // no, this is downgraded to a warning after various + // JRE bugs surfaced. Hand off + // to our built in code on a failure + //throw new IllegalArgumentException( + // "Bad URI " + uri + ":" + e.getMessage(), e); + e.printStackTrace(); + } catch (Exception e) { + // Unexpected exception? Should not happen. + e.printStackTrace(); + } + } + return null; + } + */ + + /** + * @param uri uri to expand + * @return the decoded URI + * @since Ant1.7.1 + */ + private static String fromURIJava13(String uri) { + // Fallback method for Java 1.3 or earlier. + + URL url = null; + try { + url = new URL(uri); + } catch (MalformedURLException emYouEarlEx) { + // Ignore malformed exception + } + if (url == null || !("file".equals(url.getProtocol()))) { + throw new IllegalArgumentException(ERROR_NOT_FILE_URI + uri); + } + final StringBuilder buf = new StringBuilder(url.getHost()); + if (buf.length() > 0) { + buf.insert(0, File.separatorChar).insert(0, File.separatorChar); + } + String file = url.getFile(); + int queryPos = file.indexOf('?'); + buf.append((queryPos < 0) ? file : file.substring(0, queryPos)); + + uri = buf.toString().replace('/', File.separatorChar); + + if (File.pathSeparatorChar == ';' && uri.startsWith("\\") && uri.length() > 2 + && Character.isLetter(uri.charAt(1)) && uri.lastIndexOf(':') > -1) { + uri = uri.substring(1); + } + String path = null; + try { + path = decodeUri(uri); + //consider adding the current directory. This is not done when + //the path is a UNC name + String cwd = System.getProperty("user.dir"); + int posi = cwd.indexOf(':'); + boolean pathStartsWithFileSeparator = path.startsWith(File.separator); + boolean pathStartsWithUNC = path.startsWith("" + File.separator + File.separator); + if ((posi > 0) && pathStartsWithFileSeparator && !pathStartsWithUNC) { + path = cwd.substring(0, posi + 1) + path; + } + } catch (UnsupportedEncodingException exc) { + // not sure whether this is clean, but this method is + // declared not to throw exceptions. + throw new IllegalStateException( + "Could not convert URI " + uri + " to path: " + + exc.getMessage()); + } + return path; + } + + /** + * Crack a JAR URI. + * This method is public for testing; we may delete it without any warning -it is not part of Ant's stable API. + * @param uri uri to expand; contains jar: somewhere in it + * @return the decoded URI + * @since Ant1.7.1 + */ + public static String fromJarURI(String uri) { + int pling = uri.indexOf("!/"); + String jarName = uri.substring("jar:".length(), pling); + return fromURI(jarName); + } + + /** + * Decodes an Uri with % characters. + * The URI is escaped + * @param uri String with the uri possibly containing % characters. + * @return The decoded Uri + * @throws UnsupportedEncodingException if UTF-8 is not available + * @since Ant 1.7 + */ + public static String decodeUri(String uri) throws UnsupportedEncodingException { + if (uri.indexOf('%') == -1) { + return uri; + } + ByteArrayOutputStream sb = new ByteArrayOutputStream(uri.length()); + CharacterIterator iter = new StringCharacterIterator(uri); + for (char c = iter.first(); c != CharacterIterator.DONE; + c = iter.next()) { + if (c == '%') { + char c1 = iter.next(); + if (c1 != CharacterIterator.DONE) { + int i1 = Character.digit(c1, WORD); + char c2 = iter.next(); + if (c2 != CharacterIterator.DONE) { + int i2 = Character.digit(c2, WORD); + sb.write((char) ((i1 << NIBBLE) + i2)); + } + } + } else if (c >= 0x0000 && c < 0x0080) { + sb.write(c); + } else { // #50543 + byte[] bytes = String.valueOf(c).getBytes(URI_ENCODING); + sb.write(bytes, 0, bytes.length); + } + } + return sb.toString(URI_ENCODING); + } + + /** + * Encodes an Uri with % characters. + * The URI is escaped + * @param path String to encode. + * @return The encoded string, according to URI norms + * @throws UnsupportedEncodingException if UTF-8 is not available + * @since Ant 1.7 + */ + public static String encodeURI(String path) throws UnsupportedEncodingException { + int i = 0; + int len = path.length(); + int ch = 0; + StringBuilder sb = null; + for (; i < len; i++) { + ch = path.charAt(i); + // if it's not an ASCII character, break here, and use UTF-8 encoding + if (ch >= ASCII_SIZE) { + break; + } + if (gNeedEscaping[ch]) { + if (sb == null) { + sb = new StringBuilder(path.substring(0, i)); + } + sb.append('%'); + sb.append(gAfterEscaping1[ch]); + sb.append(gAfterEscaping2[ch]); + // record the fact that it's escaped + } else if (sb != null) { + sb.append((char) ch); + } + } + + // we saw some non-ascii character + if (i < len) { + if (sb == null) { + sb = new StringBuilder(path.substring(0, i)); + } + // get UTF-8 bytes for the remaining sub-string + byte[] bytes = null; + byte b; + bytes = path.substring(i).getBytes(URI_ENCODING); + len = bytes.length; + + // for each byte + for (i = 0; i < len; i++) { + b = bytes[i]; + // for non-ascii character: make it positive, then escape + if (b < 0) { + ch = b + BYTE_SIZE; + sb.append('%'); + sb.append(gHexChs[ch >> NIBBLE]); + sb.append(gHexChs[ch & NIBBLE_MASK]); + } else if (gNeedEscaping[b]) { + sb.append('%'); + sb.append(gAfterEscaping1[b]); + sb.append(gAfterEscaping2[b]); + } else { + sb.append((char) b); + } + } + } + return sb == null ? path : sb.toString(); + } + + /** + * Convert a File to a URL. + * File.toURL() does not encode characters like #. + * File.toURI() has been introduced in java 1.4, so + * Ant cannot use it (except by reflection) + * FileUtils.toURI() cannot be used by Locator.java + * Implemented this way. + * File.toURL() adds file: and changes '\' to '/' for dos OSes + * encodeURI converts characters like ' ' and '#' to %DD + * @param file the file to convert + * @return URL the converted File + * @throws MalformedURLException on error + * @deprecated since 1.9, use {@link FileUtils#getFileURL(File)} + */ + @Deprecated + public static URL fileToURL(File file) + throws MalformedURLException { + return new URL(file.toURI().toASCIIString()); + } + + /** + * Get the File necessary to load the Sun compiler tools. If the classes + * are available to this class, then no additional URL is required and + * null is returned. This may be because the classes are explicitly in the + * class path or provided by the JVM directly. + * + * @return the tools jar as a File if required, null otherwise. + */ + public static File getToolsJar() { + // firstly check if the tools jar is already in the classpath + boolean toolsJarAvailable = false; + try { + // just check whether this throws an exception + Class.forName("com.sun.tools.javac.Main"); + toolsJarAvailable = true; + } catch (Exception e) { + try { + Class.forName("sun.tools.javac.Main"); + toolsJarAvailable = true; + } catch (Exception e2) { + // ignore + } + } + if (toolsJarAvailable) { + return null; + } + // couldn't find compiler - try to find tools.jar + // based on java.home setting + String libToolsJar + = File.separator + "lib" + File.separator + "tools.jar"; + String javaHome = System.getProperty("java.home"); + File toolsJar = new File(javaHome + libToolsJar); + if (toolsJar.exists()) { + // Found in java.home as given + return toolsJar; + } + if (javaHome.toLowerCase(Locale.ENGLISH).endsWith(File.separator + "jre")) { + javaHome = javaHome.substring( + 0, javaHome.length() - "/jre".length()); + toolsJar = new File(javaHome + libToolsJar); + } + if (!toolsJar.exists()) { + System.out.println("Unable to locate tools.jar. " + + "Expected to find it in " + toolsJar.getPath()); + return null; + } + return toolsJar; + } + + /** + * Get an array of URLs representing all of the jar files in the + * given location. If the location is a file, it is returned as the only + * element of the array. If the location is a directory, it is scanned for + * jar files. + * + * @param location the location to scan for Jars. + * + * @return an array of URLs for all jars in the given location. + * + * @exception MalformedURLException if the URLs for the jars cannot be + * formed. + */ + public static URL[] getLocationURLs(File location) + throws MalformedURLException { + return getLocationURLs(location, new String[]{".jar"}); + } + + /** + * Get an array of URLs representing all of the files of a given set of + * extensions in the given location. If the location is a file, it is + * returned as the only element of the array. If the location is a + * directory, it is scanned for matching files. + * + * @param location the location to scan for files. + * @param extensions an array of extension that are to match in the + * directory search. + * + * @return an array of URLs of matching files. + * @exception MalformedURLException if the URLs for the files cannot be + * formed. + */ + public static URL[] getLocationURLs(File location, + final String[] extensions) + throws MalformedURLException { + URL[] urls = new URL[0]; + + if (!location.exists()) { + return urls; + } + if (!location.isDirectory()) { + urls = new URL[1]; + String path = location.getPath(); + String littlePath = path.toLowerCase(Locale.ENGLISH); + for (int i = 0; i < extensions.length; ++i) { + if (littlePath.endsWith(extensions[i])) { + urls[0] = fileToURL(location); + break; + } + } + return urls; + } + File[] matches = location.listFiles( + new FilenameFilter() { + public boolean accept(File dir, String name) { + String littleName = name.toLowerCase(Locale.ENGLISH); + for (int i = 0; i < extensions.length; ++i) { + if (littleName.endsWith(extensions[i])) { + return true; + } + } + return false; + } + }); + urls = new URL[matches.length]; + for (int i = 0; i < matches.length; ++i) { + urls[i] = fileToURL(matches[i]); + } + return urls; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorUtils.java new file mode 100644 index 000000000..7f37eda43 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorUtils.java @@ -0,0 +1,695 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; + +import java.io.File; +import java.util.StringTokenizer; +import java.util.Vector; + +import org.owasp.dependencycheck.org.apache.tools.ant.types.Resource; +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; + +/** + *

This is a utility class used by selectors and DirectoryScanner. The + * functionality more properly belongs just to selectors, but unfortunately + * DirectoryScanner exposed these as protected methods. Thus we have to + * support any subclasses of DirectoryScanner that may access these methods. + *

+ *

This is a Singleton.

+ * + * @since 1.5 + */ +public final class SelectorUtils { + + /** + * The pattern that matches an arbitrary number of directories. + * @since Ant 1.8.0 + */ + public static final String DEEP_TREE_MATCH = "**"; + + private static final SelectorUtils instance = new SelectorUtils(); + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Private Constructor + */ + private SelectorUtils() { + } + + /** + * Retrieves the instance of the Singleton. + * @return singleton instance + */ + public static SelectorUtils getInstance() { + return instance; + } + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + *

+ * This is not a general purpose test and should only be used if you + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + public static boolean matchPatternStart(String pattern, String str) { + return matchPatternStart(pattern, str, true); + } + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + *

+ * This is not a general purpose test and should only be used if you + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + public static boolean matchPatternStart(String pattern, String str, + boolean isCaseSensitive) { + // When str starts with a File.separator, pattern has to start with a + // File.separator. + // When pattern starts with a File.separator, str has to start with a + // File.separator. + if (str.startsWith(File.separator) + != pattern.startsWith(File.separator)) { + return false; + } + + String[] patDirs = tokenizePathAsArray(pattern); + String[] strDirs = tokenizePathAsArray(str); + return matchPatternStart(patDirs, strDirs, isCaseSensitive); + } + + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + *

+ * This is not a general purpose test and should only be used if you + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param patDirs The tokenized pattern to match against. Must not be + * null. + * @param strDirs The tokenized path to match. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + static boolean matchPatternStart(String[] patDirs, String[] strDirs, + boolean isCaseSensitive) { + int patIdxStart = 0; + int patIdxEnd = patDirs.length - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.length - 1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = patDirs[patIdxStart]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) { + return false; + } + patIdxStart++; + strIdxStart++; + } + + // CheckStyle:SimplifyBooleanReturnCheck OFF + // Check turned off as the code needs the comments for the various + // code paths. + if (strIdxStart > strIdxEnd) { + // String is exhausted + return true; + } else if (patIdxStart > patIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } else { + // pattern now holds ** while string is not exhausted + // this will generate false positives but we can live with that. + return true; + } + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * If you need to call this method multiple times with the same + * pattern you should rather use TokenizedPath + * + * @see TokenizedPath + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * + * @return true if the pattern matches against the string, + * or false otherwise. + */ + public static boolean matchPath(String pattern, String str) { + String[] patDirs = tokenizePathAsArray(pattern); + return matchPath(patDirs, tokenizePathAsArray(str), true); + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * If you need to call this method multiple times with the same + * pattern you should rather use TokenizedPattern + * + * @see TokenizedPattern + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return true if the pattern matches against the string, + * or false otherwise. + */ + public static boolean matchPath(String pattern, String str, + boolean isCaseSensitive) { + String[] patDirs = tokenizePathAsArray(pattern); + return matchPath(patDirs, tokenizePathAsArray(str), isCaseSensitive); + } + + /** + * Core implementation of matchPath. It is isolated so that it + * can be called from TokenizedPattern. + */ + static boolean matchPath(String[] tokenizedPattern, String[] strDirs, + boolean isCaseSensitive) { + int patIdxStart = 0; + int patIdxEnd = tokenizedPattern.length - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.length - 1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = tokenizedPattern[patIdxStart]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) { + return false; + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + return true; + } else { + if (patIdxStart > patIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } + } + + // up to last '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = tokenizedPattern[patIdxEnd]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) { + return false; + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + return true; + } + + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { + if (tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == patIdxStart + 1) { + // '**/**' situation, so skip one + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp - patIdxStart - 1); + int strLength = (strIdxEnd - strIdxStart + 1); + int foundIdx = -1; + strLoop: + for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + String subPat = tokenizedPattern[patIdxStart + j + 1]; + String subStr = strDirs[strIdxStart + i + j]; + if (!match(subPat, subStr, isCaseSensitive)) { + continue strLoop; + } + } + + foundIdx = strIdxStart + i; + break; + } + + if (foundIdx == -1) { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + + return true; + } + + /** + * Tests whether or not a string matches against a pattern. + * The pattern may contain two special characters:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be null. + * @param str The string which must be matched against the pattern. + * Must not be null. + * + * @return true if the string matches against the pattern, + * or false otherwise. + */ + public static boolean match(String pattern, String str) { + return match(pattern, str, true); + } + + /** + * Tests whether or not a string matches against a pattern. + * The pattern may contain two special characters:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be null. + * @param str The string which must be matched against the pattern. + * Must not be null. + * @param caseSensitive Whether or not matching should be performed + * case sensitively. + * + * + * @return true if the string matches against the pattern, + * or false otherwise. + */ + public static boolean match(String pattern, String str, + boolean caseSensitive) { + char[] patArr = pattern.toCharArray(); + char[] strArr = str.toCharArray(); + int patIdxStart = 0; + int patIdxEnd = patArr.length - 1; + int strIdxStart = 0; + int strIdxEnd = strArr.length - 1; + char ch; + + boolean containsStar = false; + for (int i = 0; i < patArr.length; i++) { + if (patArr[i] == '*') { + containsStar = true; + break; + } + } + + if (!containsStar) { + // No '*'s, so we make a shortcut + if (patIdxEnd != strIdxEnd) { + return false; // Pattern and string do not have the same size + } + for (int i = 0; i <= patIdxEnd; i++) { + ch = patArr[i]; + if (ch != '?') { + if (different(caseSensitive, ch, strArr[i])) { + return false; // Character mismatch + } + } + } + return true; // String matches against pattern + } + + if (patIdxEnd == 0) { + return true; // Pattern contains only '*', which matches anything + } + + // Process characters before first star + while (true) { + ch = patArr[patIdxStart]; + if (ch == '*' || strIdxStart > strIdxEnd) { + break; + } + if (ch != '?') { + if (different(caseSensitive, ch, strArr[strIdxStart])) { + return false; // Character mismatch + } + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + // Process characters after last star + while (true) { + ch = patArr[patIdxEnd]; + if (ch == '*' || strIdxStart > strIdxEnd) { + break; + } + if (ch != '?') { + if (different(caseSensitive, ch, strArr[strIdxEnd])) { + return false; // Character mismatch + } + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + // process pattern between stars. padIdxStart and patIdxEnd point + // always to a '*'. + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { + if (patArr[i] == '*') { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == patIdxStart + 1) { + // Two stars next to each other, skip the first one. + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp - patIdxStart - 1); + int strLength = (strIdxEnd - strIdxStart + 1); + int foundIdx = -1; + strLoop: + for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + ch = patArr[patIdxStart + j + 1]; + if (ch != '?') { + if (different(caseSensitive, ch, + strArr[strIdxStart + i + j])) { + continue strLoop; + } + } + } + + foundIdx = strIdxStart + i; + break; + } + + if (foundIdx == -1) { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + // All characters in the string are used. Check if only '*'s are left + // in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + private static boolean allStars(char[] chars, int start, int end) { + for (int i = start; i <= end; ++i) { + if (chars[i] != '*') { + return false; + } + } + return true; + } + + private static boolean different( + boolean caseSensitive, char ch, char other) { + return caseSensitive + ? ch != other + : Character.toUpperCase(ch) != Character.toUpperCase(other); + } + + /** + * Breaks a path up into a Vector of path elements, tokenizing on + * File.separator. + * + * @param path Path to tokenize. Must not be null. + * + * @return a Vector of path elements from the tokenized path + */ + public static Vector tokenizePath(String path) { + return tokenizePath(path, File.separator); + } + + /** + * Breaks a path up into a Vector of path elements, tokenizing on + * + * @param path Path to tokenize. Must not be null. + * @param separator the separator against which to tokenize. + * + * @return a Vector of path elements from the tokenized path + * @since Ant 1.6 + */ + public static Vector tokenizePath(String path, String separator) { + Vector ret = new Vector(); + if (FileUtils.isAbsolutePath(path)) { + String[] s = FILE_UTILS.dissect(path); + ret.add(s[0]); + path = s[1]; + } + StringTokenizer st = new StringTokenizer(path, separator); + while (st.hasMoreTokens()) { + ret.addElement(st.nextToken()); + } + return ret; + } + + /** + * Same as {@link #tokenizePath tokenizePath} but hopefully faster. + */ + /*package*/ static String[] tokenizePathAsArray(String path) { + String root = null; + if (FileUtils.isAbsolutePath(path)) { + String[] s = FILE_UTILS.dissect(path); + root = s[0]; + path = s[1]; + } + char sep = File.separatorChar; + int start = 0; + int len = path.length(); + int count = 0; + for (int pos = 0; pos < len; pos++) { + if (path.charAt(pos) == sep) { + if (pos != start) { + count++; + } + start = pos + 1; + } + } + if (len != start) { + count++; + } + String[] l = new String[count + ((root == null) ? 0 : 1)]; + + if (root != null) { + l[0] = root; + count = 1; + } else { + count = 0; + } + start = 0; + for (int pos = 0; pos < len; pos++) { + if (path.charAt(pos) == sep) { + if (pos != start) { + String tok = path.substring(start, pos); + l[count++] = tok; + } + start = pos + 1; + } + } + if (len != start) { + String tok = path.substring(start); + l[count/*++*/] = tok; + } + return l; + } + + /** + * Returns dependency information on these two files. If src has been + * modified later than target, it returns true. If target doesn't exist, + * it likewise returns true. Otherwise, target is newer than src and + * is not out of date, thus the method returns false. It also returns + * false if the src file doesn't even exist, since how could the + * target then be out of date. + * + * @param src the original file + * @param target the file being compared against + * @param granularity the amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static boolean isOutOfDate(File src, File target, int granularity) { + if (!src.exists()) { + return false; + } + if (!target.exists()) { + return true; + } + if ((src.lastModified() - granularity) > target.lastModified()) { + return true; + } + return false; + } + + /** + * Returns dependency information on these two resources. If src has been + * modified later than target, it returns true. If target doesn't exist, + * it likewise returns true. Otherwise, target is newer than src and + * is not out of date, thus the method returns false. It also returns + * false if the src file doesn't even exist, since how could the + * target then be out of date. + * + * @param src the original resource + * @param target the resource being compared against + * @param granularity the int amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static boolean isOutOfDate(Resource src, Resource target, + int granularity) { + return isOutOfDate(src, target, (long) granularity); + } + + /** + * Returns dependency information on these two resources. If src has been + * modified later than target, it returns true. If target doesn't exist, + * it likewise returns true. Otherwise, target is newer than src and + * is not out of date, thus the method returns false. It also returns + * false if the src file doesn't even exist, since how could the + * target then be out of date. + * + * @param src the original resource + * @param target the resource being compared against + * @param granularity the long amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static boolean isOutOfDate(Resource src, Resource target, long granularity) { + long sourceLastModified = src.getLastModified(); + long targetLastModified = target.getLastModified(); + return src.isExists() + && (sourceLastModified == Resource.UNKNOWN_DATETIME + || targetLastModified == Resource.UNKNOWN_DATETIME + || (sourceLastModified - granularity) > targetLastModified); + } + + /** + * "Flattens" a string by removing all whitespace (space, tab, linefeed, + * carriage return, and formfeed). This uses StringTokenizer and the + * default set of tokens as documented in the single argument constructor. + * + * @param input a String to remove all whitespace. + * @return a String that has had all whitespace removed. + */ + public static String removeWhitespace(String input) { + final StringBuilder result = new StringBuilder(); + if (input != null) { + final StringTokenizer st = new StringTokenizer(input); + while (st.hasMoreTokens()) { + result.append(st.nextToken()); + } + } + return result.toString(); + } + + /** + * Tests if a string contains stars or question marks + * @param input a String which one wants to test for containing wildcard + * @return true if the string contains at least a star or a question mark + */ + public static boolean hasWildcards(String input) { + return (input.indexOf('*') != -1 || input.indexOf('?') != -1); + } + + /** + * removes from a pattern all tokens to the right containing wildcards + * @param input the input string + * @return the leftmost part of the pattern without wildcards + */ + public static String rtrimWildcardTokens(String input) { + return new TokenizedPattern(input).rtrimWildcardTokens().toString(); + } +} + diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/FileUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/FileUtils.java new file mode 100644 index 000000000..32940225c --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/FileUtils.java @@ -0,0 +1,1667 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.util; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.net.HttpURLConnection; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.channels.Channel; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Stack; +import java.util.StringTokenizer; +import java.util.jar.JarFile; +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; +import org.owasp.dependencycheck.org.apache.tools.ant.PathTokenizer; +import org.owasp.dependencycheck.org.apache.tools.ant.launch.Locator; +import org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition.Os; + +/** + * This class also encapsulates methods which allow Files to be referred to using abstract path names which are + * translated to native system file paths at runtime as well as copying files or setting their last modification time. + * + */ +public class FileUtils { + + private static final int DELETE_RETRY_SLEEP_MILLIS = 10; + private static final int EXPAND_SPACE = 50; + private static final FileUtils PRIMARY_INSTANCE = new FileUtils(); + + //get some non-crypto-grade randomness from various places. + private static Random rand = new Random(System.currentTimeMillis() + + Runtime.getRuntime().freeMemory()); + + private static final boolean ON_NETWARE = Os.isFamily("netware"); + private static final boolean ON_DOS = Os.isFamily("dos"); + private static final boolean ON_WIN9X = Os.isFamily("win9x"); + private static final boolean ON_WINDOWS = Os.isFamily("windows"); + + static final int BUF_SIZE = 8192; + + /** + * The granularity of timestamps under FAT. + */ + public static final long FAT_FILE_TIMESTAMP_GRANULARITY = 2000; + + /** + * The granularity of timestamps under Unix. + */ + public static final long UNIX_FILE_TIMESTAMP_GRANULARITY = 1000; + + /** + * The granularity of timestamps under the NT File System. NTFS has a granularity of 100 nanoseconds, which is less + * than 1 millisecond, so we round this up to 1 millisecond. + */ + public static final long NTFS_FILE_TIMESTAMP_GRANULARITY = 1; + + /** + * A one item cache for fromUri. fromUri is called for each element when parseing ant build files. It is a costly + * operation. This just caches the result of the last call. + */ + private Object cacheFromUriLock = new Object(); + private String cacheFromUriRequest = null; + private String cacheFromUriResponse = null; + + /** + * Factory method. + * + * @return a new instance of FileUtils. + * @deprecated since 1.7. Use getFileUtils instead, FileUtils do not have state. + */ + public static FileUtils newFileUtils() { + return new FileUtils(); + } + + /** + * Method to retrieve The FileUtils, which is shared by all users of this method. + * + * @return an instance of FileUtils. + * @since Ant 1.6.3 + */ + public static FileUtils getFileUtils() { + return PRIMARY_INSTANCE; + } + + /** + * Empty constructor. + */ + protected FileUtils() { + } + + /** + * Get the URL for a file taking into account # characters. + * + * @param file the file whose URL representation is required. + * @return The FileURL value. + * @throws MalformedURLException if the URL representation cannot be formed. + */ + public URL getFileURL(File file) throws MalformedURLException { + return new URL(file.toURI().toASCIIString()); + } + +// /** +// * Convenience method to copy a file from a source to a destination. +// * No filtering is performed. +// * +// * @param sourceFile Name of file to copy from. +// * Must not be null. +// * @param destFile Name of file to copy to. +// * Must not be null. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(String sourceFile, String destFile) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), null, false, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination +// * specifying if token filtering must be used. +// * +// * @param sourceFile Name of file to copy from. +// * Must not be null. +// * @param destFile Name of file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(String sourceFile, String destFile, FilterSetCollection filters) +// throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, false, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination specifying if token +// * filtering must be used and if source files may overwrite newer destination files. +// * +// * @param sourceFile Name of file to copy from. Must not be null. +// * @param destFile Name of file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be overwritten if it already +// * exists. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(String sourceFile, String destFile, FilterSetCollection filters, +// boolean overwrite) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, overwrite, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination +// * specifying if token +// * filtering must be used, if source files may overwrite newer destination +// * files and the last +// * modified time of destFile file should be made equal to +// * the last modified time +// * of sourceFile. +// * +// * @param sourceFile Name of file to copy from. Must not be null. +// * @param destFile Name of file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file +// * should be set to that of the source file. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(String sourceFile, String destFile, +// FilterSetCollection filters, +// boolean overwrite, boolean preserveLastModified) +// throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, overwrite, +// preserveLastModified); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination specifying if token +// * filtering must be used, if source files may overwrite newer destination files and the last +// * modified time of destFile file should be made equal to the last modified time +// * of sourceFile. +// * +// * @param sourceFile Name of file to copy from. Must not be null. +// * @param destFile Name of file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be overwritten if it already +// * exists. +// * @param preserveLastModified Whether or not the last modified time of the resulting file +// * should be set to that of the source file. +// * @param encoding the encoding used to read and write the files. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.5 +// */ +// public void copyFile(String sourceFile, String destFile, +// FilterSetCollection filters, boolean overwrite, +// boolean preserveLastModified, String encoding) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, +// overwrite, preserveLastModified, encoding); +// } +// // CheckStyle:ParameterNumberCheck OFF - bc +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile Name of file to copy from. +// * Must not be null. +// * @param destFile Name of file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param encoding the encoding used to read and write the files. +// * @param project the project instance. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.5 +// */ +// public void copyFile(String sourceFile, String destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// String encoding, Project project) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, filterChains, overwrite, +// preserveLastModified, encoding, project); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination specifying if token +// * filtering must be used, if filter chains must be used, if source files may overwrite newer +// * destination files and the last modified time of destFile file should be made +// * equal to the last modified time of sourceFile. +// * +// * @param sourceFile Name of file to copy from. Must not be null. +// * @param destFile Name of file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be overwritten if it already +// * exists. +// * @param preserveLastModified Whether or not the last modified time of the resulting file +// * should be set to that of the source file. +// * @param inputEncoding the encoding used to read the files. +// * @param outputEncoding the encoding used to write the files. +// * @param project the project instance. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.6 +// */ +// public void copyFile(String sourceFile, String destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// String inputEncoding, String outputEncoding, +// Project project) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, filterChains, overwrite, +// preserveLastModified, inputEncoding, outputEncoding, project); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination. No filtering is performed. +// * +// * @param sourceFile the file to copy from. Must not be null. +// * @param destFile the file to copy to. Must not be null. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(File sourceFile, File destFile) throws IOException { +// copyFile(sourceFile, destFile, null, false, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination +// * specifying if token filtering must be used. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(File sourceFile, File destFile, FilterSetCollection filters) +// throws IOException { +// copyFile(sourceFile, destFile, filters, false, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used and if +// * source files may overwrite newer destination files. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(File sourceFile, File destFile, FilterSetCollection filters, +// boolean overwrite) throws IOException { +// copyFile(sourceFile, destFile, filters, overwrite, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * source files may overwrite newer destination files and the +// * last modified time of destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(File sourceFile, File destFile, FilterSetCollection filters, +// boolean overwrite, boolean preserveLastModified) throws IOException { +// copyFile(sourceFile, destFile, filters, overwrite, preserveLastModified, null); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination specifying if token +// * filtering must be used, if source files may overwrite newer destination files, the last +// * modified time of destFile file should be made equal to the last modified time +// * of sourceFile and which character encoding to assume. +// * +// * @param sourceFile the file to copy from. Must not be null. +// * @param destFile the file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be overwritten if it already +// * exists. +// * @param preserveLastModified Whether or not the last modified time of the resulting file +// * should be set to that of the source file. +// * @param encoding the encoding used to read and write the files. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.5 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, boolean overwrite, +// boolean preserveLastModified, String encoding) throws IOException { +// copyFile(sourceFile, destFile, filters, null, overwrite, +// preserveLastModified, encoding, null); +// } +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param encoding the encoding used to read and write the files. +// * @param project the project instance. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.5 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// String encoding, Project project) throws IOException { +// copyFile(sourceFile, destFile, filters, filterChains, +// overwrite, preserveLastModified, encoding, encoding, project); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param inputEncoding the encoding used to read the files. +// * @param outputEncoding the encoding used to write the files. +// * @param project the project instance. +// * +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.6 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// String inputEncoding, String outputEncoding, +// Project project) throws IOException { +// copyFile(sourceFile, destFile, filters, filterChains, overwrite, preserveLastModified, +// false, inputEncoding, outputEncoding, project); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param append whether to append to the destination file. +// * @param inputEncoding the encoding used to read the files. +// * @param outputEncoding the encoding used to write the files. +// * @param project the project instance. +// * +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.8 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// boolean append, +// String inputEncoding, String outputEncoding, +// Project project) throws IOException { +// copyFile(sourceFile, destFile, filters, filterChains, overwrite, +// preserveLastModified, append, inputEncoding, outputEncoding, +// project, /* force: */ false); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param append whether to append to the destination file. +// * @param inputEncoding the encoding used to read the files. +// * @param outputEncoding the encoding used to write the files. +// * @param project the project instance. +// * @param force whether to overwrite read-only destination files. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.8.2 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// boolean append, +// String inputEncoding, String outputEncoding, +// Project project, boolean force) throws IOException { +// ResourceUtils.copyResource(new FileResource(sourceFile), +// new FileResource(destFile), +// filters, filterChains, overwrite, +// preserveLastModified, append, inputEncoding, +// outputEncoding, project, force); +// } +// +// // CheckStyle:ParameterNumberCheck ON +// +// /** +// * Calls File.setLastModified(long time). Originally written to +// * to dynamically bind to that call on Java1.2+. +// * +// * @param file the file whose modified time is to be set +// * @param time the time to which the last modified time is to be set. +// * if this is -1, the current time is used. +// */ +// public void setFileLastModified(File file, long time) { +// ResourceUtils.setLastModified(new FileResource(file), time); +// } + /** + * Interpret the filename as a file relative to the given file unless the filename already represents an absolute + * filename. Differs from new File(file, filename) in that the resulting File's path will always be a + * normalized, absolute pathname. Also, if it is determined that filename is context-relative, + * file will be discarded and the reference will be resolved using available context/state information + * about the filesystem. + * + * @param file the "reference" file for relative paths. This instance must be an absolute file and must not contain + * "./" or "../" sequences (same for \ instead of /). If it is null, this call is equivalent to + * new java.io.File(filename).getAbsoluteFile(). + * + * @param filename a file name. + * + * @return an absolute file. + * @throws java.lang.NullPointerException if filename is null. + */ + public File resolveFile(File file, String filename) { + if (!isAbsolutePath(filename)) { + char sep = File.separatorChar; + filename = filename.replace('/', sep).replace('\\', sep); + if (isContextRelativePath(filename)) { + file = null; + // on cygwin, our current directory can be a UNC; + // assume user.dir is absolute or all hell breaks loose... + String udir = System.getProperty("user.dir"); + if (filename.charAt(0) == sep && udir.charAt(0) == sep) { + filename = dissect(udir)[0] + filename.substring(1); + } + } + filename = new File(file, filename).getAbsolutePath(); + } + return normalize(filename); + } + + /** + * On DOS and NetWare, the evaluation of certain file specifications is context-dependent. These are filenames + * beginning with a single separator (relative to current root directory) and filenames with a drive specification + * and no intervening separator (relative to current directory of the specified root). + * + * @param filename the filename to evaluate. + * @return true if the filename is relative to system context. + * @throws java.lang.NullPointerException if filename is null. + * @since Ant 1.7 + */ + public static boolean isContextRelativePath(String filename) { + if (!(ON_DOS || ON_NETWARE) || filename.length() == 0) { + return false; + } + char sep = File.separatorChar; + filename = filename.replace('/', sep).replace('\\', sep); + char c = filename.charAt(0); + int len = filename.length(); + return (c == sep && (len == 1 || filename.charAt(1) != sep)) + || (Character.isLetter(c) && len > 1 + && filename.charAt(1) == ':' + && (len == 2 || filename.charAt(2) != sep)); + } + + /** + * Verifies that the specified filename represents an absolute path. Differs from new + * java.io.File("filename").isAbsolute() in that a path beginning with a double file separator--signifying a Windows + * UNC--must at minimum match "\\a\b" to be considered an absolute path. + * + * @param filename the filename to be checked. + * @return true if the filename represents an absolute path. + * @throws java.lang.NullPointerException if filename is null. + * @since Ant 1.6.3 + */ + public static boolean isAbsolutePath(String filename) { + int len = filename.length(); + if (len == 0) { + return false; + } + char sep = File.separatorChar; + filename = filename.replace('/', sep).replace('\\', sep); + char c = filename.charAt(0); + if (!(ON_DOS || ON_NETWARE)) { + return (c == sep); + } + if (c == sep) { + // CheckStyle:MagicNumber OFF + if (!(ON_DOS && len > 4 && filename.charAt(1) == sep)) { + return false; + } + // CheckStyle:MagicNumber ON + int nextsep = filename.indexOf(sep, 2); + return nextsep > 2 && nextsep + 1 < len; + } + int colon = filename.indexOf(':'); + return (Character.isLetter(c) && colon == 1 + && filename.length() > 2 && filename.charAt(2) == sep) + || (ON_NETWARE && colon > 0); + } + + /** + * Translate a path into its native (platform specific) format. + *

+ * This method uses PathTokenizer to separate the input path into its components. This handles DOS style paths in a + * relatively sensible way. The file separators are then converted to their platform specific versions. + * + * @param toProcess The path to be translated. May be null. + * + * @return the native version of the specified path or an empty string if the path is null or empty. + * + * @since ant 1.7 + * @see PathTokenizer + */ + public static String translatePath(String toProcess) { + if (toProcess == null || toProcess.length() == 0) { + return ""; + } + final StringBuilder path = new StringBuilder(toProcess.length() + EXPAND_SPACE); + final PathTokenizer tokenizer = new PathTokenizer(toProcess); + while (tokenizer.hasMoreTokens()) { + String pathComponent = tokenizer.nextToken(); + pathComponent = pathComponent.replace('/', File.separatorChar); + pathComponent = pathComponent.replace('\\', File.separatorChar); + if (path.length() != 0) { + path.append(File.pathSeparatorChar); + } + path.append(pathComponent); + } + return path.toString(); + } + + /** + * "Normalize" the given absolute path. + * + *

+ * This includes: + *

+ * Unlike {@link File#getCanonicalPath()} this method specifically does not resolve symbolic links. + * + * @param path the path to be normalized. + * @return the normalized version of the path. + * + * @throws java.lang.NullPointerException if path is null. + */ + public File normalize(final String path) { + Stack s = new Stack(); + String[] dissect = dissect(path); + s.push(dissect[0]); + + StringTokenizer tok = new StringTokenizer(dissect[1], File.separator); + while (tok.hasMoreTokens()) { + String thisToken = tok.nextToken(); + if (".".equals(thisToken)) { + continue; + } + if ("..".equals(thisToken)) { + if (s.size() < 2) { + // Cannot resolve it, so skip it. + return new File(path); + } + s.pop(); + } else { // plain component + s.push(thisToken); + } + } + final StringBuilder sb = new StringBuilder(); + final int size = s.size(); + for (int i = 0; i < size; i++) { + if (i > 1) { + // not before the filesystem root and not after it, since root + // already contains one + sb.append(File.separatorChar); + } + sb.append(s.elementAt(i)); + } + return new File(sb.toString()); + } + + /** + * Dissect the specified absolute path. + * + * @param path the path to dissect. + * @return String[] {root, remaining path}. + * @throws java.lang.NullPointerException if path is null. + * @since Ant 1.7 + */ + public String[] dissect(String path) { + char sep = File.separatorChar; + path = path.replace('/', sep).replace('\\', sep); + + // make sure we are dealing with an absolute path + if (!isAbsolutePath(path)) { + throw new BuildException(path + " is not an absolute path"); + } + String root = null; + int colon = path.indexOf(':'); + if (colon > 0 && (ON_DOS || ON_NETWARE)) { + + int next = colon + 1; + root = path.substring(0, next); + char[] ca = path.toCharArray(); + root += sep; + //remove the initial separator; the root has it. + next = (ca[next] == sep) ? next + 1 : next; + + final StringBuilder sbPath = new StringBuilder(); + // Eliminate consecutive slashes after the drive spec: + for (int i = next; i < ca.length; i++) { + if (ca[i] != sep || ca[i - 1] != sep) { + sbPath.append(ca[i]); + } + } + path = sbPath.toString(); + } else if (path.length() > 1 && path.charAt(1) == sep) { + // UNC drive + int nextsep = path.indexOf(sep, 2); + nextsep = path.indexOf(sep, nextsep + 1); + root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path; + path = path.substring(root.length()); + } else { + root = File.separator; + path = path.substring(1); + } + return new String[]{root, path}; + } + + /** + * Returns a VMS String representation of a File object. This is useful since the JVM by default + * internally converts VMS paths to Unix style. The returned String is always an absolute path. + * + * @param f The File to get the VMS path for. + * @return The absolute VMS path to f. + */ + public String toVMSPath(File f) { + // format: "DEVICE:[DIR.SUBDIR]FILE" + String osPath; + String path = normalize(f.getAbsolutePath()).getPath(); + String name = f.getName(); + boolean isAbsolute = path.charAt(0) == File.separatorChar; + // treat directories specified using .DIR syntax as files + // CheckStyle:MagicNumber OFF + boolean isDirectory = f.isDirectory() + && !name.regionMatches(true, name.length() - 4, ".DIR", 0, 4); + // CheckStyle:MagicNumber ON + String device = null; + StringBuilder directory = null; + String file = null; + + int index = 0; + + if (isAbsolute) { + index = path.indexOf(File.separatorChar, 1); + if (index == -1) { + return path.substring(1) + ":[000000]"; + } + device = path.substring(1, index++); + } + if (isDirectory) { + directory = new StringBuilder(path.substring(index).replace(File.separatorChar, '.')); + } else { + int dirEnd = path.lastIndexOf(File.separatorChar, path.length()); + if (dirEnd == -1 || dirEnd < index) { + file = path.substring(index); + } else { + directory = new StringBuilder(path.substring(index, dirEnd). + replace(File.separatorChar, '.')); + index = dirEnd + 1; + if (path.length() > index) { + file = path.substring(index); + } + } + } + if (!isAbsolute && directory != null) { + directory.insert(0, '.'); + } + osPath = ((device != null) ? device + ":" : "") + + ((directory != null) ? "[" + directory + "]" : "") + + ((file != null) ? file : ""); + return osPath; + } + + /** + * Create a File object for a temporary file in a given directory. Without actually creating the file. + * + *

+ * The file denoted by the returned abstract pathname did not exist before this method was invoked, any subsequent + * invocation of this method will yield a different file name. + *

+ *

+ * The filename is prefixNNNNNsuffix where NNNN is a random number. + *

+ * + * @param prefix prefix before the random number. + * @param suffix file extension; include the '.'. + * @param parentDir Directory to create the temporary file in; java.io.tmpdir used if not specified. + * + * @deprecated since ant 1.7.1 use createTempFile(String, String, File, boolean, boolean) instead. + * @return a File reference to the new, nonexistent temporary file. + */ + public File createTempFile(String prefix, String suffix, File parentDir) { + return createTempFile(prefix, suffix, parentDir, false, false); + } + + private static final String NULL_PLACEHOLDER = "null"; + + /** + * Create a temporary file in a given directory. + * + *

+ * The file denoted by the returned abstract pathname did not exist before this method was invoked, any subsequent + * invocation of this method will yield a different file name.

+ * + * @param prefix prefix before the random number. + * @param suffix file extension; include the '.'. + * @param parentDir Directory to create the temporary file in; java.io.tmpdir used if not specified. + * @param deleteOnExit whether to set the tempfile for deletion on normal VM exit. + * @param createFile true if the file must actually be created. If false chances exist that a file with the same + * name is created in the time between invoking this method and the moment the file is actually created. If possible + * set to true. + * + * @return a File reference to the new temporary file. + * @since Ant 1.7.1 + */ + public File createTempFile(String prefix, String suffix, File parentDir, + boolean deleteOnExit, boolean createFile) { + File result = null; + String parent = (parentDir == null) + ? System.getProperty("java.io.tmpdir") + : parentDir.getPath(); + if (prefix == null) { + prefix = NULL_PLACEHOLDER; + } + if (suffix == null) { + suffix = NULL_PLACEHOLDER; + } + + if (createFile) { + try { + result = File.createTempFile(prefix, suffix, new File(parent)); + } catch (IOException e) { + throw new BuildException("Could not create tempfile in " + + parent, e); + } + } else { + DecimalFormat fmt = new DecimalFormat("#####"); + synchronized (rand) { + do { + result = new File(parent, prefix + + fmt.format(rand.nextInt(Integer.MAX_VALUE)) + suffix); + } while (result.exists()); + } + } + + if (deleteOnExit) { + result.deleteOnExit(); + } + return result; + } + + /** + * Create a File object for a temporary file in a given directory. Without actually creating the file. + * + *

+ * The file denoted by the returned abstract pathname did not exist before this method was invoked, any subsequent + * invocation of this method will yield a different file name. + *

+ *

+ * The filename is prefixNNNNNsuffix where NNNN is a random number. + *

+ * + * @param prefix prefix before the random number. + * @param suffix file extension; include the '.'. + * @param parentDir Directory to create the temporary file in; java.io.tmpdir used if not specified. + * @param deleteOnExit whether to set the tempfile for deletion on normal VM exit. + * + * @deprecated since ant 1.7.1 use createTempFile(String, String, File, boolean, boolean) instead. + * @return a File reference to the new, nonexistent temporary file. + */ + public File createTempFile(String prefix, String suffix, + File parentDir, boolean deleteOnExit) { + return createTempFile(prefix, suffix, parentDir, deleteOnExit, false); + } + +// /** +// * Compares the contents of two files. +// * +// * @param f1 the file whose content is to be compared. +// * @param f2 the other file whose content is to be compared. +// * +// * @return true if the content of the files is the same. +// * +// * @throws IOException if the files cannot be read. +// */ +// public boolean contentEquals(File f1, File f2) throws IOException { +// return contentEquals(f1, f2, false); +// } +// +// /** +// * Compares the contents of two files. +// * +// * @param f1 the file whose content is to be compared. +// * @param f2 the other file whose content is to be compared. +// * @param textfile true if the file is to be treated as a text file and +// * differences in kind of line break are to be ignored. +// * +// * @return true if the content of the files is the same. +// * +// * @throws IOException if the files cannot be read. +// * @since Ant 1.6.3 +// */ +// public boolean contentEquals(File f1, File f2, boolean textfile) throws IOException { +// return ResourceUtils.contentEquals(new FileResource(f1), new FileResource(f2), textfile); +// } + /** + * This was originally an emulation of {@link File#getParentFile} for JDK 1.1, but it is now implemented using that + * method (Ant 1.6.3 onwards). + * + * @param f the file whose parent is required. + * @return the given file's parent, or null if the file does not have a parent. + * @since 1.10 + * @deprecated since 1.7. Just use {@link File#getParentFile} directly. + */ + public File getParentFile(File f) { + return (f == null) ? null : f.getParentFile(); + } + + /** + * Read from reader till EOF. + * + * @param rdr the reader from which to read. + * @return the contents read out of the given reader. + * + * @throws IOException if the contents could not be read out from the reader. + */ + public static String readFully(Reader rdr) throws IOException { + return readFully(rdr, BUF_SIZE); + } + + /** + * Read from reader till EOF. + * + * @param rdr the reader from which to read. + * @param bufferSize the buffer size to use when reading. + * + * @return the contents read out of the given reader. + * + * @throws IOException if the contents could not be read out from the reader. + */ + public static String readFully(Reader rdr, int bufferSize) + throws IOException { + if (bufferSize <= 0) { + throw new IllegalArgumentException("Buffer size must be greater " + + "than 0"); + } + final char[] buffer = new char[bufferSize]; + int bufferLength = 0; + StringBuilder textBuffer = null; + while (bufferLength != -1) { + bufferLength = rdr.read(buffer); + if (bufferLength > 0) { + textBuffer = (textBuffer == null) ? new StringBuilder() : textBuffer; + textBuffer.append(new String(buffer, 0, bufferLength)); + } + } + return (textBuffer == null) ? null : textBuffer.toString(); + } + + /** + * Safe read fully - do not return a null for an empty reader. + * + * @param reader the input to read from. + * @return the string. + * @throws IOException if unable to read from reader. + * @since Ant 1.7.1 + */ + public static String safeReadFully(Reader reader) throws IOException { + String ret = readFully(reader); + return ret == null ? "" : ret; + } + + /** + * This was originally an emulation of File.createNewFile for JDK 1.1, but it is now implemented using that method + * (Ant 1.6.3 onwards). + * + *

+ * This method has historically not guaranteed that the operation was atomic. In its current + * implementation it is. + * + * @param f the file to be created. + * @return true if the file did not exist already. + * @throws IOException on error. + * @since Ant 1.5 + */ + public boolean createNewFile(File f) throws IOException { + return f.createNewFile(); + } + + /** + * Create a new file, optionally creating parent directories. + * + * @param f the file to be created. + * @param mkdirs boolean whether to create parent directories. + * @return true if the file did not exist already. + * @throws IOException on error. + * @since Ant 1.6.3 + */ + public boolean createNewFile(File f, boolean mkdirs) throws IOException { + File parent = f.getParentFile(); + if (mkdirs && !(parent.exists())) { + parent.mkdirs(); + } + return f.createNewFile(); + } + + /** + * Checks whether a given file is a symbolic link. + * + *

+ * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are + * identical--this may lead to false positives on some platforms.

+ * + * @param parent the parent directory of the file to test + * @param name the name of the file to test. + * + * @return true if the file is a symbolic link. + * @throws IOException on error. + * @since Ant 1.5 + * @deprecated use SymbolicLinkUtils instead + */ + public boolean isSymbolicLink(File parent, String name) + throws IOException { + SymbolicLinkUtils u = SymbolicLinkUtils.getSymbolicLinkUtils(); + if (parent == null) { + return u.isSymbolicLink(name); + } + return u.isSymbolicLink(parent, name); + } + + /** + * Removes a leading path from a second path. + * + * @param leading The leading path, must not be null, must be absolute. + * @param path The path to remove from, must not be null, must be absolute. + * + * @return path's normalized absolute if it doesn't start with leading; path's path with leading's path removed + * otherwise. + * + * @since Ant 1.5 + */ + public String removeLeadingPath(File leading, File path) { + String l = normalize(leading.getAbsolutePath()).getAbsolutePath(); + String p = normalize(path.getAbsolutePath()).getAbsolutePath(); + if (l.equals(p)) { + return ""; + } + // ensure that l ends with a / + // so we never think /foo was a parent directory of /foobar + if (!l.endsWith(File.separator)) { + l += File.separator; + } + return (p.startsWith(l)) ? p.substring(l.length()) : p; + } + + /** + * Learn whether one path "leads" another. + * + * @param leading The leading path, must not be null, must be absolute. + * @param path The path to remove from, must not be null, must be absolute. + * @return true if path starts with leading; false otherwise. + * @since Ant 1.7 + */ + public boolean isLeadingPath(File leading, File path) { + String l = normalize(leading.getAbsolutePath()).getAbsolutePath(); + String p = normalize(path.getAbsolutePath()).getAbsolutePath(); + if (l.equals(p)) { + return true; + } + // ensure that l ends with a / + // so we never think /foo was a parent directory of /foobar + if (!l.endsWith(File.separator)) { + l += File.separator; + } + return p.startsWith(l); + } + + /** + * Constructs a file: URI that represents the external form of the given pathname. + * + *

+ * Will be an absolute URI if the given path is absolute.

+ * + *

+ * This code encodes non ASCII characters too.

+ * + *

+ * The coding of the output is the same as what File.toURI().toASCIIString() produces

+ * + * See dt-sysid + * which makes some mention of how characters not supported by URI Reference syntax should be escaped. + * + * @param path the path in the local file system. + * @return the URI version of the local path. + * @since Ant 1.6 + */ + public String toURI(String path) { + return new File(path).toURI().toASCIIString(); + } + + /** + * Constructs a file path from a file: URI. + * + *

+ * Will be an absolute path if the given URI is absolute.

+ * + *

+ * Swallows '%' that are not followed by two characters, doesn't deal with non-ASCII characters.

+ * + * @param uri the URI designating a file in the local filesystem. + * @return the local file system path for the file. + * @since Ant 1.6 + */ + public String fromURI(String uri) { + synchronized (cacheFromUriLock) { + if (uri.equals(cacheFromUriRequest)) { + return cacheFromUriResponse; + } + String path = Locator.fromURI(uri); + String ret = isAbsolutePath(path) ? normalize(path).getAbsolutePath() : path; + cacheFromUriRequest = uri; + cacheFromUriResponse = ret; + return ret; + } + } + + /** + * Compares two filenames. + * + *

+ * Unlike java.io.File#equals this method will try to compare the absolute paths and "normalize" the + * filenames before comparing them.

+ * + * @param f1 the file whose name is to be compared. + * @param f2 the other file whose name is to be compared. + * + * @return true if the file are for the same file. + * + * @since Ant 1.5.3 + */ + public boolean fileNameEquals(File f1, File f2) { + return normalize(f1.getAbsolutePath()).getAbsolutePath().equals( + normalize(f2.getAbsolutePath()).getAbsolutePath()); + } + + /** + * Are the two File instances pointing to the same object on the file system? + * + * @since Ant 1.8.2 + */ + public boolean areSame(File f1, File f2) throws IOException { + if (f1 == null && f2 == null) { + return true; + } + if (f1 == null || f2 == null) { + return false; + } + File f1Normalized = normalize(f1.getAbsolutePath()); + File f2Normalized = normalize(f2.getAbsolutePath()); + return f1Normalized.equals(f2Normalized) + || f1Normalized.getCanonicalFile().equals(f2Normalized + .getCanonicalFile()); + } +// +// /** +// * Renames a file, even if that involves crossing file system boundaries. +// * +// *

+// * This will remove to (if it exists), ensure that to's parent directory exists and move +// * from, which involves deleting from as well.

+// * +// * @param from the file to move. +// * @param to the new file name. +// * +// * @throws IOException if anything bad happens during this process. Note that to may have been deleted +// * already when this happens. +// * +// * @since Ant 1.6 +// */ +// public void rename(File from, File to) throws IOException { +// // identical logic lives in Move.renameFile(): +// from = normalize(from.getAbsolutePath()).getCanonicalFile(); +// to = normalize(to.getAbsolutePath()); +// if (!from.exists()) { +// System.err.println("Cannot rename nonexistent file " + from); +// return; +// } +// if (from.getAbsolutePath().equals(to.getAbsolutePath())) { +// System.err.println("Rename of " + from + " to " + to + " is a no-op."); +// return; +// } +// if (to.exists() && !(areSame(from, to) || tryHardToDelete(to))) { +// throw new IOException("Failed to delete " + to + " while trying to rename " + from); +// } +// File parent = to.getParentFile(); +// if (parent != null && !parent.isDirectory() +// && !(parent.mkdirs() || parent.isDirectory())) { +// throw new IOException("Failed to create directory " + parent +// + " while trying to rename " + from); +// } +// if (!from.renameTo(to)) { +// copyFile(from, to); +// if (!tryHardToDelete(from)) { +// throw new IOException("Failed to delete " + from + " while trying to rename it."); +// } +// } +// } + + /** + * Get the granularity of file timestamps. The choice is made based on OS, which is incorrect--it should really be + * by filesystem. We do not have an easy way to probe for file systems, however, so this heuristic gives us a decent + * default. + * + * @return the difference, in milliseconds, which two file timestamps must have in order for the two files to be + * considered to have different timestamps. + */ + public long getFileTimestampGranularity() { + if (ON_WIN9X) { + return FAT_FILE_TIMESTAMP_GRANULARITY; + } + if (ON_WINDOWS) { + return NTFS_FILE_TIMESTAMP_GRANULARITY; + } + if (ON_DOS) { + return FAT_FILE_TIMESTAMP_GRANULARITY; + } + return UNIX_FILE_TIMESTAMP_GRANULARITY; + } + + /** + * test whether a file or directory exists, with an error in the upper/lower case spelling of the name. Using this + * method is only interesting on case insensitive file systems (Windows).
+ * It will return true only if 3 conditions are met : + *
+ * + *
+ * the purpose is to identify files or directories on case-insensitive filesystems whose case is not what is + * expected.
+ * Possibly to rename them afterwards to the desired upper/lowercase combination. + *
+ * + * @param localFile file to test + * @return true if the file exists and the case of the actual file is not the case of the parameter + * @since Ant 1.7.1 + */ + public boolean hasErrorInCase(File localFile) { + localFile = normalize(localFile.getAbsolutePath()); + if (!localFile.exists()) { + return false; + } + final String localFileName = localFile.getName(); + FilenameFilter ff = new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.equalsIgnoreCase(localFileName) && (!name.equals(localFileName)); + } + }; + String[] names = localFile.getParentFile().list(ff); + return names != null && names.length == 1; + } + + /** + * Returns true if the source is older than the dest. If the dest file does not exist, then the test returns false; + * it is implicitly not up do date. + * + * @param source source file (should be the older). + * @param dest dest file (should be the newer). + * @param granularity an offset added to the source time. + * @return true if the source is older than the dest after accounting for granularity. + * @since Ant 1.6.3 + */ + public boolean isUpToDate(File source, File dest, long granularity) { + //do a check for the destination file existing + if (!dest.exists()) { + //if it does not, then the file is not up to date. + return false; + } + long sourceTime = source.lastModified(); + long destTime = dest.lastModified(); + return isUpToDate(sourceTime, destTime, granularity); + } + + /** + * Returns true if the source is older than the dest. + * + * @param source source file (should be the older). + * @param dest dest file (should be the newer). + * @return true if the source is older than the dest, taking the granularity into account. + * @since Ant 1.6.3 + */ + public boolean isUpToDate(File source, File dest) { + return isUpToDate(source, dest, getFileTimestampGranularity()); + } + + /** + * Compare two timestamps for being up to date using the specified granularity. + * + * @param sourceTime timestamp of source file. + * @param destTime timestamp of dest file. + * @param granularity os/filesys granularity. + * @return true if the dest file is considered up to date. + */ + public boolean isUpToDate(long sourceTime, long destTime, long granularity) { + return destTime != -1 && destTime >= sourceTime + granularity; + } + + /** + * Compare two timestamps for being up to date using the current granularity. + * + * @param sourceTime timestamp of source file. + * @param destTime timestamp of dest file. + * @return true if the dest file is considered up to date. + */ + public boolean isUpToDate(long sourceTime, long destTime) { + return isUpToDate(sourceTime, destTime, getFileTimestampGranularity()); + } + + /** + * Close a Writer without throwing any exception if something went wrong. Do not attempt to close it if the argument + * is null. + * + * @param device output writer, can be null. + */ + public static void close(Writer device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Close a Reader without throwing any exception if something went wrong. Do not attempt to close it if the argument + * is null. + * + * @param device Reader, can be null. + */ + public static void close(Reader device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Close a stream without throwing any exception if something went wrong. Do not attempt to close it if the argument + * is null. + * + * @param device stream, can be null. + */ + public static void close(OutputStream device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Close a stream without throwing any exception if something went wrong. Do not attempt to close it if the argument + * is null. + * + * @param device stream, can be null. + */ + public static void close(InputStream device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Close a Channel without throwing any exception if something went wrong. Do not attempt to close it if the + * argument is null. + * + * @param device channel, can be null. + * @since Ant 1.8.0 + */ + public static void close(Channel device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Closes an URLConnection if its concrete implementation provides a way to close it that Ant knows of. + * + * @param conn connection, can be null + * @since Ant 1.8.0 + */ + public static void close(URLConnection conn) { + if (conn != null) { + try { + if (conn instanceof JarURLConnection) { + JarURLConnection juc = (JarURLConnection) conn; + JarFile jf = juc.getJarFile(); + jf.close(); + jf = null; + } else if (conn instanceof HttpURLConnection) { + ((HttpURLConnection) conn).disconnect(); + } + } catch (IOException exc) { + //ignore + } + } + } + + /** + * Delete the file with {@link File#delete()} if the argument is not null. Do nothing on a null argument. + * + * @param file file to delete. + */ + public static void delete(File file) { + if (file != null) { + file.delete(); + } + } + + /** + * Accommodate Windows bug encountered in both Sun and IBM JDKs. Others possible. If the delete does not work, call + * System.gc(), wait a little and try again. + * + * @return whether deletion was successful + * @since Ant 1.8.0 + */ + public boolean tryHardToDelete(File f) { + return tryHardToDelete(f, ON_WINDOWS); + } + + /** + * If delete does not work, call System.gc() if asked to, wait a little and try again. + * + * @return whether deletion was successful + * @since Ant 1.8.3 + */ + public boolean tryHardToDelete(File f, boolean runGC) { + if (!f.delete()) { + if (runGC) { + System.gc(); + } + try { + Thread.sleep(DELETE_RETRY_SLEEP_MILLIS); + } catch (InterruptedException ex) { + // Ignore Exception + } + return f.delete(); + } + return true; + } + + /** + * Calculates the relative path between two files. + *

+ * Implementation note:
This function may throw an IOException if an I/O error occurs because its use of the + * canonical pathname may require filesystem queries. + *

+ * + * @param fromFile the File to calculate the path from + * @param toFile the File to calculate the path to + * @return the relative path between the files + * @throws Exception for undocumented reasons + * @see File#getCanonicalPath() + * + * @since Ant 1.7 + */ + public static String getRelativePath(File fromFile, File toFile) throws Exception { + String fromPath = fromFile.getCanonicalPath(); + String toPath = toFile.getCanonicalPath(); + + // build the path stack info to compare + String[] fromPathStack = getPathStack(fromPath); + String[] toPathStack = getPathStack(toPath); + + if (0 < toPathStack.length && 0 < fromPathStack.length) { + if (!fromPathStack[0].equals(toPathStack[0])) { + // not the same device (would be "" on Linux/Unix) + + return getPath(Arrays.asList(toPathStack)); + } + } else { + // no comparison possible + return getPath(Arrays.asList(toPathStack)); + } + + int minLength = Math.min(fromPathStack.length, toPathStack.length); + int same = 1; // Used outside the for loop + + // get index of parts which are equal + for (; + same < minLength && fromPathStack[same].equals(toPathStack[same]); + same++) { + // Do nothing + } + + List relativePathStack = new ArrayList(); + + // if "from" part is longer, fill it up with ".." + // to reach path which is equal to both paths + for (int i = same; i < fromPathStack.length; i++) { + relativePathStack.add(".."); + } + + // fill it up path with parts which were not equal + for (int i = same; i < toPathStack.length; i++) { + relativePathStack.add(toPathStack[i]); + } + + return getPath(relativePathStack); + } + + /** + * Gets all names of the path as an array of Strings. + * + * @param path to get names from + * @return Strings, never null + * + * @since Ant 1.7 + */ + public static String[] getPathStack(String path) { + String normalizedPath = path.replace(File.separatorChar, '/'); + + return normalizedPath.split("/"); + } + + /** + * Gets path from a List of Strings. + * + * @param pathStack List of Strings to be concatenated as a path. + * @return String, never null + * + * @since Ant 1.7 + */ + public static String getPath(List pathStack) { + // can safely use '/' because Windows understands '/' as separator + return getPath(pathStack, '/'); + } + + /** + * Gets path from a List of Strings. + * + * @param pathStack List of Strings to be concated as a path. + * @param separatorChar char to be used as separator between names in path + * @return String, never null + * + * @since Ant 1.7 + */ + public static String getPath(final List pathStack, final char separatorChar) { + final StringBuilder buffer = new StringBuilder(); + + final Iterator iter = pathStack.iterator(); + if (iter.hasNext()) { + buffer.append(iter.next()); + } + while (iter.hasNext()) { + buffer.append(separatorChar); + buffer.append(iter.next()); + } + return buffer.toString(); + } + + /** + * Get the default encoding. This is done by opening an InputStreamReader on a dummy InputStream and getting the + * encoding. Could use System.getProperty("file.encoding"), but cannot see where this is documented. + * + * @return the default file encoding. + */ + public String getDefaultEncoding() { + InputStreamReader is = new InputStreamReader( + new InputStream() { + public int read() { + return -1; + } + }); + try { + return is.getEncoding(); + } finally { + close(is); + } + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java index ba3f06c08..b066bb884 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java @@ -22,8 +22,6 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; import java.util.UUID; /**