mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-15 01:07:42 +00:00
350 lines
11 KiB
Java
350 lines
11 KiB
Java
/*
|
|
* Autopsy Forensic Browser
|
|
*
|
|
* Copyright 2011 Basis Technology Corp.
|
|
* Contact: carrier <at> sleuthkit <dot> org
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package org.sleuthkit.autopsy.datamodel;
|
|
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.TimeZone;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
import org.sleuthkit.datamodel.Content;
|
|
import org.sleuthkit.datamodel.ContentVisitor;
|
|
import org.sleuthkit.datamodel.Directory;
|
|
import org.sleuthkit.datamodel.File;
|
|
import org.sleuthkit.datamodel.FileSystem;
|
|
import org.sleuthkit.datamodel.FsContent;
|
|
import org.sleuthkit.datamodel.Image;
|
|
import org.sleuthkit.datamodel.LayoutFile;
|
|
import org.sleuthkit.datamodel.ReadContentInputStream;
|
|
import org.sleuthkit.datamodel.TskException;
|
|
import org.sleuthkit.datamodel.Volume;
|
|
import org.sleuthkit.datamodel.VolumeSystem;
|
|
|
|
/**
|
|
* Static class of utility methods for Content objects
|
|
*/
|
|
public final class ContentUtils {
|
|
|
|
private final static Logger logger = Logger.getLogger(ContentUtils.class.getName());
|
|
private static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
|
// don't instantiate
|
|
private ContentUtils() {
|
|
throw new AssertionError();
|
|
}
|
|
|
|
private static final ShortNameVisitor shortName = new ShortNameVisitor();
|
|
|
|
private static final GetPathVisitor getDisplayPath = new GetPathVisitor(shortName);
|
|
|
|
/**
|
|
* Returns full path to this node.
|
|
*
|
|
* @return the path of this node
|
|
*/
|
|
public static String[] getDisplayPath(Content content) {
|
|
return content.accept(getDisplayPath).toArray(new String[]{});
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert epoch seconds to a string value in the given time zone
|
|
* @param epochSeconds
|
|
* @param tzone
|
|
* @return
|
|
*/
|
|
public static String getStringTime(long epochSeconds, TimeZone tzone) {
|
|
String time = "0000-00-00 00:00:00";
|
|
if (epochSeconds != 0) {
|
|
dateFormatter.setTimeZone(tzone);
|
|
time = dateFormatter.format(new java.util.Date(epochSeconds * 1000));
|
|
}
|
|
return time;
|
|
}
|
|
|
|
/**
|
|
* Convert epoch seconds to a string value (convenience method)
|
|
* @param epochSeconds
|
|
* @param c
|
|
* @return
|
|
*/
|
|
public static String getStringTime(long epochSeconds, Content c) {
|
|
return getStringTime(epochSeconds, getTimeZone(c));
|
|
}
|
|
|
|
public static TimeZone getTimeZone(Content c) {
|
|
try {
|
|
return TimeZone.getTimeZone(c.getImage().getTimeZone());
|
|
} catch(TskException ex) {
|
|
return TimeZone.getDefault();
|
|
}
|
|
}
|
|
|
|
private static final SystemNameVisitor systemName = new SystemNameVisitor();
|
|
|
|
private static final GetPathVisitor getSystemPath = new GetPathVisitor(systemName);
|
|
|
|
/**
|
|
* Returns full path to this node.
|
|
*
|
|
* @return the path of this node
|
|
*/
|
|
public static String[] getSystemPath(Content content) {
|
|
return content.accept(getSystemPath).toArray(new String[]{});
|
|
}
|
|
|
|
static String getSystemName(Content content) {
|
|
return content.accept(systemName);
|
|
}
|
|
|
|
private static class SystemNameVisitor extends ContentVisitor.Default<String> {
|
|
SystemNameVisitor() {}
|
|
|
|
@Override
|
|
protected String defaultVisit(Content cntnt) {
|
|
return cntnt.accept(shortName) + ":" + Long.toString(cntnt.getId());
|
|
}
|
|
}
|
|
|
|
private static class ShortNameVisitor extends ContentVisitor.Default<String> {
|
|
ShortNameVisitor() {}
|
|
|
|
@Override
|
|
protected String defaultVisit(Content cntnt) {
|
|
return cntnt.getName();
|
|
}
|
|
}
|
|
|
|
private static class GetPathVisitor implements ContentVisitor<List<String>> {
|
|
ContentVisitor<String> toString;
|
|
|
|
GetPathVisitor(ContentVisitor<String> toString) {
|
|
this.toString = toString;
|
|
}
|
|
|
|
@Override
|
|
public List<String> visit(LayoutFile lay) {
|
|
List<String> path = lay.getParent().accept(this);
|
|
path.add(toString.visit(lay));
|
|
return path;
|
|
}
|
|
|
|
@Override
|
|
public List<String> visit(Directory dir) {
|
|
List<String> path;
|
|
|
|
if (dir.isRoot()) {
|
|
path = dir.getFileSystem().accept(this);
|
|
} else {
|
|
try {
|
|
path = dir.getParentDirectory().accept(this);
|
|
path.add(toString.visit(dir));
|
|
} catch (TskException ex) {
|
|
throw new RuntimeException("Couldn't get directory path.", ex);
|
|
}
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
@Override
|
|
public List<String> visit(File file) {
|
|
try {
|
|
List<String> path = file.getParentDirectory().accept(this);
|
|
path.add(toString.visit(file));
|
|
return path;
|
|
} catch (TskException ex) {
|
|
throw new RuntimeException("Couldn't get file path.", ex);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public List<String> visit(FileSystem fs) {
|
|
return fs.getParent().accept(this);
|
|
}
|
|
|
|
@Override
|
|
public List<String> visit(Image image) {
|
|
List<String> path = new LinkedList<String>();
|
|
path.add(toString.visit(image));
|
|
return path;
|
|
}
|
|
|
|
@Override
|
|
public List<String> visit(Volume volume) {
|
|
List<String> path = volume.getParent().accept(this);
|
|
path.add(toString.visit(volume));
|
|
return path;
|
|
}
|
|
|
|
@Override
|
|
public List<String> visit(VolumeSystem vs) {
|
|
return vs.getParent().accept(this);
|
|
}
|
|
}
|
|
|
|
|
|
private static final int TO_FILE_BUFFER_SIZE = 8192;
|
|
|
|
/**
|
|
* Reads all the data from any content object and writes it to a file.
|
|
* @param content Any content object.
|
|
* @param outputFile Will be created if it doesn't exist, and overwritten if
|
|
* it does
|
|
* @throws IOException
|
|
*/
|
|
public static void writeToFile(Content content, java.io.File outputFile) throws IOException {
|
|
|
|
InputStream in = new ReadContentInputStream(content);
|
|
|
|
boolean append = false;
|
|
FileOutputStream out = new FileOutputStream(outputFile, append);
|
|
|
|
try {
|
|
byte[] buffer = new byte[TO_FILE_BUFFER_SIZE];
|
|
int len = in.read(buffer);
|
|
while (len != -1) {
|
|
out.write(buffer, 0, len);
|
|
len = in.read(buffer);
|
|
}
|
|
} finally {
|
|
out.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper to ignore the '.' and '..' directories
|
|
*/
|
|
public static boolean isDotDirectory(Directory dir) {
|
|
String name = dir.getName();
|
|
return name.equals(".") || name.equals("..");
|
|
}
|
|
|
|
|
|
/**
|
|
* Extracts file/folder as given destination file, recursing into folders.
|
|
* Assumes there will be no collisions with existing directories/files, and
|
|
* that the directory to contain the destination file already exists.
|
|
*/
|
|
public static class ExtractFscContentVisitor extends ContentVisitor.Default<Void> {
|
|
|
|
java.io.File dest;
|
|
|
|
/**
|
|
* Make new extractor for a specific destination
|
|
* @param dest The file/folder visited will be extracted as this file
|
|
*/
|
|
public ExtractFscContentVisitor(java.io.File dest) {
|
|
this.dest = dest;
|
|
}
|
|
|
|
/**
|
|
* Convenience method to make a new instance for given destination
|
|
* and extract given content
|
|
*/
|
|
public static void extract(Content cntnt, java.io.File dest) {
|
|
cntnt.accept(new ExtractFscContentVisitor(dest));
|
|
}
|
|
|
|
public Void visit(File f) {
|
|
try {
|
|
ContentUtils.writeToFile(f, dest);
|
|
} catch (IOException ex) {
|
|
logger.log(Level.SEVERE,
|
|
"Trouble extracting file to " + dest.getAbsolutePath(),
|
|
ex);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public Void visit(Directory dir) {
|
|
|
|
// don't extract . and .. directories
|
|
if (isDotDirectory(dir)) {
|
|
return null;
|
|
}
|
|
|
|
dest.mkdir();
|
|
|
|
// member visitor to generate destination files for children
|
|
DestFileContentVisitor destFileCV = new DestFileContentVisitor();
|
|
|
|
try {
|
|
// recurse on children
|
|
for (Content child : dir.getChildren()) {
|
|
java.io.File childFile = child.accept(destFileCV);
|
|
ExtractFscContentVisitor childVisitor =
|
|
new ExtractFscContentVisitor(childFile);
|
|
child.accept(childVisitor);
|
|
}
|
|
} catch (TskException ex) {
|
|
logger.log(Level.SEVERE,
|
|
"Trouble fetching children to extract.", ex);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
protected Void defaultVisit(Content cntnt) {
|
|
throw new UnsupportedOperationException("Can't extract a "
|
|
+ cntnt.getClass().getSimpleName());
|
|
}
|
|
|
|
/**
|
|
* Helper visitor to get the destination file for a child Content object
|
|
*/
|
|
private class DestFileContentVisitor extends
|
|
ContentVisitor.Default<java.io.File> {
|
|
|
|
/**
|
|
* Get destination file by adding File/Directory name to the path
|
|
* of parent
|
|
*/
|
|
private java.io.File getFsContentDest(FsContent fsc) {
|
|
String path = dest.getAbsolutePath() + java.io.File.separator
|
|
+ fsc.getName();
|
|
return new java.io.File(path);
|
|
}
|
|
|
|
@Override
|
|
public java.io.File visit(File f) {
|
|
return getFsContentDest(f);
|
|
}
|
|
|
|
@Override
|
|
public java.io.File visit(Directory dir) {
|
|
return getFsContentDest(dir);
|
|
}
|
|
|
|
@Override
|
|
protected java.io.File defaultVisit(Content cntnt) {
|
|
throw new UnsupportedOperationException("Can't get destination file for a "
|
|
+ cntnt.getClass().getSimpleName());
|
|
}
|
|
}
|
|
}
|
|
}
|