Merge pull request #3990 from raman-bt/1001-ig-multiuser

1001 ig multiuser
This commit is contained in:
Richard Cordovano 2018-08-01 13:16:27 -04:00 committed by GitHub
commit 35303f66c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 312 additions and 44 deletions

View File

@ -329,6 +329,7 @@
<package>org.sleuthkit.autopsy.guiutils</package> <package>org.sleuthkit.autopsy.guiutils</package>
<package>org.sleuthkit.autopsy.healthmonitor</package> <package>org.sleuthkit.autopsy.healthmonitor</package>
<package>org.sleuthkit.autopsy.ingest</package> <package>org.sleuthkit.autopsy.ingest</package>
<package>org.sleuthkit.autopsy.ingest.events</package>
<package>org.sleuthkit.autopsy.keywordsearchservice</package> <package>org.sleuthkit.autopsy.keywordsearchservice</package>
<package>org.sleuthkit.autopsy.menuactions</package> <package>org.sleuthkit.autopsy.menuactions</package>
<package>org.sleuthkit.autopsy.modules.encryptiondetection</package> <package>org.sleuthkit.autopsy.modules.encryptiondetection</package>
@ -499,7 +500,7 @@
<runtime-relative-path>ext/xmpcore-5.1.3.jar</runtime-relative-path> <runtime-relative-path>ext/xmpcore-5.1.3.jar</runtime-relative-path>
<binary-origin>release/modules/ext/xmpcore-5.1.3.jar</binary-origin> <binary-origin>release/modules/ext/xmpcore-5.1.3.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/SparseBitSet-1.1.jar</runtime-relative-path> <runtime-relative-path>ext/SparseBitSet-1.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/SparseBitSet-1.1.jar</binary-origin> <binary-origin>release/modules/ext/SparseBitSet-1.1.jar</binary-origin>
</class-path-extension> </class-path-extension>

View File

@ -23,8 +23,10 @@ import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
@ -53,11 +55,13 @@ import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.Cancellable; import org.openide.util.Cancellable;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.Case.CaseType;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
@ -66,6 +70,7 @@ import org.sleuthkit.autopsy.coreutils.History;
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.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.imagegallery.actions.UndoRedoManager; import org.sleuthkit.autopsy.imagegallery.actions.UndoRedoManager;
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager; import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB; import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
@ -77,9 +82,11 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
import org.sleuthkit.autopsy.imagegallery.gui.NoGroupsDialog; import org.sleuthkit.autopsy.imagegallery.gui.NoGroupsDialog;
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar; import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -213,11 +220,15 @@ public final class ImageGalleryController {
listeningEnabled.addListener((observable, oldValue, newValue) -> { listeningEnabled.addListener((observable, oldValue, newValue) -> {
try { try {
//if we just turned on listening and a case is open and that case is not up to date // if we just turned on listening and a single-user case is open and that case is not up to date, then rebuild it
if (newValue && !oldValue && ImageGalleryModule.isDrawableDBStale(Case.getCurrentCaseThrows())) { // For multiuser cases, we defer DB rebuild till the user actually opens Image Gallery
if ( newValue && !oldValue &&
ImageGalleryModule.isDrawableDBStale(Case.getCurrentCaseThrows()) &&
(Case.getCurrentCaseThrows().getCaseType() == CaseType.SINGLE_USER_CASE) ) {
//populate the db //populate the db
queueDBTask(new CopyAnalyzedFiles(instance, db, sleuthKitCase)); this.rebuildDB();
} }
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
LOGGER.log(Level.WARNING, "Exception while getting open case.", ex); LOGGER.log(Level.WARNING, "Exception while getting open case.", ex);
} }
@ -386,6 +397,14 @@ public final class ImageGalleryController {
} }
} }
/**
* Rebuilds the DrawableDB database.
*
*/
public void rebuildDB() {
queueDBTask(new CopyAnalyzedFiles(instance, db, sleuthKitCase));
}
/** /**
* reset the state of the controller (eg if the case is closed) * reset the state of the controller (eg if the case is closed)
*/ */
@ -411,6 +430,56 @@ public final class ImageGalleryController {
db = null; db = null;
} }
/**
* Checks if the datasources table in drawable DB is stale.
*
* @return true if datasources table is stale
*/
boolean isDataSourcesTableStale() {
// no current case open to check
if ((null == getDatabase()) || (null == getSleuthKitCase())) {
return false;
}
try {
Set<Long> knownDataSourceIds= getDatabase().getDataSourceIds();
List<DataSource> dataSources = getSleuthKitCase().getDataSources();
Set<Long> caseDataSourceIds = new HashSet<>();
dataSources.forEach((dataSource) -> {
caseDataSourceIds.add(dataSource.getId());
});
return !(knownDataSourceIds.containsAll(caseDataSourceIds) && caseDataSourceIds.containsAll(knownDataSourceIds));
}
catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Image Gallery failed to check if datasources table is stale.", ex);
return false;
}
}
/**
* Update the datasources table in drawable DB.
*
*/
private void updateDataSourcesTable() {
// no current case open to update
if ((null == getDatabase()) || (null == getSleuthKitCase())) {
return;
}
try {
List<DataSource> dataSources = getSleuthKitCase().getDataSources();
dataSources.forEach((dataSource) -> {
getDatabase().insertDataSource(dataSource.getId());
});
}
catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Image Gallery failed to update data_sources table.", ex);
}
}
synchronized private void shutDownDBExecutor() { synchronized private void shutDownDBExecutor() {
if (dbExecutor != null) { if (dbExecutor != null) {
dbExecutor.shutdownNow(); dbExecutor.shutdownNow();
@ -483,8 +552,8 @@ public final class ImageGalleryController {
void onStart() { void onStart() {
Platform.setImplicitExit(false); Platform.setImplicitExit(false);
LOGGER.info("setting up ImageGallery listeners"); //NON-NLS LOGGER.info("setting up ImageGallery listeners"); //NON-NLS
//TODO can we do anything usefull in an InjestJobEventListener?
//IngestManager.getInstance().addIngestJobEventListener((PropertyChangeEvent evt) -> {}); IngestManager.getInstance().addIngestJobEventListener( new IngestJobEventListener());
IngestManager.getInstance().addIngestModuleEventListener(new IngestModuleEventListener()); IngestManager.getInstance().addIngestModuleEventListener(new IngestModuleEventListener());
Case.addPropertyChangeListener(new CaseEventListener()); Case.addPropertyChangeListener(new CaseEventListener());
} }
@ -661,9 +730,9 @@ public final class ImageGalleryController {
abstract static private class BulkTransferTask extends BackgroundTask { abstract static private class BulkTransferTask extends BackgroundTask {
static private final String FILE_EXTENSION_CLAUSE = static private final String FILE_EXTENSION_CLAUSE =
"(name LIKE '%." //NON-NLS "(extension LIKE '" //NON-NLS
+ String.join("' OR name LIKE '%.", FileTypeUtils.getAllSupportedExtensions()) //NON-NLS + String.join("' OR extension LIKE '", FileTypeUtils.getAllSupportedExtensions()) //NON-NLS
+ "')"; + "') ";
static private final String MIMETYPE_CLAUSE = static private final String MIMETYPE_CLAUSE =
"(mime_type LIKE '" //NON-NLS "(mime_type LIKE '" //NON-NLS
@ -684,6 +753,8 @@ public final class ImageGalleryController {
ProgressHandle progressHandle; ProgressHandle progressHandle;
private boolean taskCompletionStatus;
BulkTransferTask(ImageGalleryController controller, DrawableDB taskDB, SleuthkitCase tskCase) { BulkTransferTask(ImageGalleryController controller, DrawableDB taskDB, SleuthkitCase tskCase) {
this.controller = controller; this.controller = controller;
this.taskDB = taskDB; this.taskDB = taskDB;
@ -713,16 +784,22 @@ public final class ImageGalleryController {
updateProgress(0.0); updateProgress(0.0);
taskCompletionStatus = true;
//do in transaction //do in transaction
DrawableDB.DrawableTransaction tr = taskDB.beginTransaction(); DrawableDB.DrawableTransaction tr = taskDB.beginTransaction();
int workDone = 0; int workDone = 0;
for (final AbstractFile f : files) { for (final AbstractFile f : files) {
if (isCancelled() || Thread.interrupted()) { if (isCancelled()) {
LOGGER.log(Level.WARNING, "Task cancelled: not all contents may be transfered to drawable database."); //NON-NLS LOGGER.log(Level.WARNING, "Task cancelled: not all contents may be transfered to drawable database."); //NON-NLS
progressHandle.finish(); progressHandle.finish();
break; break;
} }
if (Thread.interrupted()) {
LOGGER.log(Level.WARNING, "BulkTransferTask interrupted. Ignoring it to update the contents of drawable database."); //NON-NLS
}
processFile(f, tr); processFile(f, tr);
workDone++; workDone++;
@ -750,10 +827,14 @@ public final class ImageGalleryController {
updateMessage(""); updateMessage("");
updateProgress(-1.0); updateProgress(-1.0);
} }
cleanup(true); cleanup(taskCompletionStatus);
} }
abstract ProgressHandle getInitialProgressHandle(); abstract ProgressHandle getInitialProgressHandle();
protected void setTaskCompletionStatus(boolean status) {
taskCompletionStatus = status;
}
} }
/** /**
@ -774,6 +855,7 @@ public final class ImageGalleryController {
@Override @Override
protected void cleanup(boolean success) { protected void cleanup(boolean success) {
controller.updateDataSourcesTable();
controller.setStale(!success); controller.setStale(!success);
} }
@ -783,7 +865,7 @@ public final class ImageGalleryController {
} }
@Override @Override
void processFile(AbstractFile f, DrawableDB.DrawableTransaction tr) { void processFile(AbstractFile f, DrawableDB.DrawableTransaction tr) throws TskCoreException {
final boolean known = f.getKnown() == TskData.FileKnown.KNOWN; final boolean known = f.getKnown() == TskData.FileKnown.KNOWN;
if (known) { if (known) {
@ -791,13 +873,20 @@ public final class ImageGalleryController {
} else { } else {
try { try {
if (FileTypeUtils.hasDrawableMIMEType(f)) { //supported mimetype => analyzed //supported mimetype => analyzed
if ( null != f.getMIMEType() && FileTypeUtils.hasDrawableMIMEType(f)) {
taskDB.updateFile(DrawableFile.create(f, true, false), tr); taskDB.updateFile(DrawableFile.create(f, true, false), tr);
} else { //unsupported mimtype => analyzed but shouldn't include }
else { //unsupported mimtype => analyzed but shouldn't include
// if mimetype of the file hasn't been ascertained, ingest might not have completed yet.
if (null == f.getMIMEType()) {
this.setTaskCompletionStatus(false);
}
taskDB.removeFile(f.getId(), tr); taskDB.removeFile(f.getId(), tr);
} }
} catch (FileTypeDetector.FileTypeDetectorInitException ex) { } catch (FileTypeDetector.FileTypeDetectorInitException ex) {
throw new RuntimeException(ex); throw new TskCoreException("Failed to initialize FileTypeDetector.", ex);
} }
} }
} }
@ -890,28 +979,32 @@ public final class ImageGalleryController {
AbstractFile file = (AbstractFile) evt.getNewValue(); AbstractFile file = (AbstractFile) evt.getNewValue();
if (isListeningEnabled()) { // only process individual files in realtime on the node that is running the ingest
if (file.isFile()) { // on a remote node, image files are processed enblock when ingest is complete
try { if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.LOCAL) {
synchronized (ImageGalleryController.this) { if (isListeningEnabled()) {
if (ImageGalleryModule.isDrawableAndNotKnown(file)) { if (file.isFile()) {
//this file should be included and we don't already know about it from hash sets (NSRL) try {
queueDBTask(new UpdateFileTask(file, db)); synchronized (ImageGalleryController.this) {
} else if (FileTypeUtils.getAllSupportedExtensions().contains(file.getNameExtension())) { if (ImageGalleryModule.isDrawableAndNotKnown(file)) {
//doing this check results in fewer tasks queued up, and faster completion of db update //this file should be included and we don't already know about it from hash sets (NSRL)
//this file would have gotten scooped up in initial grab, but actually we don't need it queueDBTask(new UpdateFileTask(file, db));
queueDBTask(new RemoveFileTask(file, db)); } else if (FileTypeUtils.getAllSupportedExtensions().contains(file.getNameExtension())) {
//doing this check results in fewer tasks queued up, and faster completion of db update
//this file would have gotten scooped up in initial grab, but actually we don't need it
queueDBTask(new RemoveFileTask(file, db));
}
} }
} catch (TskCoreException | FileTypeDetector.FileTypeDetectorInitException ex) {
//TODO: What to do here?
LOGGER.log(Level.SEVERE, "Unable to determine if file is drawable and not known. Not making any changes to DB", ex); //NON-NLS
MessageNotifyUtil.Notify.error("Image Gallery Error",
"Unable to determine if file is drawable and not known. Not making any changes to DB. See the logs for details.");
} }
} catch (TskCoreException | FileTypeDetector.FileTypeDetectorInitException ex) {
//TODO: What to do here?
LOGGER.log(Level.SEVERE, "Unable to determine if file is drawable and not known. Not making any changes to DB", ex); //NON-NLS
MessageNotifyUtil.Notify.error("Image Gallery Error",
"Unable to determine if file is drawable and not known. Not making any changes to DB. See the logs for details.");
} }
} else { //TODO: keep track of what we missed for later
setStale(true);
} }
} else { //TODO: keep track of what we missed for later
setStale(true);
} }
break; break;
} }
@ -943,12 +1036,14 @@ public final class ImageGalleryController {
} }
break; break;
case DATA_SOURCE_ADDED: case DATA_SOURCE_ADDED:
//copy all file data to drawable databse //For a data source added on the local node, prepopulate all file data to drawable database
Content newDataSource = (Content) evt.getNewValue(); if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.LOCAL) {
if (isListeningEnabled()) { Content newDataSource = (Content) evt.getNewValue();
queueDBTask(new PrePopulateDataSourceFiles(newDataSource, ImageGalleryController.this, getDatabase(), getSleuthKitCase())); if (isListeningEnabled()) {
} else {//TODO: keep track of what we missed for later queueDBTask(new PrePopulateDataSourceFiles(newDataSource, ImageGalleryController.this, getDatabase(), getSleuthKitCase()));
setStale(true); } else {//TODO: keep track of what we missed for later
setStale(true);
}
} }
break; break;
case CONTENT_TAG_ADDED: case CONTENT_TAG_ADDED:
@ -966,4 +1061,62 @@ public final class ImageGalleryController {
} }
} }
} }
/**
* Listener for Ingest Job events.
*/
private class IngestJobEventListener implements PropertyChangeListener {
@NbBundle.Messages({
"ImageGalleryController.dataSourceAnalyzed.confDlg.msg= A new data source was added and finished ingest.\n" +
"The image / video database may be out of date. " +
"Do you want to update the database with ingest results?\n",
"ImageGalleryController.dataSourceAnalyzed.confDlg.title=Image Gallery"
})
@Override
public void propertyChange(PropertyChangeEvent evt) {
String eventName = evt.getPropertyName();
if ( eventName.equals(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED.toString())) {
if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.REMOTE) {
// A remote node added a new data source and just finished ingest on it.
//drawable db is stale, and if ImageGallery is open, ask user what to do
setStale(true);
SwingUtilities.invokeLater(() -> {
if (isListeningEnabled() && ImageGalleryTopComponent.isImageGalleryOpen()) {
int answer = JOptionPane.showConfirmDialog(ImageGalleryTopComponent.getTopComponent(),
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_msg(),
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_title(),
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
switch (answer) {
case JOptionPane.YES_OPTION:
rebuildDB();
break;
case JOptionPane.NO_OPTION:
case JOptionPane.CANCEL_OPTION:
default:
break; //do nothing
}
}
});
} else {
// received event from local node
// add the datasource to drawable db
long dsObjId = 0;
DataSourceAnalysisCompletedEvent event = (DataSourceAnalysisCompletedEvent)evt;
if(event.getDataSource() != null) {
dsObjId = event.getDataSource().getId();
db.insertDataSource(dsObjId);
// All files for the data source have been analyzed.
setStale(false);
} else {
LOGGER.log(Level.SEVERE, "DataSourceAnalysisCompletedEvent does not contain a dataSource object"); //NON-NLS
}
}
}
}
}
} }

View File

@ -86,7 +86,9 @@ public class ImageGalleryModule {
public static boolean isDrawableDBStale(Case c) { public static boolean isDrawableDBStale(Case c) {
if (c != null) { if (c != null) {
String stale = new PerCaseProperties(c).getConfigSetting(ImageGalleryModule.MODULE_NAME, PerCaseProperties.STALE); String stale = new PerCaseProperties(c).getConfigSetting(ImageGalleryModule.MODULE_NAME, PerCaseProperties.STALE);
return StringUtils.isNotBlank(stale) ? Boolean.valueOf(stale) : true;
return ( ImageGalleryController.getDefault().isDataSourcesTableStale() ||
(StringUtils.isNotBlank(stale) ? Boolean.valueOf(stale) : true) );
} else { } else {
return false; return false;
} }

View File

@ -39,6 +39,7 @@ import org.openide.windows.RetainLocation;
import org.openide.windows.TopComponent; import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.imagegallery.gui.StatusBar; import org.sleuthkit.autopsy.imagegallery.gui.StatusBar;
import org.sleuthkit.autopsy.imagegallery.gui.SummaryTablePane; import org.sleuthkit.autopsy.imagegallery.gui.SummaryTablePane;
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar; import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
@ -88,6 +89,29 @@ public final class ImageGalleryTopComponent extends TopComponent implements Expl
private VBox leftPane; private VBox leftPane;
private Scene myScene; private Scene myScene;
/**
* Returns whether the ImageGallery window is open or not.
*
* @return true, if Image gallery is opened, false otherwise
*/
public static boolean isImageGalleryOpen() {
final TopComponent topComponent = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
if (topComponent != null) {
return topComponent.isOpened();
}
return false;
}
/**
* Returns the top component window.
*
* @return Image gallery top component window, null if it's not open
*/
public static TopComponent getTopComponent() {
return WindowManager.getDefault().findTopComponent(PREFERRED_ID);
}
public static void openTopComponent() { public static void openTopComponent() {
//TODO:eventually move to this model, throwing away everything and rebuilding controller groupmanager etc for each case. //TODO:eventually move to this model, throwing away everything and rebuilding controller groupmanager etc for each case.
// synchronized (OpenTimelineAction.class) { // synchronized (OpenTimelineAction.class) {

View File

@ -172,7 +172,18 @@ public final class OpenAction extends CallableSystemAction {
switch (answer) { switch (answer) {
case JOptionPane.YES_OPTION: case JOptionPane.YES_OPTION:
ImageGalleryController.getDefault().setListeningEnabled(true);
// For a single-user case, we favor user experience, and rebuild the database
// as soon as Image Gallery is enabled for the case.
// For a multi-user case, we favor overall performance and user experience, not every user may want to review images,
// so we rebuild the database only when a user launches Image Gallery
if (currentCase.getCaseType() == Case.CaseType.SINGLE_USER_CASE) {
ImageGalleryController.getDefault().setListeningEnabled(true);
}
else {
ImageGalleryController.getDefault().rebuildDB();
}
//fall through //fall through
case JOptionPane.NO_OPTION: case JOptionPane.NO_OPTION:
ImageGalleryTopComponent.openTopComponent(); ImageGalleryTopComponent.openTopComponent();

View File

@ -47,7 +47,6 @@ import javax.annotation.Nonnull;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import javax.swing.SortOrder; import javax.swing.SortOrder;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.imagegallery.FileTypeUtils; import org.sleuthkit.autopsy.imagegallery.FileTypeUtils;
@ -102,6 +101,8 @@ public final class DrawableDB {
private final PreparedStatement insertHashHitStmt; private final PreparedStatement insertHashHitStmt;
private final PreparedStatement insertDataSourceStmt;
private final PreparedStatement updateFileStmt; private final PreparedStatement updateFileStmt;
private final PreparedStatement insertFileStmt; private final PreparedStatement insertFileStmt;
@ -208,6 +209,10 @@ public final class DrawableDB {
"INSERT OR IGNORE INTO drawable_files (obj_id , path, name, created_time, modified_time, make, model, analyzed) " //NON-NLS "INSERT OR IGNORE INTO drawable_files (obj_id , path, name, created_time, modified_time, make, model, analyzed) " //NON-NLS
+ "VALUES (?,?,?,?,?,?,?,?)"); //NON-NLS + "VALUES (?,?,?,?,?,?,?,?)"); //NON-NLS
insertDataSourceStmt = prepareStatement(
"INSERT OR IGNORE INTO datasources (ds_obj_id) " //NON-NLS
+ "VALUES (?)"); //NON-NLS
removeFileStmt = prepareStatement("DELETE FROM drawable_files WHERE obj_id = ?"); //NON-NLS removeFileStmt = prepareStatement("DELETE FROM drawable_files WHERE obj_id = ?"); //NON-NLS
pathGroupStmt = prepareStatement("SELECT obj_id , analyzed FROM drawable_files WHERE path = ? ", DrawableAttribute.PATH); //NON-NLS pathGroupStmt = prepareStatement("SELECT obj_id , analyzed FROM drawable_files WHERE path = ? ", DrawableAttribute.PATH); //NON-NLS
@ -348,6 +353,17 @@ public final class DrawableDB {
LOGGER.log(Level.SEVERE, "problem accessing database", ex); //NON-NLS LOGGER.log(Level.SEVERE, "problem accessing database", ex); //NON-NLS
return false; return false;
} }
try (Statement stmt = con.createStatement()) {
String sql = "CREATE TABLE if not exists datasources " //NON-NLS
+ "( id INTEGER PRIMARY KEY, " //NON-NLS
+ " ds_obj_id integer UNIQUE NOT NULL)"; //NON-NLS
stmt.execute(sql);
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "problem creating datasources table", ex); //NON-NLS
return false;
}
try (Statement stmt = con.createStatement()) { try (Statement stmt = con.createStatement()) {
String sql = "CREATE TABLE if not exists drawable_files " //NON-NLS String sql = "CREATE TABLE if not exists drawable_files " //NON-NLS
+ "( obj_id INTEGER PRIMARY KEY, " //NON-NLS + "( obj_id INTEGER PRIMARY KEY, " //NON-NLS
@ -688,6 +704,67 @@ public final class DrawableDB {
} }
} }
/**
* Gets all data source object ids from datasources table
*
* @return list of known data source object ids
*/
public Set<Long> getDataSourceIds() throws TskCoreException {
Statement statement = null;
ResultSet rs = null;
Set<Long> ret = new HashSet<>();
dbReadLock();
try {
statement = con.createStatement();
rs = statement.executeQuery("SELECT ds_obj_id FROM datasources "); //NON-NLS
while (rs.next()) {
ret.add(rs.getLong(1));
}
} catch (SQLException e) {
throw new TskCoreException("SQLException while getting data source object ids", e);
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "Error closing resultset", ex); //NON-NLS
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "Error closing statement ", ex); //NON-NLS
}
}
dbReadUnlock();
}
return ret;
}
/**
* Insert given data source object id into datasources table
*
* If the object id exists in the table already, it does nothing.
*
* @param dsObjectId data source object id to insert
*/
public void insertDataSource(long dsObjectId) {
dbWriteLock();
try {
// "INSERT OR IGNORE/ INTO datasources (ds_obj_id)"
insertDataSourceStmt.setLong(1,dsObjectId);
insertDataSourceStmt.executeUpdate();
} catch (SQLException | NullPointerException ex) {
LOGGER.log(Level.SEVERE, "failed to insert/update datasources table", ex); //NON-NLS
} finally {
dbWriteUnlock();
}
}
public DrawableTransaction beginTransaction() { public DrawableTransaction beginTransaction() {
return new DrawableTransaction(); return new DrawableTransaction();
} }