mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
commit
e3f663bcc3
@ -392,85 +392,69 @@ public final class ContentUtils {
|
|||||||
public static <T, V> void extract(Content cntnt, java.io.File dest, ProgressHandle progress, SwingWorker<T, V> worker) {
|
public static <T, V> void extract(Content cntnt, java.io.File dest, ProgressHandle progress, SwingWorker<T, V> worker) {
|
||||||
cntnt.accept(new ExtractFscContentVisitor<>(dest, progress, worker, true));
|
cntnt.accept(new ExtractFscContentVisitor<>(dest, progress, worker, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base method writing a file to disk.
|
||||||
|
*
|
||||||
|
* @param file The TSK content file.
|
||||||
|
* @param dest The disk location where the content will be written.
|
||||||
|
* @param progress progress bar handle to update, if available. null
|
||||||
|
* otherwise
|
||||||
|
* @param worker the swing worker background thread the process runs
|
||||||
|
* within, or null, if in the main thread, used to
|
||||||
|
* handle task cancellation
|
||||||
|
* @param source true if source file
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
protected void writeFile(Content file, java.io.File dest, ProgressHandle progress, SwingWorker<T, V> worker, boolean source) throws IOException {
|
||||||
|
ContentUtils.writeToFile(file, dest, progress, worker, source);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public Void visit(File file) {
|
* Visits a TSK content file and writes that file to disk.
|
||||||
|
* @param file The file to be written.
|
||||||
|
* @param fileType The file type (i.e. "derived file") for error logging.
|
||||||
|
* @return null.
|
||||||
|
*/
|
||||||
|
protected Void visitFile(Content file, String fileType) {
|
||||||
try {
|
try {
|
||||||
ContentUtils.writeToFile(file, dest, progress, worker, source);
|
writeFile(file, dest, progress, worker, source);
|
||||||
} catch (ReadContentInputStreamException ex) {
|
} catch (ReadContentInputStreamException ex) {
|
||||||
logger.log(Level.WARNING,
|
logger.log(Level.WARNING,
|
||||||
String.format("Error reading file '%s' (id=%d).",
|
String.format("Error reading file '%s' (id=%d).",
|
||||||
file.getName(), file.getId()), ex); //NON-NLS
|
file.getName(), file.getId()), ex); //NON-NLS
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.SEVERE,
|
logger.log(Level.SEVERE,
|
||||||
String.format("Error extracting file '%s' (id=%d) to '%s'.",
|
String.format("Error extracting %s '%s' (id=%d) to '%s'.",
|
||||||
file.getName(), file.getId(), dest.getAbsolutePath()), ex); //NON-NLS
|
fileType, file.getName(), file.getId(), dest.getAbsolutePath()), ex); //NON-NLS
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visit(File file) {
|
||||||
|
return visitFile(file, "file");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(LayoutFile file) {
|
public Void visit(LayoutFile file) {
|
||||||
try {
|
return visitFile(file, "unallocated content file");
|
||||||
ContentUtils.writeToFile(file, dest, progress, worker, source);
|
|
||||||
} catch (ReadContentInputStreamException ex) {
|
|
||||||
logger.log(Level.WARNING,
|
|
||||||
String.format("Error reading file '%s' (id=%d).",
|
|
||||||
file.getName(), file.getId()), ex); //NON-NLS
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.SEVERE,
|
|
||||||
String.format("Error extracting unallocated content file '%s' (id=%d) to '%s'.",
|
|
||||||
file.getName(), file.getId(), dest.getAbsolutePath()), ex); //NON-NLS
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(DerivedFile file) {
|
public Void visit(DerivedFile file) {
|
||||||
try {
|
return visitFile(file, "derived file");
|
||||||
ContentUtils.writeToFile(file, dest, progress, worker, source);
|
|
||||||
} catch (ReadContentInputStreamException ex) {
|
|
||||||
logger.log(Level.WARNING,
|
|
||||||
String.format("Error reading file '%s' (id=%d).",
|
|
||||||
file.getName(), file.getId()), ex); //NON-NLS
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.SEVERE,
|
|
||||||
String.format("Error extracting derived file '%s' (id=%d) to '%s'.",
|
|
||||||
file.getName(), file.getId(), dest.getAbsolutePath()), ex); //NON-NLS
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(LocalFile file) {
|
public Void visit(LocalFile file) {
|
||||||
try {
|
return visitFile(file, "local file");
|
||||||
ContentUtils.writeToFile(file, dest, progress, worker, source);
|
|
||||||
} catch (ReadContentInputStreamException ex) {
|
|
||||||
logger.log(Level.WARNING,
|
|
||||||
String.format("Error reading file '%s' (id=%d).",
|
|
||||||
file.getName(), file.getId()), ex); //NON-NLS
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.SEVERE,
|
|
||||||
String.format("Error extracting local file '%s' (id=%d) to '%s'.",
|
|
||||||
file.getName(), file.getId(), dest.getAbsolutePath()), ex); //NON-NLS
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(SlackFile file) {
|
public Void visit(SlackFile file) {
|
||||||
try {
|
return visitFile(file, "slack file");
|
||||||
ContentUtils.writeToFile(file, dest, progress, worker, source);
|
|
||||||
} catch (ReadContentInputStreamException ex) {
|
|
||||||
logger.log(Level.WARNING,
|
|
||||||
String.format("Error reading file '%s' (id=%d).",
|
|
||||||
file.getName(), file.getId()), ex); //NON-NLS
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.SEVERE,
|
|
||||||
String.format("Error extracting slack file '%s' (id=%d) to '%s'.",
|
|
||||||
file.getName(), file.getId(), dest.getAbsolutePath()), ex); //NON-NLS
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -493,6 +477,20 @@ public final class ContentUtils {
|
|||||||
+ content.getName();
|
+ content.getName();
|
||||||
return new java.io.File(path);
|
return new java.io.File(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a visitor to visit any child content.
|
||||||
|
* @param childFile The disk location where the content will be written.
|
||||||
|
* @param progress progress bar handle to update, if available. null
|
||||||
|
* otherwise
|
||||||
|
* @param worker the swing worker background thread the process runs
|
||||||
|
* within, or null, if in the main thread, used to
|
||||||
|
* handle task cancellation
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected ExtractFscContentVisitor<T, V> getChildVisitor(java.io.File childFile, ProgressHandle progress, SwingWorker<T, V> worker) {
|
||||||
|
return new ExtractFscContentVisitor<>(childFile, progress, worker, false);
|
||||||
|
}
|
||||||
|
|
||||||
public Void visitDir(AbstractFile dir) {
|
public Void visitDir(AbstractFile dir) {
|
||||||
|
|
||||||
@ -509,8 +507,7 @@ public final class ContentUtils {
|
|||||||
for (Content child : dir.getChildren()) {
|
for (Content child : dir.getChildren()) {
|
||||||
if (child instanceof AbstractFile) { //ensure the directory's artifact children are ignored
|
if (child instanceof AbstractFile) { //ensure the directory's artifact children are ignored
|
||||||
java.io.File childFile = getFsContentDest(child);
|
java.io.File childFile = getFsContentDest(child);
|
||||||
ExtractFscContentVisitor<T, V> childVisitor
|
ExtractFscContentVisitor<T, V> childVisitor = getChildVisitor(childFile, progress, worker);
|
||||||
= new ExtractFscContentVisitor<>(childFile, progress, worker, false);
|
|
||||||
// If this is the source directory of an extract it
|
// If this is the source directory of an extract it
|
||||||
// will have a progress and worker, and will keep track
|
// will have a progress and worker, and will keep track
|
||||||
// of the progress bar's progress
|
// of the progress bar's progress
|
||||||
|
@ -43,6 +43,8 @@ public final class ExtractAction extends AbstractAction {
|
|||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final ExtractActionHelper extractor = new ExtractActionHelper();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor for the action.
|
* Private constructor for the action.
|
||||||
@ -61,7 +63,6 @@ public final class ExtractAction extends AbstractAction {
|
|||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
Lookup lookup = Utilities.actionsGlobalContext();
|
Lookup lookup = Utilities.actionsGlobalContext();
|
||||||
Collection<? extends AbstractFile> selectedFiles =lookup.lookupAll(AbstractFile.class);
|
Collection<? extends AbstractFile> selectedFiles =lookup.lookupAll(AbstractFile.class);
|
||||||
ExtractActionHelper extractor = new ExtractActionHelper();
|
|
||||||
extractor.extract(e, selectedFiles);
|
extractor.extract(e, selectedFiles);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.directorytree.actionhelpers;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -42,8 +44,10 @@ import org.sleuthkit.autopsy.coreutils.FileUtil;
|
|||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor;
|
||||||
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
|
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for methods needed by actions which extract files.
|
* Helper class for methods needed by actions which extract files.
|
||||||
@ -52,7 +56,7 @@ public class ExtractActionHelper {
|
|||||||
|
|
||||||
private final Logger logger = Logger.getLogger(ExtractActionHelper.class.getName());
|
private final Logger logger = Logger.getLogger(ExtractActionHelper.class.getName());
|
||||||
private String userDefinedExportPath;
|
private String userDefinedExportPath;
|
||||||
|
|
||||||
private final JFileChooserFactory extractFileHelper = new JFileChooserFactory();
|
private final JFileChooserFactory extractFileHelper = new JFileChooserFactory();
|
||||||
private final JFileChooserFactory extractFilesHelper = new JFileChooserFactory();
|
private final JFileChooserFactory extractFilesHelper = new JFileChooserFactory();
|
||||||
|
|
||||||
@ -68,12 +72,7 @@ public class ExtractActionHelper {
|
|||||||
if (selectedFiles.size() > 1) {
|
if (selectedFiles.size() > 1) {
|
||||||
extractFiles(event, selectedFiles);
|
extractFiles(event, selectedFiles);
|
||||||
} else if (selectedFiles.size() == 1) {
|
} else if (selectedFiles.size() == 1) {
|
||||||
AbstractFile source = selectedFiles.iterator().next();
|
extractFile(event, selectedFiles.iterator().next());
|
||||||
if (source.isDir()) {
|
|
||||||
extractFiles(event, selectedFiles);
|
|
||||||
} else {
|
|
||||||
extractFile(event, selectedFiles.iterator().next());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +82,11 @@ public class ExtractActionHelper {
|
|||||||
* @param event
|
* @param event
|
||||||
* @param selectedFile Selected file
|
* @param selectedFile Selected file
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages({"ExtractActionHelper.noOpenCase.errMsg=No open case available."})
|
@NbBundle.Messages({"ExtractActionHelper.noOpenCase.errMsg=No open case available.",
|
||||||
|
"ExtractActionHelper.extractOverwrite.title=Export to csv file",
|
||||||
|
"# {0} - fileName",
|
||||||
|
"ExtractActionHelper.extractOverwrite.msg=A file already exists at {0}. Do you want to overwrite the existing file?"
|
||||||
|
})
|
||||||
private void extractFile(ActionEvent event, AbstractFile selectedFile) {
|
private void extractFile(ActionEvent event, AbstractFile selectedFile) {
|
||||||
Case openCase;
|
Case openCase;
|
||||||
try {
|
try {
|
||||||
@ -269,6 +272,68 @@ public class ExtractActionHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A file content extraction visitor that handles for the UI designed to
|
||||||
|
* handle file name conflicts by appending the object id to the file name.
|
||||||
|
*/
|
||||||
|
private static class UIExtractionVisitor<T, V> extends ExtractFscContentVisitor<T, V> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param file The TSK content file.
|
||||||
|
* @param dest The disk location where the content will be written.
|
||||||
|
* @param progress progress bar handle to update, if available. null
|
||||||
|
* otherwise
|
||||||
|
* @param worker the swing worker background thread the process runs
|
||||||
|
* within, or null, if in the main thread, used to
|
||||||
|
* handle task cancellation
|
||||||
|
* @param source true if source file
|
||||||
|
*/
|
||||||
|
UIExtractionVisitor(File dest, ProgressHandle progress, SwingWorker<T, V> worker, boolean source) {
|
||||||
|
super(dest, progress, worker, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes content and children to disk.
|
||||||
|
*
|
||||||
|
* @param content The root content.
|
||||||
|
* @param file The TSK content file.
|
||||||
|
* @param dest The disk location where the content will be written.
|
||||||
|
* @param progress progress bar handle to update, if available. null
|
||||||
|
* otherwise
|
||||||
|
* @param worker the swing worker background thread the process runs
|
||||||
|
* within, or null, if in the main thread, used to
|
||||||
|
* handle task cancellation
|
||||||
|
* @param source true if source file
|
||||||
|
*/
|
||||||
|
static <T,V> void writeContent(Content content, File dest, ProgressHandle progress, SwingWorker<T, V> worker) {
|
||||||
|
content.accept(new UIExtractionVisitor<>(dest, progress, worker, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeFile(Content file, File dest, ProgressHandle progress, SwingWorker<T, V> worker, boolean source) throws IOException {
|
||||||
|
File destFile;
|
||||||
|
if (dest.exists()) {
|
||||||
|
String parent = dest.getParent();
|
||||||
|
String fileName = dest.getName();
|
||||||
|
String objIdFileName = MessageFormat.format("{0}-{1}", file.getId(), fileName);
|
||||||
|
destFile = new File(parent, objIdFileName);
|
||||||
|
} else {
|
||||||
|
destFile = dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.writeFile(file, destFile, progress, worker, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ExtractFscContentVisitor<T, V> getChildVisitor(File childFile, ProgressHandle progress, SwingWorker<T, V> worker) {
|
||||||
|
return new UIExtractionVisitor(childFile, progress, worker, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thread that does the actual extraction work
|
* Thread that does the actual extraction work
|
||||||
*/
|
*/
|
||||||
@ -321,8 +386,7 @@ public class ExtractActionHelper {
|
|||||||
// Do the extraction tasks.
|
// Do the extraction tasks.
|
||||||
for (FileExtractionTask task : this.extractionTasks) {
|
for (FileExtractionTask task : this.extractionTasks) {
|
||||||
progress.progress(Bundle.ExtractActionHelper_progress_fileExtracting(task.destination.getName()));
|
progress.progress(Bundle.ExtractActionHelper_progress_fileExtracting(task.destination.getName()));
|
||||||
|
UIExtractionVisitor.writeContent(task.source, task.destination, null, this);
|
||||||
ContentUtils.ExtractFscContentVisitor.extract(task.source, task.destination, null, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.ingest;
|
package org.sleuthkit.autopsy.ingest;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -950,18 +951,25 @@ final class IngestJobExecutor {
|
|||||||
currentDataSourceIngestModuleCancelled = false;
|
currentDataSourceIngestModuleCancelled = false;
|
||||||
cancelledDataSourceIngestModules.add(moduleDisplayName);
|
cancelledDataSourceIngestModules.add(moduleDisplayName);
|
||||||
if (usingNetBeansGUI && !jobCancelled) {
|
if (usingNetBeansGUI && !jobCancelled) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
try {
|
||||||
/**
|
// use invokeAndWait to ensure synchronous behavior.
|
||||||
* A new progress bar must be created because the cancel button
|
// See JIRA-8298 for more information.
|
||||||
* of the previously constructed component is disabled by
|
SwingUtilities.invokeAndWait(() -> {
|
||||||
* NetBeans when the user selects the "OK" button of the
|
/**
|
||||||
* cancellation confirmation dialog popped up by NetBeans when
|
* A new progress bar must be created because the cancel
|
||||||
* the progress bar cancel button is pressed.
|
* button of the previously constructed component is
|
||||||
*/
|
* disabled by NetBeans when the user selects the "OK"
|
||||||
dataSourceIngestProgressBar.finish();
|
* button of the cancellation confirmation dialog popped up
|
||||||
dataSourceIngestProgressBar = null;
|
* by NetBeans when the progress bar cancel button is
|
||||||
startDataSourceIngestProgressBar();
|
* pressed.
|
||||||
});
|
*/
|
||||||
|
dataSourceIngestProgressBar.finish();
|
||||||
|
dataSourceIngestProgressBar = null;
|
||||||
|
startDataSourceIngestProgressBar();
|
||||||
|
});
|
||||||
|
} catch (InvocationTargetException | InterruptedException ex) {
|
||||||
|
logger.log(Level.WARNING, "Cancellation worker cancelled.", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ public class ALeappAnalyzerIngestModule implements DataSourceIngestModule {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
aLeappFileProcessor.processFiles(dataSource, moduleOutputPath, aLeappFile);
|
aLeappFileProcessor.processFiles(dataSource, moduleOutputPath, aLeappFile, statusHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -274,7 +274,7 @@ public class ALeappAnalyzerIngestModule implements DataSourceIngestModule {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
aLeappFileProcessor.processFileSystem(dataSource, moduleOutputPath);
|
aLeappFileProcessor.processFileSystem(dataSource, moduleOutputPath, statusHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,9 +46,12 @@ LeappFileProcessor.error.creating.new.artifacts=Error creating new artifacts.
|
|||||||
LeappFileProcessor.error.creating.output.dir=Error creating Leapp module output directory.
|
LeappFileProcessor.error.creating.output.dir=Error creating Leapp module output directory.
|
||||||
LeappFileProcessor.error.reading.Leapp.directory=Error reading Leapp Output Directory
|
LeappFileProcessor.error.reading.Leapp.directory=Error reading Leapp Output Directory
|
||||||
LeappFileProcessor.error.running.Leapp=Error running Leapp, see log file.
|
LeappFileProcessor.error.running.Leapp=Error running Leapp, see log file.
|
||||||
|
LeappFileProcessor.findTsv=Finding all Leapp ouput
|
||||||
LeappFileProcessor.has.run=Leapp
|
LeappFileProcessor.has.run=Leapp
|
||||||
LeappFileProcessor.Leapp.cancelled=Leapp run was canceled
|
LeappFileProcessor.Leapp.cancelled=Leapp run was canceled
|
||||||
LeappFileProcessor.postartifacts_error=Error posting Blackboard Artifact
|
LeappFileProcessor.postartifacts_error=Error posting Blackboard Artifact
|
||||||
LeappFileProcessor.running.Leapp=Running Leapp
|
LeappFileProcessor.running.Leapp=Running Leapp
|
||||||
LeappFileProcessor.starting.Leapp=Starting Leapp
|
LeappFileProcessor.starting.Leapp=Starting Leapp
|
||||||
|
# {0} - fileName
|
||||||
|
LeappFileProcessor.tsvProcessed=Processing LEAPP output file: {0}
|
||||||
LeappFileProcessor_cannotParseXml=Cannot Parse XML file.
|
LeappFileProcessor_cannotParseXml=Cannot Parse XML file.
|
||||||
|
@ -232,7 +232,7 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
iLeappFileProcessor.processFiles(dataSource, moduleOutputPath, iLeappFile);
|
iLeappFileProcessor.processFiles(dataSource, moduleOutputPath, iLeappFile, statusHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -274,7 +274,7 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
iLeappFileProcessor.processFileSystem(dataSource, moduleOutputPath);
|
iLeappFileProcessor.processFileSystem(dataSource, moduleOutputPath, statusHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,6 +54,7 @@ import org.apache.commons.collections4.MapUtils;
|
|||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import static org.sleuthkit.autopsy.casemodule.Case.getCurrentCase;
|
import static org.sleuthkit.autopsy.casemodule.Case.getCurrentCase;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
@ -61,6 +62,7 @@ import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
|||||||
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||||
|
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
|
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModule.ProcessResult;
|
import org.sleuthkit.autopsy.ingest.IngestModule.ProcessResult;
|
||||||
@ -221,13 +223,19 @@ public final class LeappFileProcessor {
|
|||||||
"LeappFileProcessor.has.run=Leapp",
|
"LeappFileProcessor.has.run=Leapp",
|
||||||
"LeappFileProcessor.Leapp.cancelled=Leapp run was canceled",
|
"LeappFileProcessor.Leapp.cancelled=Leapp run was canceled",
|
||||||
"LeappFileProcessor.completed=Leapp Processing Completed",
|
"LeappFileProcessor.completed=Leapp Processing Completed",
|
||||||
|
"LeappFileProcessor.findTsv=Finding all Leapp ouput",
|
||||||
"LeappFileProcessor.error.reading.Leapp.directory=Error reading Leapp Output Directory"
|
"LeappFileProcessor.error.reading.Leapp.directory=Error reading Leapp Output Directory"
|
||||||
})
|
})
|
||||||
public ProcessResult processFiles(Content dataSource, Path moduleOutputPath, AbstractFile LeappFile) {
|
public ProcessResult processFiles(Content dataSource, Path moduleOutputPath, AbstractFile LeappFile, DataSourceIngestModuleProgress progress) {
|
||||||
try {
|
try {
|
||||||
|
if (checkCancelled()) {
|
||||||
|
return ProcessResult.OK;
|
||||||
|
}
|
||||||
|
progress.switchToIndeterminate();
|
||||||
|
progress.progress(Bundle.LeappFileProcessor_findTsv());
|
||||||
List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath);
|
List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath);
|
||||||
processLeappFiles(LeappTsvOutputFiles, LeappFile);
|
processLeappFiles(LeappTsvOutputFiles, LeappFile, progress);
|
||||||
} catch (IOException | IngestModuleException ex) {
|
} catch (IngestModuleException ex) {
|
||||||
logger.log(Level.SEVERE, String.format("Error trying to process Leapp output files in directory %s. ", moduleOutputPath.toString()), ex); //NON-NLS
|
logger.log(Level.SEVERE, String.format("Error trying to process Leapp output files in directory %s. ", moduleOutputPath.toString()), ex); //NON-NLS
|
||||||
return ProcessResult.ERROR;
|
return ProcessResult.ERROR;
|
||||||
}
|
}
|
||||||
@ -235,11 +243,15 @@ public final class LeappFileProcessor {
|
|||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProcessResult processFileSystem(Content dataSource, Path moduleOutputPath) {
|
public ProcessResult processFileSystem(Content dataSource, Path moduleOutputPath, DataSourceIngestModuleProgress progress) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (checkCancelled()) {
|
||||||
|
return ProcessResult.OK;
|
||||||
|
}
|
||||||
|
progress.switchToIndeterminate();
|
||||||
|
progress.progress(Bundle.LeappFileProcessor_findTsv());
|
||||||
List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath);
|
List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath);
|
||||||
processLeappFiles(LeappTsvOutputFiles, dataSource);
|
processLeappFiles(LeappTsvOutputFiles, dataSource, progress);
|
||||||
} catch (IngestModuleException ex) {
|
} catch (IngestModuleException ex) {
|
||||||
logger.log(Level.SEVERE, String.format("Error trying to process Leapp output files in directory %s. ", moduleOutputPath.toString()), ex); //NON-NLS
|
logger.log(Level.SEVERE, String.format("Error trying to process Leapp output files in directory %s. ", moduleOutputPath.toString()), ex); //NON-NLS
|
||||||
return ProcessResult.ERROR;
|
return ProcessResult.ERROR;
|
||||||
@ -275,75 +287,58 @@ public final class LeappFileProcessor {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private boolean checkCancelled() {
|
||||||
* Process the Leapp files that were found that match the xml mapping file
|
if (this.context.dataSourceIngestIsCancelled()) {
|
||||||
*
|
logger.log(Level.INFO, "Leapp File processing module run was cancelled"); //NON-NLS
|
||||||
* @param LeappFilesToProcess List of files to process
|
return true;
|
||||||
* @param LeappImageFile Abstract file to create artifact for
|
} else {
|
||||||
*
|
return false;
|
||||||
* @throws FileNotFoundException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
private void processLeappFiles(List<String> LeappFilesToProcess, AbstractFile LeappImageFile) throws FileNotFoundException, IOException, IngestModuleException {
|
|
||||||
List<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
|
||||||
|
|
||||||
for (String LeappFileName : LeappFilesToProcess) {
|
|
||||||
String fileName = FilenameUtils.getName(LeappFileName);
|
|
||||||
File LeappFile = new File(LeappFileName);
|
|
||||||
if (tsvFileAttributes.containsKey(fileName)) {
|
|
||||||
BlackboardArtifact.Type artifactType = null;
|
|
||||||
try {
|
|
||||||
List<TsvColumn> attrList = tsvFileAttributes.get(fileName);
|
|
||||||
artifactType = tsvFileArtifacts.get(fileName);
|
|
||||||
processFile(LeappFile, attrList, fileName, artifactType, bbartifacts, LeappImageFile);
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
throw new IngestModuleException(String.format("Error getting Blackboard Artifact Type for %s", artifactType == null ? "<null>" : artifactType.toString()), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bbartifacts.isEmpty()) {
|
|
||||||
postArtifacts(bbartifacts);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the Leapp files that were found that match the xml mapping file
|
* Process the Leapp files that were found that match the xml mapping file
|
||||||
*
|
*
|
||||||
* @param LeappFilesToProcess List of files to process
|
* @param LeappFilesToProcess List of files to process.
|
||||||
* @param dataSource The data source.
|
* @param dataSource The data source.
|
||||||
|
* @param progress Means of updating progress in UI.
|
||||||
*
|
*
|
||||||
* @throws FileNotFoundException
|
* @throws FileNotFoundException
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private void processLeappFiles(List<String> LeappFilesToProcess, Content dataSource) throws IngestModuleException {
|
@Messages({
|
||||||
List<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
"# {0} - fileName",
|
||||||
|
"LeappFileProcessor.tsvProcessed=Processing LEAPP output file: {0}"
|
||||||
|
})
|
||||||
|
private void processLeappFiles(List<String> LeappFilesToProcess, Content dataSource, DataSourceIngestModuleProgress progress) throws IngestModuleException {
|
||||||
|
progress.switchToDeterminate(LeappFilesToProcess.size());
|
||||||
|
|
||||||
for (String LeappFileName : LeappFilesToProcess) {
|
for (int i = 0; i < LeappFilesToProcess.size(); i++) {
|
||||||
|
if (checkCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String LeappFileName = LeappFilesToProcess.get(i);
|
||||||
String fileName = FilenameUtils.getName(LeappFileName);
|
String fileName = FilenameUtils.getName(LeappFileName);
|
||||||
|
progress.progress(Bundle.LeappFileProcessor_tsvProcessed(fileName), i);
|
||||||
|
|
||||||
File LeappFile = new File(LeappFileName);
|
File LeappFile = new File(LeappFileName);
|
||||||
if (tsvFileAttributes.containsKey(fileName)) {
|
if (tsvFileAttributes.containsKey(fileName)) {
|
||||||
List<TsvColumn> attrList = tsvFileAttributes.get(fileName);
|
List<TsvColumn> attrList = tsvFileAttributes.get(fileName);
|
||||||
BlackboardArtifact.Type artifactType = tsvFileArtifacts.get(fileName);
|
BlackboardArtifact.Type artifactType = tsvFileArtifacts.get(fileName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
processFile(LeappFile, attrList, fileName, artifactType, bbartifacts, dataSource);
|
processFile(LeappFile, attrList, fileName, artifactType, dataSource);
|
||||||
} catch (TskCoreException | IOException ex) {
|
} catch (TskCoreException | IOException ex) {
|
||||||
logger.log(Level.SEVERE, String.format("Error processing file at %s", LeappFile.toString()), ex);
|
logger.log(Level.SEVERE, String.format("Error processing file at %s", LeappFile.toString()), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bbartifacts.isEmpty()) {
|
|
||||||
postArtifacts(bbartifacts);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processFile(File LeappFile, List<TsvColumn> attrList, String fileName, BlackboardArtifact.Type artifactType,
|
private void processFile(File LeappFile, List<TsvColumn> attrList, String fileName,
|
||||||
List<BlackboardArtifact> bbartifacts, Content dataSource) throws FileNotFoundException, IOException, IngestModuleException,
|
BlackboardArtifact.Type artifactType, Content dataSource) throws FileNotFoundException, IOException, IngestModuleException,
|
||||||
TskCoreException {
|
TskCoreException {
|
||||||
|
|
||||||
String trackpointSegmentName = null;
|
String trackpointSegmentName = null;
|
||||||
@ -358,6 +353,8 @@ public final class LeappFileProcessor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<BlackboardArtifact> bbartifacts = new ArrayList<>();
|
||||||
|
|
||||||
// based on https://stackoverflow.com/questions/56921465/jackson-csv-schema-for-array
|
// based on https://stackoverflow.com/questions/56921465/jackson-csv-schema-for-array
|
||||||
try (MappingIterator<List<String>> iterator = new CsvMapper()
|
try (MappingIterator<List<String>> iterator = new CsvMapper()
|
||||||
.enable(CsvParser.Feature.WRAP_AS_ARRAY)
|
.enable(CsvParser.Feature.WRAP_AS_ARRAY)
|
||||||
@ -418,6 +415,9 @@ public final class LeappFileProcessor {
|
|||||||
throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_message_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS
|
throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_message_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!bbartifacts.isEmpty()) {
|
||||||
|
postArtifacts(bbartifacts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
|
@ -165,7 +165,7 @@ public class KeywordList {
|
|||||||
/**
|
/**
|
||||||
* Gets the keywords included in the list
|
* Gets the keywords included in the list
|
||||||
*
|
*
|
||||||
* @return A colleciton of Keyword objects.
|
* @return A collection of Keyword objects.
|
||||||
*/
|
*/
|
||||||
public List<Keyword> getKeywords() {
|
public List<Keyword> getKeywords() {
|
||||||
return keywords;
|
return keywords;
|
||||||
|
@ -56,6 +56,8 @@ class QueryResults {
|
|||||||
private static final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
|
private static final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
|
||||||
private final KeywordSearchQuery query;
|
private final KeywordSearchQuery query;
|
||||||
private final Map<Keyword, List<KeywordHit>> results = new HashMap<>();
|
private final Map<Keyword, List<KeywordHit>> results = new HashMap<>();
|
||||||
|
|
||||||
|
private static final int MAX_INBOX_NOTIFICATIONS_PER_KW_TERM = 20;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a object that stores and processes the results of a keyword
|
* Constructs a object that stores and processes the results of a keyword
|
||||||
@ -142,6 +144,8 @@ class QueryResults {
|
|||||||
*/
|
*/
|
||||||
void process(SwingWorker<?, ?> worker, boolean notifyInbox, boolean saveResults, Long ingestJobId) {
|
void process(SwingWorker<?, ?> worker, boolean notifyInbox, boolean saveResults, Long ingestJobId) {
|
||||||
final Collection<BlackboardArtifact> hitArtifacts = new ArrayList<>();
|
final Collection<BlackboardArtifact> hitArtifacts = new ArrayList<>();
|
||||||
|
|
||||||
|
int notificationCount = 0;
|
||||||
for (final Keyword keyword : getKeywords()) {
|
for (final Keyword keyword : getKeywords()) {
|
||||||
/*
|
/*
|
||||||
* Cancellation check.
|
* Cancellation check.
|
||||||
@ -150,7 +154,7 @@ class QueryResults {
|
|||||||
logger.log(Level.INFO, "Processing cancelled, exiting before processing search term {0}", keyword.getSearchTerm()); //NON-NLS
|
logger.log(Level.INFO, "Processing cancelled, exiting before processing search term {0}", keyword.getSearchTerm()); //NON-NLS
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reduce the hits for this keyword to one hit per text source
|
* Reduce the hits for this keyword to one hit per text source
|
||||||
* object so that only one hit artifact is generated per text source
|
* object so that only one hit artifact is generated per text source
|
||||||
@ -200,8 +204,12 @@ class QueryResults {
|
|||||||
*/
|
*/
|
||||||
if (null != artifact) {
|
if (null != artifact) {
|
||||||
hitArtifacts.add(artifact);
|
hitArtifacts.add(artifact);
|
||||||
if (notifyInbox) {
|
if (notifyInbox && notificationCount < MAX_INBOX_NOTIFICATIONS_PER_KW_TERM) {
|
||||||
|
// only send ingest inbox messages for the first MAX_INBOX_NOTIFICATIONS_PER_KW_TERM hits
|
||||||
|
// for every KW term (per ingest job, aka data source). Otherwise we can have a situation
|
||||||
|
// where we send tens of thousands of notifications.
|
||||||
try {
|
try {
|
||||||
|
notificationCount++;
|
||||||
writeSingleFileInboxMessage(artifact, content);
|
writeSingleFileInboxMessage(artifact, content);
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
logger.log(Level.SEVERE, "Error sending message to ingest messages inbox", ex); //NON-NLS
|
logger.log(Level.SEVERE, "Error sending message to ingest messages inbox", ex); //NON-NLS
|
||||||
|
@ -30,6 +30,8 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -82,7 +84,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
private IngestJobContext context;
|
private IngestJobContext context;
|
||||||
private Blackboard blackboard;
|
private Blackboard blackboard;
|
||||||
private CommunicationArtifactsHelper communicationArtifactsHelper;
|
private CommunicationArtifactsHelper communicationArtifactsHelper;
|
||||||
|
|
||||||
|
// A cache of custom attributes for the VcardParser unique to each ingest run, but consistent across threads.
|
||||||
|
private static ConcurrentMap<String, BlackboardAttribute.Type> customAttributeCache = new ConcurrentHashMap<>();
|
||||||
|
private static Object customAttributeCacheLock = new Object();
|
||||||
|
|
||||||
private static final int MBOX_SIZE_TO_SPLIT = 1048576000;
|
private static final int MBOX_SIZE_TO_SPLIT = 1048576000;
|
||||||
private Case currentCase;
|
private Case currentCase;
|
||||||
|
|
||||||
@ -96,6 +102,13 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
@Messages({"ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case."})
|
@Messages({"ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case."})
|
||||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
public void startUp(IngestJobContext context) throws IngestModuleException {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|
||||||
|
synchronized(customAttributeCacheLock) {
|
||||||
|
if (!customAttributeCache.isEmpty()) {
|
||||||
|
customAttributeCache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
currentCase = Case.getCurrentCaseThrows();
|
currentCase = Case.getCurrentCaseThrows();
|
||||||
fileManager = Case.getCurrentCaseThrows().getServices().getFileManager();
|
fileManager = Case.getCurrentCaseThrows().getServices().getFileManager();
|
||||||
@ -441,7 +454,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
})
|
})
|
||||||
private ProcessResult processVcard(AbstractFile abstractFile) {
|
private ProcessResult processVcard(AbstractFile abstractFile) {
|
||||||
try {
|
try {
|
||||||
VcardParser parser = new VcardParser(currentCase, context);
|
VcardParser parser = new VcardParser(currentCase, context, customAttributeCache);
|
||||||
parser.parse(abstractFile);
|
parser.parse(abstractFile);
|
||||||
} catch (IOException | NoCurrentCaseException ex) {
|
} catch (IOException | NoCurrentCaseException ex) {
|
||||||
logger.log(Level.WARNING, String.format("Exception while parsing the file '%s' (id=%d).", abstractFile.getName(), abstractFile.getId()), ex); //NON-NLS
|
logger.log(Level.WARNING, String.format("Exception while parsing the file '%s' (id=%d).", abstractFile.getName(), abstractFile.getId()), ex); //NON-NLS
|
||||||
@ -912,7 +925,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutDown() {
|
public void shutDown() {
|
||||||
// nothing to shut down
|
synchronized(customAttributeCacheLock) {
|
||||||
|
if (!customAttributeCache.isEmpty()) {
|
||||||
|
customAttributeCache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ import java.util.Collection;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
@ -98,16 +99,22 @@ final class VcardParser {
|
|||||||
private final Blackboard blackboard;
|
private final Blackboard blackboard;
|
||||||
private final Case currentCase;
|
private final Case currentCase;
|
||||||
private final SleuthkitCase tskCase;
|
private final SleuthkitCase tskCase;
|
||||||
|
/**
|
||||||
|
* A custom attribute cache provided to every VcardParser from the
|
||||||
|
* ThunderbirdMboxFileIngestModule, but unique to one ingest run.
|
||||||
|
*/
|
||||||
|
private final ConcurrentMap<String, BlackboardAttribute.Type> customAttributeCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a VcardParser object.
|
* Create a VcardParser object.
|
||||||
*/
|
*/
|
||||||
VcardParser(Case currentCase, IngestJobContext context) {
|
VcardParser(Case currentCase, IngestJobContext context, ConcurrentMap<String, BlackboardAttribute.Type> customAttributeCache) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.currentCase = currentCase;
|
this.currentCase = currentCase;
|
||||||
tskCase = currentCase.getSleuthkitCase();
|
tskCase = currentCase.getSleuthkitCase();
|
||||||
blackboard = tskCase.getBlackboard();
|
blackboard = tskCase.getBlackboard();
|
||||||
fileManager = currentCase.getServices().getFileManager();
|
fileManager = currentCase.getServices().getFileManager();
|
||||||
|
this.customAttributeCache = customAttributeCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -421,26 +428,29 @@ final class VcardParser {
|
|||||||
if (splitType != null && !splitType.isEmpty()) {
|
if (splitType != null && !splitType.isEmpty()) {
|
||||||
attributeTypeName = "TSK_PHONE_NUMBER_" + splitType;
|
attributeTypeName = "TSK_PHONE_NUMBER_" + splitType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String finalAttrTypeName = attributeTypeName;
|
||||||
|
|
||||||
try {
|
// handled in computeIfAbsent to remove concurrency issues when adding to this concurrent hashmap.
|
||||||
BlackboardAttribute.Type attributeType = tskCase.getBlackboard().getAttributeType(attributeTypeName);
|
BlackboardAttribute.Type attributeType
|
||||||
if (attributeType == null) {
|
= this.customAttributeCache.computeIfAbsent(finalAttrTypeName, k -> {
|
||||||
try{
|
try {
|
||||||
// Add this attribute type to the case database.
|
// Add this attribute type to the case database.
|
||||||
attributeType = tskCase.getBlackboard().getOrAddAttributeType(attributeTypeName,
|
return tskCase.getBlackboard().getOrAddAttributeType(finalAttrTypeName,
|
||||||
BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING,
|
BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING,
|
||||||
String.format("Phone Number (%s)", StringUtils.capitalize(splitType.toLowerCase())));
|
String.format("Phone Number (%s)", StringUtils.capitalize(splitType.toLowerCase())));
|
||||||
|
|
||||||
ThunderbirdMboxFileIngestModule.addArtifactAttribute(telephoneText, attributeType, attributes);
|
} catch (BlackboardException ex) {
|
||||||
}catch (BlackboardException ex) {
|
VcardParser.logger.log(Level.WARNING, String.format("Unable to retrieve attribute type '%s' for file '%s' (id=%d).",
|
||||||
logger.log(Level.WARNING, String.format("Unable to retrieve attribute type '%s' for file '%s' (id=%d).", attributeTypeName, abstractFile.getName(), abstractFile.getId()), ex);
|
finalAttrTypeName, abstractFile.getName(), abstractFile.getId()), ex);
|
||||||
}
|
return null;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.WARNING, String.format("Unable to retrieve attribute type '%s' for file '%s' (id=%d).", attributeTypeName, abstractFile.getName(), abstractFile.getId()), ex);
|
if (attributeType != null) {
|
||||||
|
ThunderbirdMboxFileIngestModule.addArtifactAttribute(telephoneText, attributeType, attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,30 +479,37 @@ final class VcardParser {
|
|||||||
* ez-vcard. Therefore, we must read them manually
|
* ez-vcard. Therefore, we must read them manually
|
||||||
* ourselves.
|
* ourselves.
|
||||||
*/
|
*/
|
||||||
List<String> splitEmailTypes = Arrays.asList(
|
List<String> splitEmailTypes = Arrays.asList(
|
||||||
type.getValue().toUpperCase().replaceAll("\\s+","").split(","));
|
type.getValue().toUpperCase().replaceAll("\\s+", "").split(","));
|
||||||
|
|
||||||
if (splitEmailTypes.size() > 0) {
|
if (splitEmailTypes.size() > 0) {
|
||||||
String splitType = splitEmailTypes.get(0);
|
String splitType = splitEmailTypes.get(0);
|
||||||
String attributeTypeName = "TSK_EMAIL_" + splitType;
|
String attributeTypeName = "TSK_EMAIL_" + splitType;
|
||||||
if(splitType.isEmpty()) {
|
if (splitType.isEmpty()) {
|
||||||
attributeTypeName = "TSK_EMAIL";
|
attributeTypeName = "TSK_EMAIL";
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
BlackboardAttribute.Type attributeType = tskCase.getBlackboard().getAttributeType(attributeTypeName);
|
final String finalAttributeTypeName = attributeTypeName;
|
||||||
if (attributeType == null) {
|
|
||||||
// Add this attribute type to the case database.
|
BlackboardAttribute.Type attributeType
|
||||||
attributeType = tskCase.getBlackboard().getOrAddAttributeType(attributeTypeName,
|
= this.customAttributeCache.computeIfAbsent(finalAttributeTypeName, k -> {
|
||||||
BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING,
|
try {
|
||||||
String.format("Email (%s)", StringUtils.capitalize(splitType.toLowerCase())));
|
// Add this attribute type to the case database.
|
||||||
}
|
return tskCase.getBlackboard().getOrAddAttributeType(finalAttributeTypeName,
|
||||||
ThunderbirdMboxFileIngestModule.addArtifactAttribute(email.getValue(), attributeType, attributes);
|
BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING,
|
||||||
} catch (TskCoreException ex) {
|
String.format("Email (%s)", StringUtils.capitalize(splitType.toLowerCase())));
|
||||||
logger.log(Level.SEVERE, String.format("Unable to retrieve attribute type '%s' for file '%s' (id=%d).", attributeTypeName, abstractFile.getName(), abstractFile.getId()), ex);
|
} catch (BlackboardException ex) {
|
||||||
} catch (BlackboardException ex) {
|
logger.log(Level.SEVERE, String.format("Unable to add custom attribute type '%s' for file '%s' (id=%d).",
|
||||||
logger.log(Level.SEVERE, String.format("Unable to add custom attribute type '%s' for file '%s' (id=%d).", attributeTypeName, abstractFile.getName(), abstractFile.getId()), ex);
|
finalAttributeTypeName, abstractFile.getName(), abstractFile.getId()), ex);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (attributeType != null) {
|
||||||
|
ThunderbirdMboxFileIngestModule.addArtifactAttribute(email.getValue(), attributeType, attributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user