/*************************************************************************
* *
* CESeCore: CE Security Core *
* *
* This software is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or any later version. *
* *
* See terms of license at gnu.org. *
* *
*************************************************************************/
package org.cesecore.util;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.text.Collator;
import java.util.Arrays;
import java.util.Comparator;
import org.apache.log4j.Logger;
/**
* Tools to handle some common file operations.
*
* @version $Id: FileTools.java 27126 2017-11-13 09:28:54Z anatom $
*/
public abstract class FileTools {
private static final Logger log = Logger.getLogger(FileTools.class);
/**
* Reads binary bytes from a PEM-file. The PEM-file may contain other stuff, the first item
* between beginKey and endKey is read. Example: -----BEGIN CERTIFICATE REQUEST-----
* base64 encoded PKCS10 certification request -----END CERTIFICATE REQUEST-----
*
* @param inbuf input buffer containing PEM-formatted stuff.
* @param beginKey begin line of PEM message
* @param endKey end line of PEM message
*
* @return byte[] containing binary Base64 decoded bytes.
*
* @throws IOException if the PEM file does not contain the right keys.
*/
public static byte[] getBytesFromPEM(final byte[] inbuf, final String beginKey, final String endKey)
throws IOException {
final ByteArrayInputStream instream = new ByteArrayInputStream(inbuf);
return getBytesFromPEM(instream, beginKey, endKey);
} // getBytesfromPEM
public static byte[] getBytesFromPEM(final InputStream instream, final String beginKey, final String endKey)
throws IOException {
if (log.isTraceEnabled()) {
log.trace(">getBytesFromPEM");
}
final BufferedReader bufRdr = new BufferedReader(new InputStreamReader(instream));
final ByteArrayOutputStream ostr = new ByteArrayOutputStream();
final PrintStream opstr = new PrintStream(ostr);
String temp;
while (((temp = bufRdr.readLine()) != null) && !temp.equals(beginKey)) {
continue;
}
if (temp == null) {
throw new IOException("Error in input buffer, missing " + beginKey + " boundary");
}
while (((temp = bufRdr.readLine()) != null) && !temp.equals(endKey)) {
// Skip empty lines
if (temp.trim().length() > 0) {
opstr.print(temp);
}
}
if (temp == null) {
throw new IOException("Error in input buffer, missing " + endKey + " boundary");
}
opstr.close();
final byte[] bytes;
try {
bytes = Base64.decode(ostr.toByteArray());
} catch (Exception e) {
throw new IOException("Malformed PEM encoding or PEM of unknown type: " + e.getMessage());
}
if (log.isTraceEnabled()) {
log.trace(" 0) {
os.write(buf, 0, len);
}
in.close();
os.close();
return os.toByteArray();
} catch(IOException e) {
throw new RuntimeException("Caught IOException for unknown reason", e);
}
}
/**
* Sort the files by name with directories first.
*/
public static void sortByName(final File[] files) {
if (files == null) {
return;
}
Arrays.sort(files, new FileComp());
}
private static class FileComp implements Comparator {
private final Collator c = Collator.getInstance();
@Override
public int compare(final File f1, final File f2) {
if(f1 == f2) {
return 0;
}
if(f1.isDirectory() && f2.isFile()) {
return -1;
}
if(f1.isFile() && f2.isDirectory()) {
return 1;
}
return c.compare(f1.getName(), f2.getName());
}
}
public static File createTempDirectory() throws IOException {
return createTempDirectory(null);
}
public static File createTempDirectory(File location) throws IOException {
final File temp = File.createTempFile("tmp", Long.toString(System.nanoTime()), location);
if (!(temp.delete())) {
throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
}
//Known race condition exists here, not sure what an attacker would accomplish with it though
if (!temp.mkdir()) {
throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
}
return temp;
}
/**
* Recursively deletes a file. If file is a directory, then it will delete all files and subdirectories contained.
*
* @param file the file to delete
*/
public static void delete(File file) {
if (file.isDirectory()) {
for (File subFile : file.listFiles()) {
delete(subFile);
}
}
if (!file.delete()) {
log.error("Could not delete directory " + file.getAbsolutePath());
}
}
/**
* Copies the data from an input stream to an output stream. A limit on the file size is imposed.
*
* @param input Stream to copy from.
* @param output Stream to copy to.
* @param maxBytes Throw a SizeLimitExceededException if more than this number of bytes are read.
* @return The number of bytes copied.
* @throws IOException If reading from or writing to the streams fail.
* @throws StreamSizeLimitExceededException If more than maxBytes are read.
*/
public static long streamCopyWithLimit(final InputStream input, final OutputStream output, final long maxBytes) throws IOException, StreamSizeLimitExceededException {
if (maxBytes <= 0) {
throw new StreamSizeLimitExceededException("Size limit was reached");
}
final byte[] buff = new byte[16*1024];
long bytesCopied = 0;
while (true) {
int len = input.read(buff);
if (len <= 0) { break; }
bytesCopied += len;
if (bytesCopied > maxBytes) {
throw new StreamSizeLimitExceededException("Size limit was reached");
}
output.write(buff, 0, len);
}
return bytesCopied;
}
}