Merge pull request #4296 from millmanorama/merge-develop-into-timeline

Merge develop into timeline
This commit is contained in:
Richard Cordovano 2018-11-21 15:37:22 -05:00 committed by millmanorama
commit 47d8bc29b3
119 changed files with 6310 additions and 3760 deletions

View File

@ -23,6 +23,8 @@
<dependency conf="core->default" org="com.adobe.xmp" name="xmpcore" rev="5.1.2"/> <dependency conf="core->default" org="com.adobe.xmp" name="xmpcore" rev="5.1.2"/>
<dependency conf="core->default" org="org.apache.zookeeper" name="zookeeper" rev="3.4.6"/> <dependency conf="core->default" org="org.apache.zookeeper" name="zookeeper" rev="3.4.6"/>
<dependency conf="core->default" org="com.healthmarketscience.jackcess" name="jackcess" rev="2.2.0"/>
<dependency conf="core->default" org="com.healthmarketscience.jackcess" name="jackcess-encrypt" rev="2.1.4"/>
<dependency conf="core->default" org="org.apache.commons" name="commons-dbcp2" rev="2.1.1"/> <dependency conf="core->default" org="org.apache.commons" name="commons-dbcp2" rev="2.1.1"/>
<dependency conf="core->default" org="org.apache.commons" name="commons-pool2" rev="2.4.2"/> <dependency conf="core->default" org="org.apache.commons" name="commons-pool2" rev="2.4.2"/>

View File

@ -18,8 +18,8 @@ file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar
file.reference.sqlite-jdbc-3.8.11.jar=release\\modules\\ext\\sqlite-jdbc-3.8.11.jar file.reference.sqlite-jdbc-3.8.11.jar=release\\modules\\ext\\sqlite-jdbc-3.8.11.jar
file.reference.StixLib.jar=release/modules/ext/StixLib.jar file.reference.StixLib.jar=release/modules/ext/StixLib.jar
file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar
file.reference.jackcess-2.1.8.jar=release/modules/ext/jackcess-2.1.8.jar file.reference.jackcess-2.2.0.jar=release/modules/ext/jackcess-2.2.0.jar
file.reference.jackcess-encrypt-2.1.2.jar=release/modules/ext/jackcess-encrypt-2.1.2.jar file.reference.jackcess-encrypt-2.1.4.jar=release/modules/ext/jackcess-encrypt-2.1.4.jar
file.reference.jempbox-1.8.13.jar=release/modules/ext/jempbox-1.8.13.jar file.reference.jempbox-1.8.13.jar=release/modules/ext/jempbox-1.8.13.jar
file.reference.javax.ws.rs-api-2.0.1.jar=release/modules/ext/javax.ws.rs-api-2.0.1.jar file.reference.javax.ws.rs-api-2.0.1.jar=release/modules/ext/javax.ws.rs-api-2.0.1.jar
file.reference.cxf-core-3.0.16.jar=release/modules/ext/cxf-core-3.0.16.jar file.reference.cxf-core-3.0.16.jar=release/modules/ext/cxf-core-3.0.16.jar
@ -29,7 +29,7 @@ file.reference.cxf-rt-transports-http-3.0.16.jar=release/modules/ext/cxf-rt-tran
file.reference.fontbox-2.0.8.jar=release/modules/ext/fontbox-2.0.8.jar file.reference.fontbox-2.0.8.jar=release/modules/ext/fontbox-2.0.8.jar
file.reference.pdfbox-2.0.8.jar=release/modules/ext/pdfbox-2.0.8.jar file.reference.pdfbox-2.0.8.jar=release/modules/ext/pdfbox-2.0.8.jar
file.reference.pdfbox-tools-2.0.8.jar=release/modules/ext/pdfbox-tools-2.0.8.jar file.reference.pdfbox-tools-2.0.8.jar=release/modules/ext/pdfbox-tools-2.0.8.jar
file.reference.sleuthkit-postgresql-4.6.3.jar=release/modules/ext/sleuthkit-postgresql-4.6.3.jar file.reference.sleuthkit-postgresql-4.6.4.jar=release/modules/ext/sleuthkit-postgresql-4.6.4.jar
file.reference.tika-core-1.17.jar=release/modules/ext/tika-core-1.17.jar file.reference.tika-core-1.17.jar=release/modules/ext/tika-core-1.17.jar
file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar

View File

@ -338,12 +338,12 @@
<package>org.sleuthkit.autopsy.modules.vmextractor</package> <package>org.sleuthkit.autopsy.modules.vmextractor</package>
<package>org.sleuthkit.autopsy.progress</package> <package>org.sleuthkit.autopsy.progress</package>
<package>org.sleuthkit.autopsy.report</package> <package>org.sleuthkit.autopsy.report</package>
<package>org.sleuthkit.autopsy.tabulardatareader</package> <package>org.sleuthkit.autopsy.texttranslation</package>
<package>org.sleuthkit.datamodel</package> <package>org.sleuthkit.datamodel</package>
</public-packages> </public-packages>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/jackcess-2.1.8.jar</runtime-relative-path> <runtime-relative-path>ext/jackcess-2.2.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jackcess-2.1.8.jar</binary-origin> <binary-origin>release/modules/ext/jackcess-2.2.0.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/zookeeper-3.4.6.jar</runtime-relative-path> <runtime-relative-path>ext/zookeeper-3.4.6.jar</runtime-relative-path>
@ -394,8 +394,8 @@
<binary-origin>release/modules/ext/sevenzipjbinding.jar</binary-origin> <binary-origin>release/modules/ext/sevenzipjbinding.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/sleuthkit-postgresql-4.6.3.jar</runtime-relative-path> <runtime-relative-path>ext/sleuthkit-postgresql-4.6.4.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.3.jar</binary-origin> <binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.4.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/mchange-commons-java-0.2.9.jar</runtime-relative-path> <runtime-relative-path>ext/mchange-commons-java-0.2.9.jar</runtime-relative-path>
@ -482,8 +482,8 @@
<binary-origin>release\modules\ext\commons-pool2-2.4.2.jar</binary-origin> <binary-origin>release\modules\ext\commons-pool2-2.4.2.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/jackcess-encrypt-2.1.2.jar</runtime-relative-path> <runtime-relative-path>ext/jackcess-encrypt-2.1.4.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jackcess-encrypt-2.1.2.jar</binary-origin> <binary-origin>release/modules/ext/jackcess-encrypt-2.1.4.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/jsoup-1.10.3.jar</runtime-relative-path> <runtime-relative-path>ext/jsoup-1.10.3.jar</runtime-relative-path>

View File

@ -122,6 +122,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TimelineManager; import org.sleuthkit.datamodel.TimelineManager;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException; import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
import org.sleuthkit.autopsy.coreutils.StopWatch;
/** /**
* An Autopsy case. Currently, only one case at a time may be open. * An Autopsy case. Currently, only one case at a time may be open.
@ -744,11 +745,15 @@ public class Case {
"Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or there is a problem with the coordination service." "Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or there is a problem with the coordination service."
}) })
public static void deleteCase(CaseMetadata metadata) throws CaseActionException { public static void deleteCase(CaseMetadata metadata) throws CaseActionException {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
synchronized (caseActionSerializationLock) { synchronized (caseActionSerializationLock) {
if (null != currentCase) { if (null != currentCase) {
throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase()); throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
} }
} }
stopWatch.stop();
logger.log(Level.INFO, String.format("Used %d s to acquire caseActionSerializationLock (Java monitor in Case class) for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
/* /*
* Set up either a GUI progress indicator without a cancel button (can't * Set up either a GUI progress indicator without a cancel button (can't
@ -770,10 +775,19 @@ public class Case {
* cannot be deleted if another node has it open. * cannot be deleted if another node has it open.
*/ */
progressIndicator.progress(Bundle.Case_progressMessage_checkingForOtherUser()); progressIndicator.progress(Bundle.Case_progressMessage_checkingForOtherUser());
stopWatch.reset();
stopWatch.start();
try (CoordinationService.Lock dirLock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) { try (CoordinationService.Lock dirLock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) {
assert (null != dirLock); stopWatch.stop();
deleteCase(metadata, progressIndicator); logger.log(Level.INFO, String.format("Used %d s to acquire case directory coordination service lock for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
if (dirLock != null) {
deleteCase(metadata, progressIndicator);
} else {
throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
}
} catch (CoordinationServiceException ex) { } catch (CoordinationServiceException ex) {
stopWatch.stop();
logger.log(Level.INFO, String.format("Used %d s to fail to acquire case directory coordination service lock for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase(), ex); throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase(), ex);
} }
} }
@ -983,11 +997,13 @@ public class Case {
"Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details" "Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details"
}) })
private static void deleteCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException { private static void deleteCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
StopWatch stopWatch = new StopWatch();
boolean errorsOccurred = false; boolean errorsOccurred = false;
if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) { if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
/* /*
* Delete the case database from the database server. * Delete the case database from the database server.
*/ */
stopWatch.start();
try { try {
progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase()); progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase());
CaseDbConnectionInfo db; CaseDbConnectionInfo db;
@ -997,10 +1013,14 @@ public class Case {
Statement statement = connection.createStatement();) { Statement statement = connection.createStatement();) {
String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS
statement.execute(deleteCommand); statement.execute(deleteCommand);
stopWatch.stop();
logger.log(Level.INFO, String.format("Used %d s to delete case database for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
} }
} catch (UserPreferencesException | ClassNotFoundException | SQLException ex) { } catch (UserPreferencesException | ClassNotFoundException | SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to delete case database %s for %s (%s) in %s", metadata.getCaseDatabaseName(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); logger.log(Level.SEVERE, String.format("Failed to delete case database %s for %s (%s) in %s", metadata.getCaseDatabaseName(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
errorsOccurred = true; errorsOccurred = true;
stopWatch.stop();
logger.log(Level.INFO, String.format("Used %d s to fail delete case database for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
} }
} }
@ -1010,10 +1030,16 @@ public class Case {
progressIndicator.progress(Bundle.Case_progressMessage_deletingTextIndex()); progressIndicator.progress(Bundle.Case_progressMessage_deletingTextIndex());
for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class)) { for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class)) {
try { try {
stopWatch.reset();
stopWatch.start();
searchService.deleteTextIndex(metadata); searchService.deleteTextIndex(metadata);
stopWatch.stop();
logger.log(Level.INFO, String.format("Used %d s to delete text index for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
} catch (KeywordSearchServiceException ex) { } catch (KeywordSearchServiceException ex) {
logger.log(Level.SEVERE, String.format("Failed to delete text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); logger.log(Level.SEVERE, String.format("Failed to delete text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
errorsOccurred = true; errorsOccurred = true;
stopWatch.stop();
logger.log(Level.INFO, String.format("Used %d s to fail to delete text index for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
} }
} }
@ -1021,9 +1047,16 @@ public class Case {
* Delete the case directory. * Delete the case directory.
*/ */
progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirectory()); progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirectory());
stopWatch.reset();
stopWatch.start();
if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) { if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) {
logger.log(Level.SEVERE, String.format("Failed to delete case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory())); logger.log(Level.SEVERE, String.format("Failed to delete case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
errorsOccurred = true; errorsOccurred = true;
stopWatch.stop();
logger.log(Level.INFO, String.format("Used %d s to fail to delete case directory for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
} else {
stopWatch.stop();
logger.log(Level.INFO, String.format("Used %d s to delete case directory for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
} }
/* /*
@ -1839,7 +1872,7 @@ public class Case {
progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources()); progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources());
acquireSharedCaseDirLock(metadata.getCaseDirectory()); acquireSharedCaseDirLock(metadata.getCaseDirectory());
try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) { try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) {
assert (null != resourcesLock); assert(resourcesLock != null); // Use reference to avoid compile time warning.
open(isNewCase, progressIndicator); open(isNewCase, progressIndicator);
} catch (CaseActionException ex) { } catch (CaseActionException ex) {
releaseSharedCaseDirLock(getMetadata().getCaseDirectory()); releaseSharedCaseDirLock(getMetadata().getCaseDirectory());
@ -2417,7 +2450,7 @@ public class Case {
* @throws CaseActionException with a user-friendly message if the lock * @throws CaseActionException with a user-friendly message if the lock
* cannot be acquired. * cannot be acquired.
*/ */
@Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory."}) @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"})
private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException { private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException {
try { try {
caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS); caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);

View File

@ -21,8 +21,6 @@ package org.sleuthkit.autopsy.casemodule;
import java.io.File; import java.io.File;
import java.util.Calendar; import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
@ -39,6 +37,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PathValidator; import org.sleuthkit.autopsy.coreutils.PathValidator;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
/** /**
* Panel for adding an image file such as .img, .E0x, .00x, etc. Allows the user * Panel for adding an image file such as .img, .E0x, .00x, etc. Allows the user
@ -72,11 +71,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
initComponents(); initComponents();
// Populate the drop down list of time zones // Populate the drop down list of time zones
for (String id : SimpleTimeZone.getAvailableIDs()) { createTimeZoneList();
timeZoneComboBox.addItem(timeZoneToString(TimeZone.getTimeZone(id)));
}
// set the selected timezone to the current timezone
timeZoneComboBox.setSelectedItem(timeZoneToString(Calendar.getInstance().getTimeZone()));
// Populate the drop down list of sector size options // Populate the drop down list of sector size options
for (String choice : SECTOR_SIZE_CHOICES) { for (String choice : SECTOR_SIZE_CHOICES) {
@ -95,6 +90,20 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
} }
} }
/**
* Creates the drop down list for the time zones and defaults the selection
* to the local machine time zone.
*/
private void createTimeZoneList() {
List<String> timeZoneList = TimeZoneUtils.createTimeZoneList();
for (String timeZone : timeZoneList) {
timeZoneComboBox.addItem(timeZone);
}
// set the selected timezone
timeZoneComboBox.setSelectedItem(TimeZoneUtils.createTimeZoneString(Calendar.getInstance().getTimeZone()));
}
/** /**
* Creates and returns an instance of a ImageFilePanel. * Creates and returns an instance of a ImageFilePanel.
* *
@ -348,21 +357,6 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
} }
} }
/**
* Get a string representation of a TimeZone for use in the drop down list.
*
* @param zone The TimeZone to make a string for
*
* @return A string representation of a TimeZone for use in the drop down
* list.
*/
static private String timeZoneToString(TimeZone zone) {
int offset = zone.getRawOffset() / 1000;
int hour = offset / 3600;
int minutes = (offset % 3600) / 60;
return String.format("(GMT%+d:%02d) %s", hour, minutes, zone.getID()); //NON-NLS
}
@Override @Override
public void insertUpdate(DocumentEvent e) { public void insertUpdate(DocumentEvent e) {
updateHelper(); updateHelper();

View File

@ -21,8 +21,7 @@ package org.sleuthkit.autopsy.casemodule;
import java.io.File; import java.io.File;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Calendar; import java.util.Calendar;
import java.util.SimpleTimeZone; import java.util.List;
import java.util.TimeZone;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JPanel; import javax.swing.JPanel;
@ -32,6 +31,7 @@ import org.sleuthkit.autopsy.coreutils.LocalDisk;
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.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings; import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
/** /**
@ -469,36 +469,13 @@ final class LocalDiskPanel extends JPanel {
* to the local machine time zone. * to the local machine time zone.
*/ */
public void createTimeZoneList() { public void createTimeZoneList() {
// load and add all timezone List<String> timeZoneList = TimeZoneUtils.createTimeZoneList();
String[] ids = SimpleTimeZone.getAvailableIDs(); for (String timeZone : timeZoneList) {
for (String id : ids) { timeZoneComboBox.addItem(timeZone);
TimeZone zone = TimeZone.getTimeZone(id);
int offset = zone.getRawOffset() / 1000;
int hour = offset / 3600;
int minutes = (offset % 3600) / 60;
String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id); //NON-NLS
/*
* DateFormat dfm = new SimpleDateFormat("z");
* dfm.setTimeZone(zone); boolean hasDaylight =
* zone.useDaylightTime(); String first = dfm.format(new Date(2010,
* 1, 1)); String second = dfm.format(new Date(2011, 6, 6)); int mid
* = hour * -1; String result = first + Integer.toString(mid);
* if(hasDaylight){ result = result + second; }
* timeZoneComboBox.addItem(item + " (" + result + ")");
*/
timeZoneComboBox.addItem(item);
} }
// get the current timezone
TimeZone thisTimeZone = Calendar.getInstance().getTimeZone();
int thisOffset = thisTimeZone.getRawOffset() / 1000;
int thisHour = thisOffset / 3600;
int thisMinutes = (thisOffset % 3600) / 60;
String formatted = String.format("(GMT%+d:%02d) %s", thisHour, thisMinutes, thisTimeZone.getID()); //NON-NLS
// set the selected timezone // set the selected timezone
timeZoneComboBox.setSelectedItem(formatted); timeZoneComboBox.setSelectedItem(TimeZoneUtils.createTimeZoneString(Calendar.getInstance().getTimeZone()));
} }
/** /**

View File

@ -369,7 +369,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
@Override @Override
public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) { public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
List<String> filePaths = Arrays.asList(new String[]{dataSourcePath.toString()}); List<String> filePaths = Arrays.asList(new String[]{dataSourcePath.toString()});
run(deviceId, deviceId, filePaths, progressMonitor, callBack); run(deviceId, "", filePaths, progressMonitor, callBack);
} }
/** /**

View File

@ -492,6 +492,7 @@ public class FileManager implements Closeable {
} }
} }
trans.commit(); trans.commit();
trans = null;
/* /*
* Publish content added events for the added files and directories. * Publish content added events for the added files and directories.
@ -502,15 +503,14 @@ public class FileManager implements Closeable {
return dataSource; return dataSource;
} catch (TskCoreException ex) { } finally {
if (null != trans) { if (null != trans) {
try { try {
trans.rollback(); trans.rollback();
} catch (TskCoreException ex2) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2); LOGGER.log(Level.SEVERE, "Failed to rollback transaction after exception", ex);
} }
} }
throw ex;
} }
} }

View File

@ -127,7 +127,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} }
} else if (jmi.equals(showCommonalityMenuItem)) { } else if (jmi.equals(showCommonalityMenuItem)) {
showCommonalityDetails(); showCommonalityDetails();
} }
} }
}; };
@ -419,7 +419,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} }
// we can correlate based on the MD5 if it is enabled // we can correlate based on the MD5 if it is enabled
if (this.file != null && EamDb.isEnabled()) { if (this.file != null && EamDb.isEnabled() && this.file.getSize() > 0) {
try { try {
List<CorrelationAttributeInstance.Type> artifactTypes = EamDb.getInstance().getDefinedCorrelationTypes(); List<CorrelationAttributeInstance.Type> artifactTypes = EamDb.getInstance().getDefinedCorrelationTypes();
@ -430,13 +430,14 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
CorrelationCase corCase = EamDb.getInstance().getCase(Case.getCurrentCase()); CorrelationCase corCase = EamDb.getInstance().getCase(Case.getCurrentCase());
try { try {
ret.add(new CorrelationAttributeInstance( ret.add(new CorrelationAttributeInstance(
md5,
aType, aType,
md5,
corCase, corCase,
CorrelationDataSource.fromTSKDataSource(corCase, file.getDataSource()), CorrelationDataSource.fromTSKDataSource(corCase, file.getDataSource()),
file.getParentPath() + file.getName(), file.getParentPath() + file.getName(),
"", "",
file.getKnown())); file.getKnown(),
file.getId()));
} catch (CorrelationAttributeNormalizationException ex) { } catch (CorrelationAttributeNormalizationException ex) {
LOGGER.log(Level.INFO, String.format("Unable to check create CorrelationAttribtueInstance for value %s and type %s.", md5, aType.toString()), ex); LOGGER.log(Level.INFO, String.format("Unable to check create CorrelationAttribtueInstance for value %s and type %s.", md5, aType.toString()), ex);
} }
@ -447,27 +448,23 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} catch (EamDbException | TskCoreException ex) { } catch (EamDbException | TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS
} }
// If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled.
} else { } else if (this.file != null && this.file.getSize() > 0) {
String md5 = this.file.getMd5Hash();
// If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled. if (md5 != null && !md5.isEmpty()) {
if (this.file != null) { try {
String md5 = this.file.getMd5Hash(); final CorrelationAttributeInstance.Type fileAttributeType
if (md5 != null && !md5.isEmpty()) { = CorrelationAttributeInstance.getDefaultCorrelationTypes()
try { .stream()
final CorrelationAttributeInstance.Type fileAttributeType .filter(attrType -> attrType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID)
= CorrelationAttributeInstance.getDefaultCorrelationTypes() .findAny()
.stream() .get();
.filter(attrType -> attrType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) //The Central Repository is not enabled
.findAny() ret.add(new CorrelationAttributeInstance(fileAttributeType, md5, null, null, "", "", TskData.FileKnown.UNKNOWN, this.file.getId()));
.get(); } catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS
ret.add(new CorrelationAttributeInstance(fileAttributeType, md5)); } catch (CorrelationAttributeNormalizationException ex) {
} catch (EamDbException ex) { LOGGER.log(Level.INFO, String.format("Unable to create CorrelationAttributeInstance for value %s", md5), ex); // NON-NLS
LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS
} catch (CorrelationAttributeNormalizationException ex) {
LOGGER.log(Level.INFO, String.format("Unable to create CorrelationAttributeInstance for value %s", md5), ex); // NON-NLS
}
} }
} }
} }
@ -515,9 +512,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
* artifact. If the central repo is not enabled, this will only return files * artifact. If the central repo is not enabled, this will only return files
* from the current case with matching MD5 hashes. * from the current case with matching MD5 hashes.
* *
* @param corAttr CorrelationAttribute to query for * @param corAttr CorrelationAttribute to query for
* @param dataSourceName Data source to filter results * @param dataSourceName Data source to filter results
* @param deviceId Device Id to filter results * @param deviceId Device Id to filter results
* *
* @return A collection of correlated artifact instances * @return A collection of correlated artifact instances
*/ */
@ -580,7 +577,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
* Get all other abstract files in the current case with the same MD5 as the * Get all other abstract files in the current case with the same MD5 as the
* selected node. * selected node.
* *
* @param corAttr The CorrelationAttribute containing the MD5 to search for * @param corAttr The CorrelationAttribute containing the MD5 to search for
* @param openCase The current case * @param openCase The current case
* *
* @return List of matching AbstractFile objects * @return List of matching AbstractFile objects
@ -657,11 +654,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// - The central repo is disabled and the backing file has a valid MD5 hash // - The central repo is disabled and the backing file has a valid MD5 hash
this.file = this.getAbstractFileFromNode(node); this.file = this.getAbstractFileFromNode(node);
if (EamDb.isEnabled()) { if (EamDb.isEnabled()) {
return this.file != null return !getCorrelationAttributesFromNode(node).isEmpty();
&& this.file.getSize() > 0
&& !getCorrelationAttributesFromNode(node).isEmpty();
} else { } else {
return this.file != null return this.file != null
&& this.file.getSize() > 0 && this.file.getSize() > 0
&& ((this.file.getMd5Hash() != null) && (!this.file.getMd5Hash().isEmpty())); && ((this.file.getMd5Hash() != null) && (!this.file.getMd5Hash().isEmpty()));
} }
@ -733,8 +728,8 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
* Adjust a given column for the text provided. * Adjust a given column for the text provided.
* *
* @param columnIndex The index of the column to adjust. * @param columnIndex The index of the column to adjust.
* @param text The text whose length will be used to adjust the column * @param text The text whose length will be used to adjust the
* width. * column width.
*/ */
private void setColumnWidthToText(int columnIndex, String text) { private void setColumnWidthToText(int columnIndex, String text) {
TableColumn column = otherCasesTable.getColumnModel().getColumn(columnIndex); TableColumn column = otherCasesTable.getColumnModel().getColumn(columnIndex);

View File

@ -808,9 +808,9 @@ abstract class AbstractSqlEamDb implements EamDb {
String sql String sql
= "INSERT INTO " = "INSERT INTO "
+ tableName + tableName
+ "(case_id, data_source_id, value, file_path, known_status, comment) " + "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
+ "VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), " + "VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), "
+ "(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?) " + "(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?, ?) "
+ getConflictClause(); + getConflictClause();
try { try {
@ -824,11 +824,13 @@ abstract class AbstractSqlEamDb implements EamDb {
preparedStatement.setString(4, eamArtifact.getCorrelationValue()); preparedStatement.setString(4, eamArtifact.getCorrelationValue());
preparedStatement.setString(5, eamArtifact.getFilePath().toLowerCase()); preparedStatement.setString(5, eamArtifact.getFilePath().toLowerCase());
preparedStatement.setByte(6, eamArtifact.getKnownStatus().getFileKnownValue()); preparedStatement.setByte(6, eamArtifact.getKnownStatus().getFileKnownValue());
if ("".equals(eamArtifact.getComment())) { if ("".equals(eamArtifact.getComment())) {
preparedStatement.setNull(7, Types.INTEGER); preparedStatement.setNull(7, Types.INTEGER);
} else { } else {
preparedStatement.setString(7, eamArtifact.getComment()); preparedStatement.setString(7, eamArtifact.getComment());
} }
preparedStatement.setLong(8, eamArtifact.getFileObjectId());
preparedStatement.executeUpdate(); preparedStatement.executeUpdate();
} }
@ -900,6 +902,8 @@ abstract class AbstractSqlEamDb implements EamDb {
+ ".id," + ".id,"
+ tableName + tableName
+ ".value," + ".value,"
+ tableName
+ ".file_obj_id,"
+ " cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + " cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM "
+ tableName + tableName
+ " LEFT JOIN cases ON " + " LEFT JOIN cases ON "
@ -963,6 +967,8 @@ abstract class AbstractSqlEamDb implements EamDb {
+ ".id, " + ".id, "
+ tableName + tableName
+ ".value," + ".value,"
+ tableName
+ ".file_obj_id,"
+ " cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + " cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM "
+ tableName + tableName
+ " LEFT JOIN cases ON " + " LEFT JOIN cases ON "
@ -1229,9 +1235,9 @@ abstract class AbstractSqlEamDb implements EamDb {
String sql String sql
= "INSERT INTO " = "INSERT INTO "
+ tableName + tableName
+ " (case_id, data_source_id, value, file_path, known_status, comment) " + " (case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
+ "VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), " + "VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), "
+ "(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?) " + "(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?, ?) "
+ getConflictClause(); + getConflictClause();
bulkPs = conn.prepareStatement(sql); bulkPs = conn.prepareStatement(sql);
@ -1275,6 +1281,7 @@ abstract class AbstractSqlEamDb implements EamDb {
} else { } else {
bulkPs.setString(7, eamArtifact.getComment()); bulkPs.setString(7, eamArtifact.getComment());
} }
bulkPs.setLong(8, eamArtifact.getFileObjectId());
bulkPs.addBatch(); bulkPs.addBatch();
} else { } else {
logger.log(Level.WARNING, ("Artifact value too long for central repository." logger.log(Level.WARNING, ("Artifact value too long for central repository."
@ -1439,6 +1446,68 @@ abstract class AbstractSqlEamDb implements EamDb {
} }
} }
/**
* Find a correlation attribute in the Central Repository database given the
* instance type, case, data source, object id.
*
* @param type The type of instance.
* @param correlationCase The case tied to the instance.
* @param correlationDataSource The data source tied to the instance.
* @param objectID The object id of the file tied to the
* instance.
*
* @return The correlation attribute if it exists; otherwise null.
*
* @throws EamDbException
*/
@Override
public CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase,
CorrelationDataSource correlationDataSource, long objectID) throws EamDbException, CorrelationAttributeNormalizationException {
if (correlationCase == null) {
throw new EamDbException("Correlation case is null");
}
Connection conn = connect();
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
CorrelationAttributeInstance correlationAttributeInstance = null;
try {
String tableName = EamDbUtil.correlationTypeToInstanceTableName(type);
String sql
= "SELECT id, value, file_path, known_status, comment FROM "
+ tableName
+ " WHERE case_id=?"
+ " AND file_obj_id=?";
preparedStatement = conn.prepareStatement(sql);
preparedStatement.setInt(1, correlationCase.getID());
preparedStatement.setInt(2, (int) objectID);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
int instanceId = resultSet.getInt(1);
String value = resultSet.getString(2);
String filePath = resultSet.getString(3);
int knownStatus = resultSet.getInt(4);
String comment = resultSet.getString(5);
correlationAttributeInstance = new CorrelationAttributeInstance(type, value,
instanceId, correlationCase, correlationDataSource, filePath, comment, TskData.FileKnown.valueOf((byte) knownStatus), objectID);
}
} catch (SQLException ex) {
throw new EamDbException("Error getting notable artifact instances.", ex); // NON-NLS
} finally {
EamDbUtil.closeStatement(preparedStatement);
EamDbUtil.closeResultSet(resultSet);
EamDbUtil.closeConnection(conn);
}
return correlationAttributeInstance;
}
/** /**
* Find a correlation attribute in the Central Repository database given the * Find a correlation attribute in the Central Repository database given the
* instance type, case, data source, value, and file path. * instance type, case, data source, value, and file path.
@ -1495,9 +1564,9 @@ abstract class AbstractSqlEamDb implements EamDb {
int instanceId = resultSet.getInt(1); int instanceId = resultSet.getInt(1);
int knownStatus = resultSet.getInt(2); int knownStatus = resultSet.getInt(2);
String comment = resultSet.getString(3); String comment = resultSet.getString(3);
//null objectId used because we only fall back to using this method when objectID was not available
correlationAttributeInstance = new CorrelationAttributeInstance(type, value, correlationAttributeInstance = new CorrelationAttributeInstance(type, value,
instanceId, correlationCase, correlationDataSource, filePath, comment, TskData.FileKnown.valueOf((byte) knownStatus)); instanceId, correlationCase, correlationDataSource, filePath, comment, TskData.FileKnown.valueOf((byte) knownStatus), null);
} }
} catch (SQLException ex) { } catch (SQLException ex) {
throw new EamDbException("Error getting notable artifact instances.", ex); // NON-NLS throw new EamDbException("Error getting notable artifact instances.", ex); // NON-NLS
@ -1637,6 +1706,8 @@ abstract class AbstractSqlEamDb implements EamDb {
+ ".id, " + ".id, "
+ tableName + tableName
+ ".value, " + ".value, "
+ tableName
+ ".file_obj_id,"
+ "cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM " + "cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM "
+ tableName + tableName
+ " LEFT JOIN cases ON " + " LEFT JOIN cases ON "
@ -1694,7 +1765,7 @@ abstract class AbstractSqlEamDb implements EamDb {
String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType); String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType);
String sql String sql
= "SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id, id, value FROM " = "SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id, id, value, file_obj_id FROM "
+ tableName + tableName
+ " LEFT JOIN cases ON " + " LEFT JOIN cases ON "
+ tableName + tableName
@ -2960,14 +3031,9 @@ abstract class AbstractSqlEamDb implements EamDb {
resultSet.getString("poc_phone")); resultSet.getString("poc_phone"));
} }
CorrelationCase eamCase = new CorrelationCase(resultSet.getInt("case_id"), resultSet.getString("case_uid"), resultSet.getString("case_name")); CorrelationCase eamCase = new CorrelationCase(resultSet.getInt("case_id"), resultSet.getString("case_uid"), eamOrg, resultSet.getString("case_name"),
eamCase.setOrg(eamOrg); resultSet.getString("creation_date"), resultSet.getString("case_number"), resultSet.getString("examiner_name"),
eamCase.setCreationDate(resultSet.getString("creation_date")); resultSet.getString("examiner_email"), resultSet.getString("examiner_phone"), resultSet.getString("notes"));
eamCase.setCaseNumber(resultSet.getString("case_number"));
eamCase.setExaminerName(resultSet.getString("examiner_name"));
eamCase.setExaminerEmail(resultSet.getString("examiner_email"));
eamCase.setExaminerPhone(resultSet.getString("examiner_phone"));
eamCase.setNotes(resultSet.getString("notes"));
return eamCase; return eamCase;
} }
@ -3017,7 +3083,6 @@ abstract class AbstractSqlEamDb implements EamDb {
if (null == resultSet) { if (null == resultSet) {
return null; return null;
} }
// @@@ We should have data source ID in the previous query instead of passing -1 into the below constructor
return new CorrelationAttributeInstance( return new CorrelationAttributeInstance(
aType, aType,
resultSet.getString("value"), resultSet.getString("value"),
@ -3026,8 +3091,8 @@ abstract class AbstractSqlEamDb implements EamDb {
new CorrelationDataSource(resultSet.getInt("case_id"), resultSet.getInt("data_source_id"), resultSet.getString("device_id"), resultSet.getString("name")), new CorrelationDataSource(resultSet.getInt("case_id"), resultSet.getInt("data_source_id"), resultSet.getString("device_id"), resultSet.getString("name")),
resultSet.getString("file_path"), resultSet.getString("file_path"),
resultSet.getString("comment"), resultSet.getString("comment"),
TskData.FileKnown.valueOf(resultSet.getByte("known_status")) TskData.FileKnown.valueOf(resultSet.getByte("known_status")),
); resultSet.getLong("file_obj_id"));
} }
private EamOrganization getEamOrganizationFromResultSet(ResultSet resultSet) throws SQLException { private EamOrganization getEamOrganizationFromResultSet(ResultSet resultSet) throws SQLException {
@ -3075,6 +3140,18 @@ abstract class AbstractSqlEamDb implements EamDb {
); );
} }
/**
* Determine if a specific column already exists in a specific table
*
* @param tableName the table to check for the specified column
* @param columnName the name of the column to check for
*
* @return true if the column exists, false if the column does not exist
*
* @throws EamDbException
*/
abstract boolean doesColumnExist(Connection conn, String tableName, String columnName) throws SQLException;
/** /**
* Upgrade the schema of the database (if needed) * Upgrade the schema of the database (if needed)
* *
@ -3085,6 +3162,7 @@ abstract class AbstractSqlEamDb implements EamDb {
ResultSet resultSet = null; ResultSet resultSet = null;
Statement statement = null; Statement statement = null;
PreparedStatement preparedStatement = null;
Connection conn = null; Connection conn = null;
try { try {
@ -3119,6 +3197,10 @@ abstract class AbstractSqlEamDb implements EamDb {
logger.log(Level.INFO, "Central Repository is up to date"); logger.log(Level.INFO, "Central Repository is up to date");
return; return;
} }
if (dbSchemaVersion.compareTo(CURRENT_DB_SCHEMA_VERSION) > 0) {
logger.log(Level.INFO, "Central Repository is of newer version than software creates");
return;
}
// Update from 1.0 to 1.1 // Update from 1.0 to 1.1
if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 1)) < 0) { if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 1)) < 0) {
@ -3131,7 +3213,71 @@ abstract class AbstractSqlEamDb implements EamDb {
// regardless of whether this succeeds. // regardless of whether this succeeds.
EamDbUtil.insertDefaultOrganization(conn); EamDbUtil.insertDefaultOrganization(conn);
} }
//Update to 1.2
if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 2)) < 0) {
EamDbPlatformEnum selectedPlatform = EamDbPlatformEnum.getSelectedPlatform();
final String addObjectIdColumnTemplate = "ALTER TABLE %s ADD COLUMN file_obj_id INTEGER;"; //NON-NLS
final String addSsidTableTemplate;
final String addCaseIdIndexTemplate;
final String addDataSourceIdIndexTemplate;
final String addValueIndexTemplate;
final String addKnownStatusIndexTemplate;
final String addObjectIdIndexTemplate;
final String addAttributeSql;
//get the data base specific code for creating a new _instance table
switch (selectedPlatform) {
case POSTGRESQL:
addAttributeSql = "INSERT INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?) " + getConflictClause(); //NON-NLS
addSsidTableTemplate = PostgresEamDbSettings.getCreateArtifactInstancesTableTemplate();
addCaseIdIndexTemplate = PostgresEamDbSettings.getAddCaseIdIndexTemplate();
addDataSourceIdIndexTemplate = PostgresEamDbSettings.getAddDataSourceIdIndexTemplate();
addValueIndexTemplate = PostgresEamDbSettings.getAddValueIndexTemplate();
addKnownStatusIndexTemplate = PostgresEamDbSettings.getAddKnownStatusIndexTemplate();
addObjectIdIndexTemplate = PostgresEamDbSettings.getAddObjectIdIndexTemplate();
break;
case SQLITE:
addAttributeSql = "INSERT OR IGNORE INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?)"; //NON-NLS
addSsidTableTemplate = SqliteEamDbSettings.getCreateArtifactInstancesTableTemplate();
addCaseIdIndexTemplate = SqliteEamDbSettings.getAddCaseIdIndexTemplate();
addDataSourceIdIndexTemplate = SqliteEamDbSettings.getAddDataSourceIdIndexTemplate();
addValueIndexTemplate = SqliteEamDbSettings.getAddValueIndexTemplate();
addKnownStatusIndexTemplate = SqliteEamDbSettings.getAddKnownStatusIndexTemplate();
addObjectIdIndexTemplate = SqliteEamDbSettings.getAddObjectIdIndexTemplate();
break;
default:
throw new EamDbException("Currently selected database platform \"" + selectedPlatform.name() + "\" can not be upgraded.");
}
//update central repository to be able to store new correlation attributes
final String wirelessNetworsDbTableName = "wireless_networks";
final String wirelessNetworksTableInstanceName = wirelessNetworsDbTableName + "_instances";
//add the wireless_networks attribute to the correlation_types table
preparedStatement = conn.prepareStatement(addAttributeSql);
preparedStatement.setInt(1, CorrelationAttributeInstance.SSID_TYPE_ID);
preparedStatement.setString(2, Bundle.CorrelationType_SSID_displayName());
preparedStatement.setString(3, wirelessNetworsDbTableName);
preparedStatement.setInt(4, 1);
preparedStatement.setInt(5, 1);
preparedStatement.execute();
//create a new wireless_networks_instances table and add indexes for its columns
statement.execute(String.format(addSsidTableTemplate, wirelessNetworksTableInstanceName, wirelessNetworksTableInstanceName));
statement.execute(String.format(addCaseIdIndexTemplate, wirelessNetworksTableInstanceName, wirelessNetworksTableInstanceName));
statement.execute(String.format(addDataSourceIdIndexTemplate, wirelessNetworksTableInstanceName, wirelessNetworksTableInstanceName));
statement.execute(String.format(addValueIndexTemplate, wirelessNetworksTableInstanceName, wirelessNetworksTableInstanceName));
statement.execute(String.format(addKnownStatusIndexTemplate, wirelessNetworksTableInstanceName, wirelessNetworksTableInstanceName));
//add file_obj_id column to _instances table which do not already have it
String instance_type_dbname;
for (CorrelationAttributeInstance.Type type : CorrelationAttributeInstance.getDefaultCorrelationTypes()) {
instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type);
if (!doesColumnExist(conn, instance_type_dbname, "file_obj_id")) {
statement.execute(String.format(addObjectIdColumnTemplate, instance_type_dbname)); //NON-NLS
}
statement.execute(String.format(addObjectIdIndexTemplate, instance_type_dbname, instance_type_dbname));
}
}
if (!updateSchemaVersion(conn)) { if (!updateSchemaVersion(conn)) {
throw new EamDbException("Error updating schema version"); throw new EamDbException("Error updating schema version");
} }
@ -3149,6 +3295,7 @@ abstract class AbstractSqlEamDb implements EamDb {
throw ex; throw ex;
} finally { } finally {
EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeResultSet(resultSet);
EamDbUtil.closeStatement(preparedStatement);
EamDbUtil.closeStatement(statement); EamDbUtil.closeStatement(statement);
EamDbUtil.closeConnection(conn); EamDbUtil.closeConnection(conn);
} }

View File

@ -48,46 +48,18 @@ public class CorrelationAttributeInstance implements Serializable {
private String filePath; private String filePath;
private String comment; private String comment;
private TskData.FileKnown knownStatus; private TskData.FileKnown knownStatus;
private Long objectId;
public CorrelationAttributeInstance( public CorrelationAttributeInstance(
String correlationValue,
CorrelationAttributeInstance.Type correlationType, CorrelationAttributeInstance.Type correlationType,
CorrelationCase eamCase,
CorrelationDataSource eamDataSource,
String filePath
) throws EamDbException, CorrelationAttributeNormalizationException {
this(correlationType, correlationValue, -1, eamCase, eamDataSource, filePath, null, TskData.FileKnown.UNKNOWN);
}
public CorrelationAttributeInstance(
String correlationValue, String correlationValue,
CorrelationAttributeInstance.Type correlationType,
CorrelationCase eamCase, CorrelationCase eamCase,
CorrelationDataSource eamDataSource, CorrelationDataSource eamDataSource,
String filePath, String filePath,
String comment, String comment,
TskData.FileKnown knownStatus TskData.FileKnown knownStatus,
) throws EamDbException, CorrelationAttributeNormalizationException { long fileObjectId) throws EamDbException, CorrelationAttributeNormalizationException {
this(correlationType, correlationValue, -1, eamCase, eamDataSource, filePath, comment, knownStatus); this(correlationType, correlationValue, -1, eamCase, eamDataSource, filePath, comment, knownStatus, fileObjectId);
}
public CorrelationAttributeInstance(
Type correlationType,
String correlationValue,
CorrelationCase correlationCase,
CorrelationDataSource fromTSKDataSource,
String string) throws EamDbException, CorrelationAttributeNormalizationException {
this(correlationType, correlationValue, -1, correlationCase, fromTSKDataSource, string, "", TskData.FileKnown.UNKNOWN);
}
/**
* NOTE: Only used for when EamDB is NOT enabled.
*
* @param aType CorrelationAttributeInstance.Type
* @param value correlation value
*/
public CorrelationAttributeInstance(Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException {
this(aType, value, -1, null, null, "", "", TskData.FileKnown.UNKNOWN);
} }
CorrelationAttributeInstance( CorrelationAttributeInstance(
@ -98,7 +70,8 @@ public class CorrelationAttributeInstance implements Serializable {
CorrelationDataSource eamDataSource, CorrelationDataSource eamDataSource,
String filePath, String filePath,
String comment, String comment,
TskData.FileKnown knownStatus TskData.FileKnown knownStatus,
Long fileObjectId
) throws EamDbException, CorrelationAttributeNormalizationException { ) throws EamDbException, CorrelationAttributeNormalizationException {
if (filePath == null) { if (filePath == null) {
throw new EamDbException("file path is null"); throw new EamDbException("file path is null");
@ -113,6 +86,7 @@ public class CorrelationAttributeInstance implements Serializable {
this.filePath = filePath.toLowerCase(); this.filePath = filePath.toLowerCase();
this.comment = comment; this.comment = comment;
this.knownStatus = knownStatus; this.knownStatus = knownStatus;
this.objectId = fileObjectId;
} }
public Boolean equals(CorrelationAttributeInstance otherInstance) { public Boolean equals(CorrelationAttributeInstance otherInstance) {
@ -145,14 +119,6 @@ public class CorrelationAttributeInstance implements Serializable {
return correlationValue; return correlationValue;
} }
/**
* @param correlationValue the correlationValue to set
*/
public void setCorrelationValue(String correlationValue) {
// Lower-case all values to normalize and improve correlation hits, going forward make sure this makes sense for all correlation types
this.correlationValue = correlationValue.toLowerCase();
}
/** /**
* @return the correlation Type * @return the correlation Type
*/ */
@ -160,18 +126,11 @@ public class CorrelationAttributeInstance implements Serializable {
return correlationType; return correlationType;
} }
/**
* @param correlationType the correlation Type to set
*/
public void setCorrelationType(Type correlationType) {
this.correlationType = correlationType;
}
/** /**
* Is this a database instance? * Is this a database instance?
* *
* @return True if the instance ID is greater or equal to zero; otherwise * @return True if the instance ID is greater or equal to zero; otherwise
* false. * false.
*/ */
public boolean isDatabaseInstance() { public boolean isDatabaseInstance() {
return (ID >= 0); return (ID >= 0);
@ -234,30 +193,42 @@ public class CorrelationAttributeInstance implements Serializable {
* as notable and should never be set to KNOWN. * as notable and should never be set to KNOWN.
* *
* @param knownStatus Should be BAD if the item is tagged as notable, * @param knownStatus Should be BAD if the item is tagged as notable,
* UNKNOWN otherwise * UNKNOWN otherwise
*/ */
public void setKnownStatus(TskData.FileKnown knownStatus) { public void setKnownStatus(TskData.FileKnown knownStatus) {
this.knownStatus = knownStatus; this.knownStatus = knownStatus;
} }
/**
* Get the objectId of the file associated with the correlation attribute or
* NULL if the objectId is not available.
*
* @return the objectId of the file
*/
public Long getFileObjectId() {
return objectId;
}
// Type ID's for Default Correlation Types // Type ID's for Default Correlation Types
public static final int FILES_TYPE_ID = 0; public static final int FILES_TYPE_ID = 0;
public static final int DOMAIN_TYPE_ID = 1; public static final int DOMAIN_TYPE_ID = 1;
public static final int EMAIL_TYPE_ID = 2; public static final int EMAIL_TYPE_ID = 2;
public static final int PHONE_TYPE_ID = 3; public static final int PHONE_TYPE_ID = 3;
public static final int USBID_TYPE_ID = 4; public static final int USBID_TYPE_ID = 4;
public static final int SSID_TYPE_ID = 5;
/** /**
* Load the default correlation types * Load the default correlation types
* *
* @throws EamDbException if the Type's dbTableName has invalid * @throws EamDbException if the Type's dbTableName has invalid
* characters/format * characters/format
*/ */
@Messages({"CorrelationType.FILES.displayName=Files", @Messages({"CorrelationType.FILES.displayName=Files",
"CorrelationType.DOMAIN.displayName=Domains", "CorrelationType.DOMAIN.displayName=Domains",
"CorrelationType.EMAIL.displayName=Email Addresses", "CorrelationType.EMAIL.displayName=Email Addresses",
"CorrelationType.PHONE.displayName=Phone Numbers", "CorrelationType.PHONE.displayName=Phone Numbers",
"CorrelationType.USBID.displayName=USB Devices"}) "CorrelationType.USBID.displayName=USB Devices",
"CorrelationType.SSID.displayName=Wireless Networks"})
public static List<CorrelationAttributeInstance.Type> getDefaultCorrelationTypes() throws EamDbException { public static List<CorrelationAttributeInstance.Type> getDefaultCorrelationTypes() throws EamDbException {
List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = new ArrayList<>(); List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = new ArrayList<>();
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(FILES_TYPE_ID, Bundle.CorrelationType_FILES_displayName(), "file", true, true)); // NON-NLS DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(FILES_TYPE_ID, Bundle.CorrelationType_FILES_displayName(), "file", true, true)); // NON-NLS
@ -265,6 +236,7 @@ public class CorrelationAttributeInstance implements Serializable {
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(EMAIL_TYPE_ID, Bundle.CorrelationType_EMAIL_displayName(), "email_address", true, true)); // NON-NLS DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(EMAIL_TYPE_ID, Bundle.CorrelationType_EMAIL_displayName(), "email_address", true, true)); // NON-NLS
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(PHONE_TYPE_ID, Bundle.CorrelationType_PHONE_displayName(), "phone_number", true, true)); // NON-NLS DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(PHONE_TYPE_ID, Bundle.CorrelationType_PHONE_displayName(), "phone_number", true, true)); // NON-NLS
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(USBID_TYPE_ID, Bundle.CorrelationType_USBID_displayName(), "usb_devices", true, true)); // NON-NLS DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(USBID_TYPE_ID, Bundle.CorrelationType_USBID_displayName(), "usb_devices", true, true)); // NON-NLS
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(SSID_TYPE_ID, Bundle.CorrelationType_SSID_displayName(), "wireless_networks", true, true)); // NON-NLS
return DEFAULT_CORRELATION_TYPES; return DEFAULT_CORRELATION_TYPES;
} }
@ -283,13 +255,14 @@ public class CorrelationAttributeInstance implements Serializable {
/** /**
* *
* @param typeId Unique ID for this Correlation Type * @param typeId Unique ID for this Correlation Type
* @param displayName Name of this type displayed in the UI. * @param displayName Name of this type displayed in the UI.
* @param dbTableName Central repository db table where data of this * @param dbTableName Central repository db table where data of this
* type is stored. Must start with a lowercase letter and only contain * type is stored. Must start with a lowercase letter
* lowercase letters, numbers, and '_' characters. * and only contain lowercase letters, numbers, and
* @param supported Is this Type currently supported * '_' characters.
* @param enabled Is this Type currently enabled. * @param supported Is this Type currently supported
* @param enabled Is this Type currently enabled.
*/ */
public Type(int typeId, String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { public Type(int typeId, String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException {
if (dbTableName == null) { if (dbTableName == null) {
@ -312,10 +285,11 @@ public class CorrelationAttributeInstance implements Serializable {
* *
* @param displayName Name of this type displayed in the UI. * @param displayName Name of this type displayed in the UI.
* @param dbTableName Central repository db table where data of this * @param dbTableName Central repository db table where data of this
* type is stored Must start with a lowercase letter and only contain * type is stored Must start with a lowercase letter
* lowercase letters, numbers, and '_' characters. * and only contain lowercase letters, numbers, and
* @param supported Is this Type currently supported * '_' characters.
* @param enabled Is this Type currently enabled. * @param supported Is this Type currently supported
* @param enabled Is this Type currently enabled.
*/ */
public Type(String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { public Type(String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException {
this(-1, displayName, dbTableName, supported, enabled); this(-1, displayName, dbTableName, supported, enabled);
@ -477,8 +451,8 @@ public class CorrelationAttributeInstance implements Serializable {
* custom_instances) * custom_instances)
* *
* @param dbTableName the dbTableName to set. Must start with lowercase * @param dbTableName the dbTableName to set. Must start with lowercase
* letter and can only contain lowercase letters, numbers, and '_' * letter and can only contain lowercase letters,
* characters. * numbers, and '_' characters.
* *
* @throws EamDbException if dbTableName contains invalid characters * @throws EamDbException if dbTableName contains invalid characters
*/ */

View File

@ -63,6 +63,8 @@ final public class CorrelationAttributeNormalizer {
return normalizePhone(data); return normalizePhone(data);
case CorrelationAttributeInstance.USBID_TYPE_ID: case CorrelationAttributeInstance.USBID_TYPE_ID:
return normalizeUsbId(data); return normalizeUsbId(data);
case CorrelationAttributeInstance.SSID_TYPE_ID:
return data;
default: default:
final String errorMessage = String.format( final String errorMessage = String.format(
"Validator function not found for attribute type: %s", "Validator function not found for attribute type: %s",

View File

@ -54,9 +54,9 @@ public class EamArtifactUtil {
* EamArtifact with a single EamArtifactInstance within. If not, return * EamArtifact with a single EamArtifactInstance within. If not, return
* null. * null.
* *
* @param bbArtifact BlackboardArtifact to examine * @param bbArtifact BlackboardArtifact to examine
* @param checkEnabled If true, only create a CorrelationAttribute if it is * @param checkEnabled If true, only create a CorrelationAttribute if it is
* enabled * enabled
* *
* @return List of EamArtifacts * @return List of EamArtifacts
*/ */
@ -93,10 +93,10 @@ public class EamArtifactUtil {
* based on the data in the blackboard artifact. * based on the data in the blackboard artifact.
* *
* @param correlationType The Central Repository artifact type to create * @param correlationType The Central Repository artifact type to create
* @param bbArtifact The blackboard artifact to pull data from * @param bbArtifact The blackboard artifact to pull data from
* *
* @return the new EamArtifact, or null if one was not created because * @return the new EamArtifact, or null if one was not created because
* bbArtifact did not contain the needed data * bbArtifact did not contain the needed data
*/ */
private static CorrelationAttributeInstance makeInstanceFromBlackboardArtifact(CorrelationAttributeInstance.Type correlationType, private static CorrelationAttributeInstance makeInstanceFromBlackboardArtifact(CorrelationAttributeInstance.Type correlationType,
BlackboardArtifact bbArtifact) throws EamDbException { BlackboardArtifact bbArtifact) throws EamDbException {
@ -159,13 +159,14 @@ public class EamArtifactUtil {
return null; return null;
} }
} }
} else if (correlationType.getId() == CorrelationAttributeInstance.USBID_TYPE_ID } else if (correlationType.getId() == CorrelationAttributeInstance.USBID_TYPE_ID
&& BlackboardArtifact.ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID() == artifactTypeID) { && BlackboardArtifact.ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID() == artifactTypeID) {
value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID)).getValueString(); value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID)).getValueString();
} else if (correlationType.getId() == CorrelationAttributeInstance.SSID_TYPE_ID
&& BlackboardArtifact.ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID() == artifactTypeID) {
value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SSID)).getValueString();
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting attribute while getting type from BlackboardArtifact.", ex); // NON-NLS logger.log(Level.SEVERE, "Error getting attribute while getting type from BlackboardArtifact.", ex); // NON-NLS
return null; return null;
@ -185,9 +186,10 @@ public class EamArtifactUtil {
* Uses the determined type and vallue, then looks up instance details to * Uses the determined type and vallue, then looks up instance details to
* create proper CorrelationAttributeInstance. * create proper CorrelationAttributeInstance.
* *
* @param bbArtifact the blackboard artifatc * @param bbArtifact the blackboard artifact
* @param correlationType the given type * @param correlationType the given type
* @param value the artifact value * @param value the artifact value
*
* @return CorrelationAttributeInstance from details * @return CorrelationAttributeInstance from details
*/ */
private static CorrelationAttributeInstance makeCorrelationAttributeInstanceUsingTypeValue(BlackboardArtifact bbArtifact, CorrelationAttributeInstance.Type correlationType, String value) { private static CorrelationAttributeInstance makeCorrelationAttributeInstanceUsingTypeValue(BlackboardArtifact bbArtifact, CorrelationAttributeInstance.Type correlationType, String value) {
@ -205,14 +207,14 @@ public class EamArtifactUtil {
correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows()); correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows());
} }
return new CorrelationAttributeInstance( return new CorrelationAttributeInstance(
value,
correlationType, correlationType,
value,
correlationCase, correlationCase,
CorrelationDataSource.fromTSKDataSource(correlationCase, bbSourceFile.getDataSource()), CorrelationDataSource.fromTSKDataSource(correlationCase, bbSourceFile.getDataSource()),
bbSourceFile.getParentPath() + bbSourceFile.getName(), bbSourceFile.getParentPath() + bbSourceFile.getName(),
"", "",
TskData.FileKnown.UNKNOWN TskData.FileKnown.UNKNOWN,
); bbSourceFile.getId());
} catch (TskCoreException | EamDbException | CorrelationAttributeNormalizationException ex) { } catch (TskCoreException | EamDbException | CorrelationAttributeNormalizationException ex) {
logger.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS logger.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS
@ -245,8 +247,6 @@ public class EamArtifactUtil {
CorrelationAttributeInstance.Type type; CorrelationAttributeInstance.Type type;
CorrelationCase correlationCase; CorrelationCase correlationCase;
CorrelationDataSource correlationDataSource; CorrelationDataSource correlationDataSource;
String value;
String filePath;
try { try {
type = EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); type = EamDb.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
@ -256,8 +256,6 @@ public class EamArtifactUtil {
return null; return null;
} }
correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource()); correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource());
value = file.getMd5Hash();
filePath = (file.getParentPath() + file.getName()).toLowerCase();
} catch (TskCoreException | EamDbException ex) { } catch (TskCoreException | EamDbException ex) {
logger.log(Level.SEVERE, "Error retrieving correlation attribute.", ex); logger.log(Level.SEVERE, "Error retrieving correlation attribute.", ex);
return null; return null;
@ -268,13 +266,26 @@ public class EamArtifactUtil {
CorrelationAttributeInstance correlationAttributeInstance; CorrelationAttributeInstance correlationAttributeInstance;
try { try {
correlationAttributeInstance = EamDb.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, value, filePath); correlationAttributeInstance = EamDb.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, file.getId());
} catch (EamDbException | CorrelationAttributeNormalizationException ex) { } catch (EamDbException | CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, String.format( logger.log(Level.WARNING, String.format(
"Correlation attribute could not be retrieved for '%s' (id=%d): %s", "Correlation attribute could not be retrieved for '%s' (id=%d): %s",
content.getName(), content.getId(), ex.getMessage())); content.getName(), content.getId(), ex.getMessage()));
return null; return null;
} }
//if there was no correlation attribute found for the item using object_id then check for attributes added with schema 1,1 which lack object_id
if (correlationAttributeInstance == null) {
String value = file.getMd5Hash();
String filePath = (file.getParentPath() + file.getName()).toLowerCase();
try {
correlationAttributeInstance = EamDb.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, value, filePath);
} catch (EamDbException | CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, String.format(
"Correlation attribute could not be retrieved for '%s' (id=%d): %s",
content.getName(), content.getId(), ex.getMessage()));
return null;
}
}
return correlationAttributeInstance; return correlationAttributeInstance;
} }
@ -317,12 +328,16 @@ public class EamArtifactUtil {
if (null == correlationCase) { if (null == correlationCase) {
correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows()); correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows());
} }
return new CorrelationAttributeInstance( return new CorrelationAttributeInstance(
filesType, filesType,
af.getMd5Hash(), af.getMd5Hash(),
correlationCase, correlationCase,
CorrelationDataSource.fromTSKDataSource(correlationCase, af.getDataSource()), CorrelationDataSource.fromTSKDataSource(correlationCase, af.getDataSource()),
af.getParentPath() + af.getName()); af.getParentPath() + af.getName(),
"",
TskData.FileKnown.UNKNOWN,
af.getId());
} catch (TskCoreException | EamDbException | CorrelationAttributeNormalizationException ex) { } catch (TskCoreException | EamDbException | CorrelationAttributeNormalizationException ex) {
logger.log(Level.SEVERE, "Error making correlation attribute.", ex); logger.log(Level.SEVERE, "Error making correlation attribute.", ex);
@ -340,7 +355,7 @@ public class EamArtifactUtil {
* @param file The file to test * @param file The file to test
* *
* @return true if the file should be added to the central repo, false * @return true if the file should be added to the central repo, false
* otherwise * otherwise
*/ */
public static boolean isSupportedAbstractFileType(AbstractFile file) { public static boolean isSupportedAbstractFileType(AbstractFile file) {
if (file == null) { if (file == null) {

View File

@ -31,11 +31,10 @@ import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
*/ */
public interface EamDb { public interface EamDb {
public static final int SCHEMA_VERSION = 1; public static final int SCHEMA_VERSION = 2;
public static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION public static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION
= new CaseDbSchemaVersionNumber(1, 1); = new CaseDbSchemaVersionNumber(1, 2);
/** /**
* Get the instance * Get the instance
* *
@ -183,13 +182,14 @@ public interface EamDb {
* @return The retrieved case * @return The retrieved case
*/ */
CorrelationCase getCaseById(int caseId) throws EamDbException; CorrelationCase getCaseById(int caseId) throws EamDbException;
/** /**
* Retrieves cases that are in DB. * Retrieves cases that are in DB.
* *
* @return List of cases * @return List of cases
*/ */
List<CorrelationCase> getCases() throws EamDbException; List<CorrelationCase> getCases() throws EamDbException;
/** /**
* Creates new Data Source in the database * Creates new Data Source in the database
* *
@ -208,18 +208,17 @@ public interface EamDb {
*/ */
CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException; CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException;
/** /**
* Retrieves Data Source details based on data source ID * Retrieves Data Source details based on data source ID
* *
* @param correlationCase the current CorrelationCase used for ensuring * @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource * uniqueness of DataSource
* @param dataSourceId the data source ID number * @param dataSourceId the data source ID number
* *
* @return The data source * @return The data source
*/ */
CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws EamDbException; CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws EamDbException;
/** /**
* Retrieves data sources that are in DB * Retrieves data sources that are in DB
* *
@ -245,7 +244,7 @@ public interface EamDb {
* @return List of artifact instances for a given type/value * @return List of artifact instances for a given type/value
*/ */
List<CorrelationAttributeInstance> getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException; List<CorrelationAttributeInstance> getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException;
/** /**
* Retrieves eamArtifact instances from the database that are associated * Retrieves eamArtifact instances from the database that are associated
* with the aType and filePath * with the aType and filePath
@ -314,8 +313,8 @@ public interface EamDb {
/** /**
* Adds an eamArtifact to an internal list to be later added to DB. Artifact * Adds an eamArtifact to an internal list to be later added to DB. Artifact
can have 1 or more Artifact Instances. Insert will be triggered by a * can have 1 or more Artifact Instances. Insert will be triggered by a
threshold or a call to commitAttributeInstancesBulk(). * threshold or a call to commitAttributeInstancesBulk().
* *
* @param eamArtifact The artifact to add * @param eamArtifact The artifact to add
*/ */
@ -323,7 +322,7 @@ public interface EamDb {
/** /**
* Executes a bulk insert of the eamArtifacts added from the * Executes a bulk insert of the eamArtifacts added from the
addAttributeInstanceBulk() method * addAttributeInstanceBulk() method
*/ */
void commitAttributeInstancesBulk() throws EamDbException; void commitAttributeInstancesBulk() throws EamDbException;
@ -346,6 +345,9 @@ public interface EamDb {
/** /**
* Find a correlation attribute in the Central Repository database given the * Find a correlation attribute in the Central Repository database given the
* instance type, case, data source, value, and file path. * instance type, case, data source, value, and file path.
*
* Method exists to support instances added using Central Repository version 1,1 and
* older
* *
* @param type The type of instance. * @param type The type of instance.
* @param correlationCase The case tied to the instance. * @param correlationCase The case tied to the instance.
@ -354,12 +356,28 @@ public interface EamDb {
* @param filePath The file path tied to the instance. * @param filePath The file path tied to the instance.
* *
* @return The correlation attribute if it exists; otherwise null. * @return The correlation attribute if it exists; otherwise null.
* *
* @throws EamDbException * @throws EamDbException
*/ */
CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase, CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase,
CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException, CorrelationAttributeNormalizationException; CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException, CorrelationAttributeNormalizationException;
/**
* Find a correlation attribute in the Central Repository database given the
* instance type, case, data source, object id.
*
* @param type The type of instance.
* @param correlationCase The case tied to the instance.
* @param correlationDataSource The data source tied to the instance.
* @param objectID The object id of the file tied to the instance.
*
* @return The correlation attribute if it exists; otherwise null.
*
* @throws EamDbException
*/
CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase,
CorrelationDataSource correlationDataSource, long objectID) throws EamDbException, CorrelationAttributeNormalizationException;
/** /**
* Sets an eamArtifact instance to the given known status. If eamArtifact * Sets an eamArtifact instance to the given known status. If eamArtifact
* exists, it is updated. If eamArtifact does not exist nothing happens * exists, it is updated. If eamArtifact does not exist nothing happens
@ -383,12 +401,15 @@ public interface EamDb {
/** /**
* Gets list of matching eamArtifact instances that have knownStatus = * Gets list of matching eamArtifact instances that have knownStatus =
* "Bad". * "Bad".
* *
* @param aType EamArtifact.Type to search for * @param aType EamArtifact.Type to search for
*
* @return List with 0 or more matching eamArtifact instances. * @return List with 0 or more matching eamArtifact instances.
*
* @throws EamDbException * @throws EamDbException
*/ */
List<CorrelationAttributeInstance> getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType) throws EamDbException; List<CorrelationAttributeInstance> getArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType) throws EamDbException;
/** /**
* Count matching eamArtifacts instances that have knownStatus = "Bad". * Count matching eamArtifacts instances that have knownStatus = "Bad".
* *
@ -490,7 +511,7 @@ public interface EamDb {
* *
* @param eamOrg The organization to add * @param eamOrg The organization to add
* *
* @return The organization with the org ID set. * @return The organization with the org ID set.
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -700,18 +721,20 @@ public interface EamDb {
/** /**
* Process the Artifact instance in the EamDb * Process the Artifact instance in the EamDb
* *
* @param type EamArtifact.Type to search for * @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance * @param instanceTableCallback callback to process the instance
*
* @throws EamDbException * @throws EamDbException
*/ */
void processInstanceTable(CorrelationAttributeInstance.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException; void processInstanceTable(CorrelationAttributeInstance.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException;
/** /**
* Process the Artifact instance in the EamDb * Process the Artifact instance in the EamDb
* *
* @param type EamArtifact.Type to search for * @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance * @param instanceTableCallback callback to process the instance
* @param whereClause query string to execute * @param whereClause query string to execute
*
* @throws EamDbException * @throws EamDbException
*/ */
void processInstanceTableWhere(CorrelationAttributeInstance.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException; void processInstanceTableWhere(CorrelationAttributeInstance.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException;

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.centralrepository.datamodel; package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.sql.Connection; import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -29,8 +30,7 @@ import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
/** /**
* Central Repository database implementation using Postgres as a * Central Repository database implementation using Postgres as a backend
* backend
*/ */
final class PostgresEamDb extends AbstractSqlEamDb { final class PostgresEamDb extends AbstractSqlEamDb {
@ -47,10 +47,11 @@ final class PostgresEamDb extends AbstractSqlEamDb {
/** /**
* Get the singleton instance of PostgresEamDb * Get the singleton instance of PostgresEamDb
* *
* @return the singleton instance of PostgresEamDb * @return the singleton instance of PostgresEamDb
* *
* @throws EamDbException if one or more default correlation type(s) have an invalid db table name. * @throws EamDbException if one or more default correlation type(s) have an
* invalid db table name.
*/ */
public synchronized static PostgresEamDb getInstance() throws EamDbException { public synchronized static PostgresEamDb getInstance() throws EamDbException {
if (instance == null) { if (instance == null) {
@ -61,9 +62,10 @@ final class PostgresEamDb extends AbstractSqlEamDb {
} }
/** /**
* *
* @throws EamDbException if the AbstractSqlEamDb class has one or more default * @throws EamDbException if the AbstractSqlEamDb class has one or more
* correlation type(s) having an invalid db table name. * default correlation type(s) having an invalid db
* table name.
*/ */
private PostgresEamDb() throws EamDbException { private PostgresEamDb() throws EamDbException {
dbSettings = new PostgresEamDbSettings(); dbSettings = new PostgresEamDbSettings();
@ -73,8 +75,8 @@ final class PostgresEamDb extends AbstractSqlEamDb {
@Override @Override
public void shutdownConnections() throws EamDbException { public void shutdownConnections() throws EamDbException {
try { try {
synchronized(this) { synchronized (this) {
if(connectionPool != null){ if (connectionPool != null) {
connectionPool.close(); connectionPool.close();
connectionPool = null; // force it to be re-created on next connect() connectionPool = null; // force it to be re-created on next connect()
} }
@ -148,7 +150,7 @@ final class PostgresEamDb extends AbstractSqlEamDb {
connectionURL.append(dbSettings.getPort()); connectionURL.append(dbSettings.getPort());
connectionURL.append("/"); connectionURL.append("/");
connectionURL.append(dbSettings.getDbName()); connectionURL.append(dbSettings.getDbName());
connectionPool.setUrl(connectionURL.toString()); connectionPool.setUrl(connectionURL.toString());
connectionPool.setUsername(dbSettings.getUserName()); connectionPool.setUsername(dbSettings.getUserName());
connectionPool.setPassword(dbSettings.getPassword()); connectionPool.setPassword(dbSettings.getPassword());
@ -189,31 +191,34 @@ final class PostgresEamDb extends AbstractSqlEamDb {
protected String getConflictClause() { protected String getConflictClause() {
return CONFLICT_CLAUSE; return CONFLICT_CLAUSE;
} }
/** /**
* Gets an exclusive lock (if applicable). * Gets an exclusive lock (if applicable). Will return the lock if
* Will return the lock if successful, null if unsuccessful because locking * successful, null if unsuccessful because locking isn't supported, and
* isn't supported, and throw an exception if we should have been able to get the * throw an exception if we should have been able to get the lock but failed
* lock but failed (meaning the database is in use). * (meaning the database is in use).
*
* @return the lock, or null if locking is not supported * @return the lock, or null if locking is not supported
* @throws EamDbException if the coordination service is running but we fail to get the lock *
* @throws EamDbException if the coordination service is running but we fail
* to get the lock
*/ */
@Override @Override
public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException{ public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException {
try { try {
// First check if multi user mode is enabled - if not there's no point trying to get a lock // First check if multi user mode is enabled - if not there's no point trying to get a lock
if( ! UserPreferences.getIsMultiUserModeEnabled()){ if (!UserPreferences.getIsMultiUserModeEnabled()) {
return null; return null;
} }
String databaseNodeName = dbSettings.getHost() + "_" + dbSettings.getDbName(); String databaseNodeName = dbSettings.getHost() + "_" + dbSettings.getDbName();
CoordinationService.Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CoordinationService.CategoryNode.CENTRAL_REPO, databaseNodeName, 5, TimeUnit.MINUTES); CoordinationService.Lock lock = CoordinationService.getInstance().tryGetExclusiveLock(CoordinationService.CategoryNode.CENTRAL_REPO, databaseNodeName, 5, TimeUnit.MINUTES);
if(lock != null){ if (lock != null) {
return lock; return lock;
} }
throw new EamDbException("Error acquiring database lock"); throw new EamDbException("Error acquiring database lock");
} catch (InterruptedException ex){ } catch (InterruptedException ex) {
throw new EamDbException("Error acquiring database lock"); throw new EamDbException("Error acquiring database lock");
} catch (CoordinationService.CoordinationServiceException ex) { } catch (CoordinationService.CoordinationServiceException ex) {
// This likely just means the coordination service isn't running, which is ok // This likely just means the coordination service isn't running, which is ok
@ -221,4 +226,23 @@ final class PostgresEamDb extends AbstractSqlEamDb {
} }
} }
@Override
boolean doesColumnExist(Connection conn, String tableName, String columnName) throws SQLException {
final String objectIdColumnExistsTemplate = "SELECT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='%s' AND column_name='%s')"; //NON-NLS
ResultSet resultSet = null;
Statement statement = null;
boolean columnExists = false;
try {
statement = conn.createStatement();
resultSet = statement.executeQuery(String.format(objectIdColumnExistsTemplate, tableName, columnName));
if (resultSet.next()) {
columnExists = resultSet.getBoolean(1);
}
} finally {
EamDbUtil.closeResultSet(resultSet);
EamDbUtil.closeStatement(statement);
}
return columnExists;
}
} }

View File

@ -35,8 +35,9 @@ import org.sleuthkit.autopsy.coreutils.TextConverterException;
/** /**
* Settings for the Postgres implementation of the Central Repository database * Settings for the Postgres implementation of the Central Repository database
* *
* NOTE: This is public scope because the options panel calls it directly to set/get * NOTE: This is public scope because the options panel calls it directly to
* set/get
*/ */
public final class PostgresEamDbSettings { public final class PostgresEamDbSettings {
@ -266,7 +267,7 @@ public final class PostgresEamDbSettings {
return true; return true;
} }
public boolean deleteDatabase() { public boolean deleteDatabase() {
Connection conn = getEphemeralConnection(true); Connection conn = getEphemeralConnection(true);
if (null == conn) { if (null == conn) {
@ -391,26 +392,13 @@ public final class PostgresEamDbSettings {
createCorrelationTypesTable.append("CONSTRAINT correlation_types_names UNIQUE (display_name, db_table_name)"); createCorrelationTypesTable.append("CONSTRAINT correlation_types_names UNIQUE (display_name, db_table_name)");
createCorrelationTypesTable.append(")"); createCorrelationTypesTable.append(")");
// Each "%s" will be replaced with the relevant TYPE_instances table name. String createArtifactInstancesTableTemplate = getCreateArtifactInstancesTableTemplate();
StringBuilder createArtifactInstancesTableTemplate = new StringBuilder();
createArtifactInstancesTableTemplate.append("CREATE TABLE IF NOT EXISTS %s (");
createArtifactInstancesTableTemplate.append("id SERIAL PRIMARY KEY,");
createArtifactInstancesTableTemplate.append("case_id integer NOT NULL,");
createArtifactInstancesTableTemplate.append("data_source_id integer NOT NULL,");
createArtifactInstancesTableTemplate.append("value text NOT NULL,");
createArtifactInstancesTableTemplate.append("file_path text NOT NULL,");
createArtifactInstancesTableTemplate.append("known_status integer NOT NULL,");
createArtifactInstancesTableTemplate.append("comment text,");
createArtifactInstancesTableTemplate.append("CONSTRAINT %s_multi_unique_ UNIQUE (data_source_id, value, file_path),");
createArtifactInstancesTableTemplate.append("foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,");
createArtifactInstancesTableTemplate.append("foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL");
createArtifactInstancesTableTemplate.append(")");
// Each "%s" will be replaced with the relevant TYPE_instances table name. String instancesCaseIdIdx = getAddCaseIdIndexTemplate();
String instancesIdx1 = "CREATE INDEX IF NOT EXISTS %s_case_id ON %s (case_id)"; String instancesDatasourceIdIdx = getAddDataSourceIdIndexTemplate();
String instancesIdx2 = "CREATE INDEX IF NOT EXISTS %s_data_source_id ON %s (data_source_id)"; String instancesValueIdx = getAddValueIndexTemplate();
String instancesIdx3 = "CREATE INDEX IF NOT EXISTS %s_value ON %s (value)"; String instancesKnownStatusIdx = getAddKnownStatusIndexTemplate();
String instancesIdx4 = "CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)"; String instancesObjectIdIdx = getAddObjectIdIndexTemplate();
StringBuilder createDbInfoTable = new StringBuilder(); StringBuilder createDbInfoTable = new StringBuilder();
createDbInfoTable.append("CREATE TABLE IF NOT EXISTS db_info ("); createDbInfoTable.append("CREATE TABLE IF NOT EXISTS db_info (");
@ -447,25 +435,26 @@ public final class PostgresEamDbSettings {
// Create a separate instance and reference table for each correlation type // Create a separate instance and reference table for each correlation type
List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes(); List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes();
String reference_type_dbname; String reference_type_dbname;
String instance_type_dbname; String instance_type_dbname;
for (CorrelationAttributeInstance.Type type : DEFAULT_CORRELATION_TYPES) { for (CorrelationAttributeInstance.Type type : DEFAULT_CORRELATION_TYPES) {
reference_type_dbname = EamDbUtil.correlationTypeToReferenceTableName(type); reference_type_dbname = EamDbUtil.correlationTypeToReferenceTableName(type);
instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type); instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type);
stmt.execute(String.format(createArtifactInstancesTableTemplate.toString(), instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(createArtifactInstancesTableTemplate, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesIdx1, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesCaseIdIdx, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesIdx2, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesDatasourceIdIdx, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesIdx3, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesValueIdx, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesIdx4, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesKnownStatusIdx, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesObjectIdIdx, instance_type_dbname, instance_type_dbname));
// FUTURE: allow more than the FILES type // FUTURE: allow more than the FILES type
if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) { if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
stmt.execute(String.format(createReferenceTypesTableTemplate.toString(), reference_type_dbname, reference_type_dbname)); stmt.execute(String.format(createReferenceTypesTableTemplate.toString(), reference_type_dbname, reference_type_dbname));
stmt.execute(String.format(referenceTypesIdx1, reference_type_dbname, reference_type_dbname)); stmt.execute(String.format(referenceTypesIdx1, reference_type_dbname, reference_type_dbname));
stmt.execute(String.format(referenceTypesIdx2, reference_type_dbname, reference_type_dbname)); stmt.execute(String.format(referenceTypesIdx2, reference_type_dbname, reference_type_dbname));
} }
} }
} catch (SQLException ex) { } catch (SQLException ex) {
@ -480,6 +469,97 @@ public final class PostgresEamDbSettings {
return true; return true;
} }
/**
* Get the template String for creating a new _instances table in a Postgres
* central repository. %s will exist in the template where the name of the
* new table will be addedd.
*
* @return a String which is a template for cretating a new _instances table
*/
static String getCreateArtifactInstancesTableTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
StringBuilder createArtifactInstancesTableTemplate = new StringBuilder();
createArtifactInstancesTableTemplate.append("CREATE TABLE IF NOT EXISTS %s (");
createArtifactInstancesTableTemplate.append("id SERIAL PRIMARY KEY,");
createArtifactInstancesTableTemplate.append("case_id integer NOT NULL,");
createArtifactInstancesTableTemplate.append("data_source_id integer NOT NULL,");
createArtifactInstancesTableTemplate.append("value text NOT NULL,");
createArtifactInstancesTableTemplate.append("file_path text NOT NULL,");
createArtifactInstancesTableTemplate.append("known_status integer NOT NULL,");
createArtifactInstancesTableTemplate.append("comment text,");
createArtifactInstancesTableTemplate.append("file_obj_id integer,");
createArtifactInstancesTableTemplate.append("CONSTRAINT %s_multi_unique_ UNIQUE (data_source_id, value, file_path),");
createArtifactInstancesTableTemplate.append("foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,");
createArtifactInstancesTableTemplate.append("foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL");
createArtifactInstancesTableTemplate.append(")");
return createArtifactInstancesTableTemplate.toString();
}
/**
* Get the template for creating an index on the case_id column of an
* instance table. %s will exist in the template where the name of the new
* table will be addedd.
*
* @return a String which is a template for adding an index to the case_id
* column of a _instances table
*/
static String getAddCaseIdIndexTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "CREATE INDEX IF NOT EXISTS %s_case_id ON %s (case_id)";
}
/**
* Get the template for creating an index on the data_source_id column of an
* instance table. %s will exist in the template where the name of the new
* table will be addedd.
*
* @return a String which is a template for adding an index to the
* data_source_id column of a _instances table
*/
static String getAddDataSourceIdIndexTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "CREATE INDEX IF NOT EXISTS %s_data_source_id ON %s (data_source_id)";
}
/**
* Get the template for creating an index on the value column of an instance
* table. %s will exist in the template where the name of the new table will
* be addedd.
*
* @return a String which is a template for adding an index to the value
* column of a _instances table
*/
static String getAddValueIndexTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "CREATE INDEX IF NOT EXISTS %s_value ON %s (value)";
}
/**
* Get the template for creating an index on the known_status column of an
* instance table. %s will exist in the template where the name of the new
* table will be addedd.
*
* @return a String which is a template for adding an index to the
* known_status column of a _instances table
*/
static String getAddKnownStatusIndexTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)";
}
/**
* Get the template for creating an index on the file_obj_id column of an
* instance table. %s will exist in the template where the name of the new
* table will be addedd.
*
* @return a String which is a template for adding an index to the file_obj_id
* column of a _instances table
*/
static String getAddObjectIdIndexTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "CREATE INDEX IF NOT EXISTS %s_file_obj_id ON %s (file_obj_id)";
}
public boolean insertDefaultDatabaseContent() { public boolean insertDefaultDatabaseContent() {
Connection conn = getEphemeralConnection(false); Connection conn = getEphemeralConnection(false);
if (null == conn) { if (null == conn) {

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.centralrepository.datamodel; package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.sql.Connection; import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Arrays; import java.util.Arrays;
@ -57,7 +58,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @return the singleton instance of SqliteEamDb * @return the singleton instance of SqliteEamDb
* *
* @throws EamDbException if one or more default correlation type(s) have an * @throws EamDbException if one or more default correlation type(s) have an
* invalid db table name. * invalid db table name.
*/ */
public synchronized static SqliteEamDb getInstance() throws EamDbException { public synchronized static SqliteEamDb getInstance() throws EamDbException {
if (instance == null) { if (instance == null) {
@ -70,7 +71,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* *
* @throws EamDbException if the AbstractSqlEamDb class has one or more * @throws EamDbException if the AbstractSqlEamDb class has one or more
* default correlation type(s) having an invalid db table name. * default correlation type(s) having an invalid db
* table name.
*/ */
private SqliteEamDb() throws EamDbException { private SqliteEamDb() throws EamDbException {
dbSettings = new SqliteEamDbSettings(); dbSettings = new SqliteEamDbSettings();
@ -205,7 +207,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Add a new name/value pair in the db_info table. * Add a new name/value pair in the db_info table.
* *
* @param name Key to set * @param name Key to set
* @param value Value to set * @param value Value to set
* *
* @throws EamDbException * @throws EamDbException
@ -242,7 +244,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Update the value for a name in the name/value db_info table. * Update the value for a name in the name/value db_info table.
* *
* @param name Name to find * @param name Name to find
* @param value Value to assign to name. * @param value Value to assign to name.
* *
* @throws EamDbException * @throws EamDbException
@ -372,8 +374,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Retrieves Data Source details based on data source device ID * Retrieves Data Source details based on data source device ID
* *
* @param correlationCase the current CorrelationCase used for ensuring * @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource * uniqueness of DataSource
* @param dataSourceDeviceId the data source device ID number * @param dataSourceDeviceId the data source device ID number
* *
* @return The data source * @return The data source
@ -387,13 +389,13 @@ final class SqliteEamDb extends AbstractSqlEamDb {
releaseSharedLock(); releaseSharedLock();
} }
} }
/** /**
* Retrieves Data Source details based on data source ID * Retrieves Data Source details based on data source ID
* *
* @param correlationCase the current CorrelationCase used for ensuring * @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource * uniqueness of DataSource
* @param dataSourceId the data source ID number * @param dataSourceId the data source ID number
* *
* @return The data source * @return The data source
*/ */
@ -461,7 +463,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* Retrieves eamArtifact instances from the database that are associated * Retrieves eamArtifact instances from the database that are associated
* with the aType and filePath * with the aType and filePath
* *
* @param aType EamArtifact.Type to search for * @param aType EamArtifact.Type to search for
* @param filePath File path to search for * @param filePath File path to search for
* *
* @return List of 0 or more EamArtifactInstances * @return List of 0 or more EamArtifactInstances
@ -486,7 +488,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @param value The value to search for * @param value The value to search for
* *
* @return Number of artifact instances having ArtifactType and * @return Number of artifact instances having ArtifactType and
* ArtifactValue. * ArtifactValue.
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -518,6 +521,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @param value The value to search for * @param value The value to search for
* *
* @return Number of unique tuples * @return Number of unique tuples
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -545,11 +549,11 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* associated with the caseDisplayName and dataSource of the given * associated with the caseDisplayName and dataSource of the given
* eamArtifact instance. * eamArtifact instance.
* *
* @param caseUUID Case ID to search for * @param caseUUID Case ID to search for
* @param dataSourceID Data source ID to search for * @param dataSourceID Data source ID to search for
* *
* @return Number of artifact instances having caseDisplayName and * @return Number of artifact instances having caseDisplayName and
* dataSource * dataSource
*/ */
@Override @Override
public Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException { public Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException {
@ -563,7 +567,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Executes a bulk insert of the eamArtifacts added from the * Executes a bulk insert of the eamArtifacts added from the
addAttributeInstanceBulk() method * addAttributeInstanceBulk() method
*/ */
@Override @Override
public void commitAttributeInstancesBulk() throws EamDbException { public void commitAttributeInstancesBulk() throws EamDbException {
@ -596,7 +600,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* *
* @param eamArtifact Artifact containing exactly one (1) ArtifactInstance. * @param eamArtifact Artifact containing exactly one (1) ArtifactInstance.
* @param knownStatus The status to change the artifact to. Should never be * @param knownStatus The status to change the artifact to. Should never be
* KNOWN * KNOWN
*/ */
@Override @Override
public void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws EamDbException { public void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws EamDbException {
@ -633,7 +637,9 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* "Bad". * "Bad".
* *
* @param aType EamArtifact.Type to search for * @param aType EamArtifact.Type to search for
*
* @return List with 0 or more matching eamArtifact instances. * @return List with 0 or more matching eamArtifact instances.
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -672,7 +678,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @param value Value to search for * @param value Value to search for
* *
* @return List of cases containing this artifact with instances marked as * @return List of cases containing this artifact with instances marked as
* bad * bad
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -690,6 +696,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* Remove a reference set and all values contained in it. * Remove a reference set and all values contained in it.
* *
* @param referenceSetID * @param referenceSetID
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -708,6 +715,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @param value * @param value
* @param referenceSetID * @param referenceSetID
* @param correlationTypeID * @param correlationTypeID
*
* @return true if the hash is found in the reference set * @return true if the hash is found in the reference set
*/ */
@Override @Override
@ -723,8 +731,9 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Process the Artifact instance in the EamDb * Process the Artifact instance in the EamDb
* *
* @param type EamArtifact.Type to search for * @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance * @param instanceTableCallback callback to process the instance
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -736,12 +745,13 @@ final class SqliteEamDb extends AbstractSqlEamDb {
releaseSharedLock(); releaseSharedLock();
} }
} }
/** /**
* Process the Artifact instance in the EamDb * Process the Artifact instance in the EamDb
* *
* @param type EamArtifact.Type to search for * @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance * @param instanceTableCallback callback to process the instance
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -752,7 +762,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
} finally { } finally {
releaseSharedLock(); releaseSharedLock();
} }
} }
/** /**
* Check whether a reference set with the given name/version is in the * Check whether a reference set with the given name/version is in the
@ -761,7 +771,9 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* *
* @param referenceSetName * @param referenceSetName
* @param version * @param version
*
* @return true if a matching set is found * @return true if a matching set is found
*
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
@ -928,7 +940,8 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* Add a new reference instance * Add a new reference instance
* *
* @param eamGlobalFileInstance The reference instance to add * @param eamGlobalFileInstance The reference instance to add
* @param correlationType Correlation Type that this Reference Instance is * @param correlationType Correlation Type that this Reference
* Instance is
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -960,7 +973,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* Get all reference entries having a given correlation type and value * Get all reference entries having a given correlation type and value
* *
* @param aType Type to use for matching * @param aType Type to use for matching
* @param aValue Value to use for matching * @param aValue Value to use for matching
* *
* @return List of all global file instances with a type and value * @return List of all global file instances with a type and value
@ -1001,7 +1014,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* artifacts. * artifacts.
* *
* @return List of EamArtifact.Type's. If none are defined in the database, * @return List of EamArtifact.Type's. If none are defined in the database,
* the default list will be returned. * the default list will be returned.
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -1020,7 +1033,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* artifacts. * artifacts.
* *
* @return List of enabled EamArtifact.Type's. If none are defined in the * @return List of enabled EamArtifact.Type's. If none are defined in the
* database, the default list will be returned. * database, the default list will be returned.
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -1039,7 +1052,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* correlate artifacts. * correlate artifacts.
* *
* @return List of supported EamArtifact.Type's. If none are defined in the * @return List of supported EamArtifact.Type's. If none are defined in the
* database, the default list will be returned. * database, the default list will be returned.
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -1111,8 +1124,9 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* (meaning the database is in use). * (meaning the database is in use).
* *
* @return the lock, or null if locking is not supported * @return the lock, or null if locking is not supported
*
* @throws EamDbException if the coordination service is running but we fail * @throws EamDbException if the coordination service is running but we fail
* to get the lock * to get the lock
*/ */
@Override @Override
public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException { public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException {
@ -1156,4 +1170,26 @@ final class SqliteEamDb extends AbstractSqlEamDb {
rwLock.readLock().unlock(); rwLock.readLock().unlock();
} }
@Override
boolean doesColumnExist(Connection conn, String tableName, String columnName) throws SQLException {
final String tableInfoQueryTemplate = "PRAGMA table_info(%s)"; //NON-NLS
ResultSet resultSet = null;
Statement statement = null;
boolean columnExists = false;
try {
statement = conn.createStatement();
resultSet = statement.executeQuery(String.format(tableInfoQueryTemplate, tableName));
while (resultSet.next()) {
// the second value ( 2 ) is the column name
if (resultSet.getString(2).equals(columnName)) {
columnExists = true;
break;
}
}
} finally {
EamDbUtil.closeResultSet(resultSet);
EamDbUtil.closeStatement(statement);
}
return columnExists;
}
} }

View File

@ -35,8 +35,9 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/** /**
* Settings for the sqlite implementation of the Central Repository database * Settings for the sqlite implementation of the Central Repository database
* *
* NOTE: This is public scope because the options panel calls it directly to set/get * NOTE: This is public scope because the options panel calls it directly to
* set/get
*/ */
public final class SqliteEamDbSettings { public final class SqliteEamDbSettings {
@ -95,7 +96,7 @@ public final class SqliteEamDbSettings {
ModuleSettings.setConfigSetting("CentralRepository", "db.sqlite.dbDirectory", getDbDirectory()); // NON-NLS ModuleSettings.setConfigSetting("CentralRepository", "db.sqlite.dbDirectory", getDbDirectory()); // NON-NLS
ModuleSettings.setConfigSetting("CentralRepository", "db.sqlite.bulkThreshold", Integer.toString(getBulkThreshold())); // NON-NLS ModuleSettings.setConfigSetting("CentralRepository", "db.sqlite.bulkThreshold", Integer.toString(getBulkThreshold())); // NON-NLS
} }
/** /**
* Verify that the db file exists. * Verify that the db file exists.
* *
@ -103,11 +104,11 @@ public final class SqliteEamDbSettings {
*/ */
public boolean dbFileExists() { public boolean dbFileExists() {
File dbFile = new File(getFileNameWithPath()); File dbFile = new File(getFileNameWithPath());
if(! dbFile.exists()){ if (!dbFile.exists()) {
return false; return false;
} }
// It's unlikely, but make sure the file isn't actually a directory // It's unlikely, but make sure the file isn't actually a directory
return ( ! dbFile.isDirectory()); return (!dbFile.isDirectory());
} }
/** /**
@ -148,10 +149,11 @@ public final class SqliteEamDbSettings {
return true; return true;
} }
/** /**
* Delete the database * Delete the database
* @return *
* @return
*/ */
public boolean deleteDatabase() { public boolean deleteDatabase() {
File dbFile = new File(this.getFileNameWithPath()); File dbFile = new File(this.getFileNameWithPath());
@ -333,27 +335,14 @@ public final class SqliteEamDbSettings {
createCorrelationTypesTable.append("CONSTRAINT correlation_types_names UNIQUE (display_name, db_table_name)"); createCorrelationTypesTable.append("CONSTRAINT correlation_types_names UNIQUE (display_name, db_table_name)");
createCorrelationTypesTable.append(")"); createCorrelationTypesTable.append(")");
// Each "%s" will be replaced with the relevant TYPE_instances table name. String createArtifactInstancesTableTemplate = getCreateArtifactInstancesTableTemplate();
StringBuilder createArtifactInstancesTableTemplate = new StringBuilder();
createArtifactInstancesTableTemplate.append("CREATE TABLE IF NOT EXISTS %s (");
createArtifactInstancesTableTemplate.append("id integer primary key autoincrement NOT NULL,");
createArtifactInstancesTableTemplate.append("case_id integer NOT NULL,");
createArtifactInstancesTableTemplate.append("data_source_id integer NOT NULL,");
createArtifactInstancesTableTemplate.append("value text NOT NULL,");
createArtifactInstancesTableTemplate.append("file_path text NOT NULL,");
createArtifactInstancesTableTemplate.append("known_status integer NOT NULL,");
createArtifactInstancesTableTemplate.append("comment text,");
createArtifactInstancesTableTemplate.append("CONSTRAINT %s_multi_unique UNIQUE(data_source_id, value, file_path) ON CONFLICT IGNORE,");
createArtifactInstancesTableTemplate.append("foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,");
createArtifactInstancesTableTemplate.append("foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL");
createArtifactInstancesTableTemplate.append(")");
// Each "%s" will be replaced with the relevant TYPE_instances table name.
String instancesIdx1 = "CREATE INDEX IF NOT EXISTS %s_case_id ON %s (case_id)";
String instancesIdx2 = "CREATE INDEX IF NOT EXISTS %s_data_source_id ON %s (data_source_id)";
String instancesIdx3 = "CREATE INDEX IF NOT EXISTS %s_value ON %s (value)";
String instancesIdx4 = "CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)";
String instancesCaseIdIdx = getAddCaseIdIndexTemplate();
String instancesDatasourceIdIdx = getAddDataSourceIdIndexTemplate();
String instancesValueIdx = getAddValueIndexTemplate();
String instancesKnownStatusIdx = getAddKnownStatusIndexTemplate();
String instancesObjectIdIdx = getAddObjectIdIndexTemplate();
StringBuilder createDbInfoTable = new StringBuilder(); StringBuilder createDbInfoTable = new StringBuilder();
createDbInfoTable.append("CREATE TABLE IF NOT EXISTS db_info ("); createDbInfoTable.append("CREATE TABLE IF NOT EXISTS db_info (");
createDbInfoTable.append("id integer primary key NOT NULL,"); createDbInfoTable.append("id integer primary key NOT NULL,");
@ -402,11 +391,12 @@ public final class SqliteEamDbSettings {
reference_type_dbname = EamDbUtil.correlationTypeToReferenceTableName(type); reference_type_dbname = EamDbUtil.correlationTypeToReferenceTableName(type);
instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type); instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type);
stmt.execute(String.format(createArtifactInstancesTableTemplate.toString(), instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(createArtifactInstancesTableTemplate, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesIdx1, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesCaseIdIdx, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesIdx2, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesDatasourceIdIdx, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesIdx3, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesValueIdx, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesIdx4, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesKnownStatusIdx, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesObjectIdIdx, instance_type_dbname, instance_type_dbname));
// FUTURE: allow more than the FILES type // FUTURE: allow more than the FILES type
if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) { if (type.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
@ -426,6 +416,97 @@ public final class SqliteEamDbSettings {
} }
return true; return true;
} }
/**
* Get the template String for creating a new _instances table in a Sqlite
* central repository. %s will exist in the template where the name of the
* new table will be addedd.
*
* @return a String which is a template for cretating a new _instances table
*/
static String getCreateArtifactInstancesTableTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
StringBuilder createArtifactInstancesTableTemplate = new StringBuilder();
createArtifactInstancesTableTemplate.append("CREATE TABLE IF NOT EXISTS %s (");
createArtifactInstancesTableTemplate.append("id integer primary key autoincrement NOT NULL,");
createArtifactInstancesTableTemplate.append("case_id integer NOT NULL,");
createArtifactInstancesTableTemplate.append("data_source_id integer NOT NULL,");
createArtifactInstancesTableTemplate.append("value text NOT NULL,");
createArtifactInstancesTableTemplate.append("file_path text NOT NULL,");
createArtifactInstancesTableTemplate.append("known_status integer NOT NULL,");
createArtifactInstancesTableTemplate.append("comment text,");
createArtifactInstancesTableTemplate.append("file_obj_id integer,");
createArtifactInstancesTableTemplate.append("CONSTRAINT %s_multi_unique UNIQUE(data_source_id, value, file_path) ON CONFLICT IGNORE,");
createArtifactInstancesTableTemplate.append("foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,");
createArtifactInstancesTableTemplate.append("foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL");
createArtifactInstancesTableTemplate.append(")");
return createArtifactInstancesTableTemplate.toString();
}
/**
* Get the template for creating an index on the case_id column of an
* instance table. %s will exist in the template where the name of the new
* table will be addedd.
*
* @return a String which is a template for adding an index to the case_id
* column of a _instances table
*/
static String getAddCaseIdIndexTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "CREATE INDEX IF NOT EXISTS %s_case_id ON %s (case_id)";
}
/**
* Get the template for creating an index on the data_source_id column of an
* instance table. %s will exist in the template where the name of the new
* table will be addedd.
*
* @return a String which is a template for adding an index to the
* data_source_id column of a _instances table
*/
static String getAddDataSourceIdIndexTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "CREATE INDEX IF NOT EXISTS %s_data_source_id ON %s (data_source_id)";
}
/**
* Get the template for creating an index on the value column of an instance
* table. %s will exist in the template where the name of the new table will
* be addedd.
*
* @return a String which is a template for adding an index to the value
* column of a _instances table
*/
static String getAddValueIndexTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "CREATE INDEX IF NOT EXISTS %s_value ON %s (value)";
}
/**
* Get the template for creating an index on the known_status column of an
* instance table. %s will exist in the template where the name of the new
* table will be addedd.
*
* @return a String which is a template for adding an index to the
* known_status column of a _instances table
*/
static String getAddKnownStatusIndexTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)";
}
/**
* Get the template for creating an index on the file_obj_id column of an
* instance table. %s will exist in the template where the name of the new
* table will be addedd.
*
* @return a String which is a template for adding an index to the file_obj_id
* column of a _instances table
*/
static String getAddObjectIdIndexTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "CREATE INDEX IF NOT EXISTS %s_file_obj_id ON %s (file_obj_id)";
}
public boolean insertDefaultDatabaseContent() { public boolean insertDefaultDatabaseContent() {
Connection conn = getEphemeralConnection(); Connection conn = getEphemeralConnection();
@ -490,8 +571,6 @@ public final class SqliteEamDbSettings {
} }
} }
/** /**
* @return the dbDirectory * @return the dbDirectory
*/ */

View File

@ -183,7 +183,10 @@ public class IngestEventsListener {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if (getCeModuleInstanceCount() > 0) { //if ingest is running we want there to check if there is a Correlation Engine module running
//sometimes artifacts are generated by DSPs or other sources while ingest is not running
//in these cases we still want to create correlation attributes for those artifacts when appropriate
if (!IngestManager.getInstance().isIngestRunning() || getCeModuleInstanceCount() > 0) {
EamDb dbManager; EamDb dbManager;
try { try {
dbManager = EamDb.getInstance(); dbManager = EamDb.getInstance();
@ -193,7 +196,9 @@ public class IngestEventsListener {
} }
switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) { switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) {
case DATA_ADDED: { case DATA_ADDED: {
jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt, isFlagNotableItems())); //if ingest isn't running create the interesting items otherwise use the ingest module setting to determine if we create interesting items
boolean flagNotable = !IngestManager.getInstance().isIngestRunning() || isFlagNotableItems();
jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt, flagNotable));
break; break;
} }
} }

View File

@ -70,7 +70,6 @@ final class IngestModule implements FileIngestModule {
private CorrelationDataSource eamDataSource; private CorrelationDataSource eamDataSource;
private Blackboard blackboard; private Blackboard blackboard;
private CorrelationAttributeInstance.Type filesType; private CorrelationAttributeInstance.Type filesType;
private final boolean flagTaggedNotableItems; private final boolean flagTaggedNotableItems;
/** /**
@ -152,14 +151,14 @@ final class IngestModule implements FileIngestModule {
// insert this file into the central repository // insert this file into the central repository
try { try {
CorrelationAttributeInstance cefi = new CorrelationAttributeInstance( CorrelationAttributeInstance cefi = new CorrelationAttributeInstance(
filesType,
md5, md5,
filesType,
eamCase, eamCase,
eamDataSource, eamDataSource,
abstractFile.getParentPath() + abstractFile.getName(), abstractFile.getParentPath() + abstractFile.getName(),
null, null,
TskData.FileKnown.UNKNOWN // NOTE: Known status in the CR is based on tagging, not hashes like the Case Database. TskData.FileKnown.UNKNOWN // NOTE: Known status in the CR is based on tagging, not hashes like the Case Database.
); , abstractFile.getId());
dbManager.addAttributeInstanceBulk(cefi); dbManager.addAttributeInstanceBulk(cefi);
} catch (EamDbException ex) { } catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS logger.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS

View File

@ -34,7 +34,7 @@ AddNewOrganizationDialog.bnOK.text=OK
AddNewOrganizationDialog.tfName.tooltip=POC Name AddNewOrganizationDialog.tfName.tooltip=POC Name
ManageTagsDialog.okButton.text=OK ManageTagsDialog.okButton.text=OK
ManageTagsDialog.cancelButton.text=Cancel ManageTagsDialog.cancelButton.text=Cancel
ManageArtifactTypesDialog.taInstructionsMsg.text=Enable one or more correlation properties to use for correlation during ingest. Note, these properties are global and impact all users of the central repository. ManageArtifactTypesDialog.taInstructionsMsg.text=Enable one or more correlation properties to use for correlation during ingest. Note, these properties are global and impact all users of the Central Repository.
EamSqliteSettingsDialog.bnOk.text=OK EamSqliteSettingsDialog.bnOk.text=OK
EamPostgresSettingsDialog.bnSave.text=Save EamPostgresSettingsDialog.bnSave.text=Save
EamDbSettingsDialog.bnDatabasePathFileOpen.text=Browse... EamDbSettingsDialog.bnDatabasePathFileOpen.text=Browse...
@ -58,15 +58,14 @@ ManageCorrelationPropertiesDialog.okButton.text=OK
GlobalSettingsPanel.bnManageProperties.text=Manage Correlation Properties GlobalSettingsPanel.bnManageProperties.text=Manage Correlation Properties
EamDbSettingsDialog.lbDatabaseDesc.text=Database File: EamDbSettingsDialog.lbDatabaseDesc.text=Database File:
EamDbSettingsDialog.lbFullDbPath.text= EamDbSettingsDialog.lbFullDbPath.text=
GlobalSettingsPanel.cbUseCentralRepo.text=Use a central repository GlobalSettingsPanel.cbUseCentralRepo.text=Use a Central Repository
GlobalSettingsPanel.correlationPropertiesTextArea.text=Choose which file and result properties to store in the central repository for later correlation.\n GlobalSettingsPanel.organizationTextArea.text=Organization information can be tracked in the Central Repository.
GlobalSettingsPanel.organizationTextArea.text=Organization information can be tracked in the central repository.
GlobalSettingsPanel.manageOrganizationButton.text=Manage Organizations GlobalSettingsPanel.manageOrganizationButton.text=Manage Organizations
GlobalSettingsPanel.lbCentralRepository.text=A central repository allows you to correlate files and results between cases. GlobalSettingsPanel.lbCentralRepository.text=A Central Repository allows you to correlate files and results between cases.
GlobalSettingsPanel.pnCorrelationProperties.border.title=Correlation Properties GlobalSettingsPanel.pnCorrelationProperties.border.title=Correlation Properties
GlobalSettingsPanel.organizationPanel.border.title=Organizations GlobalSettingsPanel.organizationPanel.border.title=Organizations
GlobalSettingsPanel.casesPanel.border.title=Case Details GlobalSettingsPanel.casesPanel.border.title=Case Details
GlobalSettingsPanel.showCasesButton.text=Show Cases GlobalSettingsPanel.showCasesButton.text=Manage Cases
ShowCasesDialog.closeButton.AccessibleContext.accessibleName=Close ShowCasesDialog.closeButton.AccessibleContext.accessibleName=Close
ShowCasesDialog.closeButton.actionCommand=Close ShowCasesDialog.closeButton.actionCommand=Close
ShowCasesDialog.closeButton.text=Close ShowCasesDialog.closeButton.text=Close
@ -74,5 +73,15 @@ ShowCasesDialog.caseDetailsTable.toolTipText=Click column name to sort. Right-cl
ShowCasesDialog.title=Case Details ShowCasesDialog.title=Case Details
GlobalSettingsPanel.Case\ Details.AccessibleContext.accessibleName=Cases Details GlobalSettingsPanel.Case\ Details.AccessibleContext.accessibleName=Cases Details
ShowCasesDialog.caseDetailsTable.AccessibleContext.accessibleDescription=Click column name to sort. ShowCasesDialog.caseDetailsTable.AccessibleContext.accessibleDescription=Click column name to sort.
GlobalSettingsPanel.casesTextArea.text=Display table that lists central repository case details. GlobalSettingsPanel.casesTextArea.text=Display table that lists Central Repository case details.
GlobalSettingsPanel.ingestRunningWarningLabel.text=Cannot make changes to central repository settings when ingest is running! GlobalSettingsPanel.ingestRunningWarningLabel.text=Cannot make changes to Central Repository settings when ingest is running!
GlobalSettingsPanel.correlationPropertiesTextArea.text=Choose which file and result properties to store in the Central Repository for later correlation.\n
ManageCasesDialog.examinerPhoneLabel.text=Examiner Phone:
ManageCasesDialog.examinerNameLabel.text=Examiner Name:
ManageCasesDialog.examinerEmailLabel.text=Examiner Email:
ManageCasesDialog.caseNumberLabel.text=Case Number:
ManageCasesDialog.orgLabel.text=Organization:
ManageCasesDialog.closeButton.text=Close
ManageCasesDialog.notesLabel.text=Notes:
ManageCasesDialog.dataSourcesLabel.text=Data Sources:
ManageCasesDialog.caseInfoLabel.text=Case Info:

View File

@ -0,0 +1,131 @@
/*
* Central Repository
*
* Copyright 2018 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.centralrepository.optionspanel;
import java.util.Collections;
import java.util.List;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization;
/**
* An object to contain both a CorrelationCase and the list of
* CorrelationDataSources which are associated with that case.
*/
class CaseDataSourcesWrapper {
private final CorrelationCase eamCase;
private final List<CorrelationDataSource> dataSources;
/**
* Create a new CaseDataSourcesWrapper object.
*
* @param correlationCase - the CorrelationCase which is being represented
* @param dataSourceList - the list of CorrelationDataSource objects which
* are associated with the CorrelationCase
*/
CaseDataSourcesWrapper(CorrelationCase correlationCase, List<CorrelationDataSource> dataSourceList) {
eamCase = correlationCase;
dataSources = dataSourceList;
}
/**
* Get the display name of the CorrelationCase.
*
* @return the display name of the CorrelationCase.
*/
String getDisplayName() {
return eamCase.getDisplayName();
}
/**
* Get the list of CorrelationDataSources associated with the
* CorrelationCase.
*
* @return the list of CorrelationDataSources associated with the
* CorrelationCase.
*/
List<CorrelationDataSource> getDataSources() {
return Collections.unmodifiableList(dataSources);
}
/**
* Get the creation date of the CorrelationCase.
*
* @return the creation date of the CorrelationCase.
*/
String getCreationDate() {
return eamCase.getCreationDate();
}
/**
* Get the organization name of the CorrelationCase.
*
* @return the organization name of the CorrelationCase.
*/
String getOrganizationName() {
EamOrganization org = eamCase.getOrg();
return org == null ? "" : org.getName();
}
/**
* Get the case number of the CorrelationCase.
*
* @return the case number of the CorrelationCase.
*/
String getCaseNumber() {
return eamCase.getCaseNumber();
}
/**
* Get the examiner name of the CorrelationCase.
*
* @return the examiner name of the CorrelationCase.
*/
String getExaminerName() {
return eamCase.getExaminerName();
}
/**
* Get the examiner email of the CorrelationCase.
*
* @return the examiner email of the CorrelationCase.
*/
String getExaminerEmail() {
return eamCase.getExaminerEmail();
}
/**
* Get the notes of the CorrelationCase.
*
* @return the notes of the CorrelationCase.
*/
String getNotes() {
return eamCase.getNotes();
}
/**
* Get the examiner phone number of the CorrelationCase.
*
* @return the examiner phone number of the CorrelationCase.
*/
String getExaminerPhone() {
return eamCase.getExaminerPhone();
}
}

View File

@ -0,0 +1,156 @@
/*
* Central Repository
*
* Copyright 2018 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.centralrepository.optionspanel;
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
/**
* Model for cells to display correlation case information
*/
class CasesTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
/**
* list of Eam Cases from central repository.
*/
private final List<CaseDataSourcesWrapper> eamCases;
/**
* Model for cells to display correlation case information
*/
CasesTableModel() {
eamCases = new ArrayList<>();
}
@Override
public int getColumnCount() {
return CaseTableColumns.values().length;
}
@Override
public int getRowCount() {
return eamCases.size();
}
@Override
public String getColumnName(int colIdx) {
return CaseTableColumns.values()[colIdx].columnName();
}
@Override
public Object getValueAt(int rowIdx, int colIdx) {
if (eamCases.isEmpty()) {
return Bundle.CasesTableModel_noData();
}
return mapValueById(rowIdx, CaseTableColumns.values()[colIdx]);
}
/**
* Map a rowIdx and colId to the value in that cell.
*
* @param rowIdx Index of row to search
* @param colId ID of column to search
*
* @return value in the cell
*/
@Messages({"CasesTableModel.noData=No Cases"})
private Object mapValueById(int rowIdx, CaseTableColumns colId) {
CaseDataSourcesWrapper eamCase = eamCases.get(rowIdx);
String value = Bundle.CasesTableModel_noData();
switch (colId) {
case CASE_NAME:
value = eamCase.getDisplayName();
break;
case CREATION_DATE:
value = eamCase.getCreationDate();
break;
default:
break;
}
return value;
}
@Override
public Class<String> getColumnClass(int colIdx) {
return String.class;
}
/**
* Add one local central repository case to the table.
*
* @param eamCase central repository case to add to the table
*/
void addEamCase(CorrelationCase eamCase, List<CorrelationDataSource> dataSourceList) {
eamCases.add(new CaseDataSourcesWrapper(eamCase, dataSourceList));
fireTableDataChanged();
}
/**
* Get the CaseDataSourcesWrapper for the specified index in the table.
*
* @param listIndex the idex of the object to get
*
* @return A caseDataSourcesWrapper containing the CorrelationCase and the
* CorrelationDataSources associated with it for the specified
* index.
*/
CaseDataSourcesWrapper getEamCase(int listIndex) {
return eamCases.get(listIndex);
}
/**
* Enum which lists columns of interest from CorrelationCase.
*/
@Messages({"CasesTableModel.case=Case Name",
"CasesTableModel.creationDate=Creation Date"})
private enum CaseTableColumns {
// Ordering here determines displayed column order in Content Viewer.
// If order is changed, update the CellRenderer to ensure correct row coloring.
CASE_NAME(Bundle.CasesTableModel_case()),
CREATION_DATE(Bundle.CasesTableModel_creationDate());
private final String columnName;
/**
* Make a DataSourceTableColumns enum item.
*
* @param columnName the name of the column.s
*/
CaseTableColumns(String columnName) {
this.columnName = columnName;
}
/**
* The name displayed in the column header.
*
* @return the name of the column.
*/
String columnName() {
return columnName;
}
}
}

View File

@ -0,0 +1,152 @@
/*
* Central Repository
*
* Copyright 2018 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.centralrepository.optionspanel;
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
/**
* Model for cells to display correlation data source information
*/
class DataSourcesTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
/**
* A list of correlation data sources from central repository.
*/
private final List<CorrelationDataSource> dataSources;
/**
* Create a new DataSourcesTableModel, with an initially empty list of data
* sources.
*/
DataSourcesTableModel() {
dataSources = new ArrayList<>();
}
@Override
public int getColumnCount() {
return DataSourcesTableColumns.values().length;
}
@Override
public int getRowCount() {
return dataSources.size();
}
@Override
public String getColumnName(int colIdx) {
return DataSourcesTableColumns.values()[colIdx].columnName();
}
@Override
public Object getValueAt(int rowIdx, int colIdx) {
if (dataSources.isEmpty()) {
return Bundle.DataSourcesTableModel_noData();
}
return mapValueById(rowIdx, DataSourcesTableColumns.values()[colIdx]);
}
/**
* Map a rowIdx and colId to the value in that cell.
*
* @param rowIdx Index of row to search
* @param colId ID of column to search
*
* @return value in the cell
*/
@Messages({"DataSourcesTableModel.noData=No Data Sources"})
private Object mapValueById(int rowIdx, DataSourcesTableColumns colId) {
CorrelationDataSource dataSource = dataSources.get(rowIdx);
String value = Bundle.DataSourcesTableModel_noData();
switch (colId) {
case DATA_SOURCE:
value = dataSource.getName();
break;
case DEVICE_ID:
value = dataSource.getDeviceID();
break;
default:
break;
}
return value;
}
@Override
public Class<String> getColumnClass(int colIdx) {
return String.class;
}
/**
* Add a list of datasources to the table.
*
* @param dataSourceList the list of datasources to add to the table
*/
void addDataSources(List<CorrelationDataSource> dataSourceList) {
dataSources.addAll(dataSourceList);
fireTableDataChanged();
}
/**
* Clear the data sources currently included in the model.
*/
void clearTable() {
dataSources.clear();
fireTableDataChanged();
}
@Messages({"DataSourcesTableModel.dataSource=Data Source Name",
"DataSourcesTableModel.deviceId=Device ID"})
/**
* Enum which lists columns of interest from CorrelationDataSource.
*/
private enum DataSourcesTableColumns {
// Ordering here determines displayed column order in Content Viewer.
// If order is changed, update the CellRenderer to ensure correct row coloring.
DATA_SOURCE(Bundle.DataSourcesTableModel_dataSource()),
DEVICE_ID(Bundle.DataSourcesTableModel_deviceId());
private final String columnName;
/**
* Make a CasesTableColumns enum item.
*
* @param columnName the name of the column.
*/
DataSourcesTableColumns(String columnName) {
this.columnName = columnName;
}
/**
* The name displayed in the column header.
*
* @return the name of the column.
*/
String columnName() {
return columnName;
}
}
}

View File

@ -102,7 +102,7 @@ public class EamDbSettingsDialog extends JDialog {
@Override @Override
public String getDescription() { public String getDescription() {
return "Directories and central repository databases"; return "Directories and Central Repository databases";
} }
}); });
cbDatabaseType.setSelectedItem(selectedPlatform); cbDatabaseType.setSelectedItem(selectedPlatform);

View File

@ -57,7 +57,7 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="lbCentralRepository" pref="1022" max="32767" attributes="0"/> <Component id="lbCentralRepository" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="pnDatabaseConfiguration" alignment="0" max="32767" attributes="0"/> <Component id="pnDatabaseConfiguration" alignment="0" max="32767" attributes="0"/>
@ -67,7 +67,7 @@
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Component id="cbUseCentralRepo" min="-2" pref="162" max="-2" attributes="0"/> <Component id="cbUseCentralRepo" min="-2" pref="162" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="ingestRunningWarningLabel" max="32767" attributes="0"/> <Component id="ingestRunningWarningLabel" pref="844" max="32767" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
@ -89,13 +89,13 @@
</Group> </Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="pnDatabaseConfiguration" min="-2" max="-2" attributes="0"/> <Component id="pnDatabaseConfiguration" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="pnCorrelationProperties" min="-2" max="-2" attributes="0"/> <Component id="pnCorrelationProperties" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="organizationPanel" min="-2" max="-2" attributes="0"/> <Component id="organizationPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="casesPanel" min="-2" max="-2" attributes="0"/> <Component id="casesPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="tbOops" min="-2" max="-2" attributes="0"/> <Component id="tbOops" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -242,14 +242,14 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="correlationPropertiesScrollPane" max="32767" attributes="0"/> <Group type="102" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="bnManageTypes" min="-2" max="-2" attributes="0"/> <Component id="bnManageTypes" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
<Component id="correlationPropertiesScrollPane" max="32767" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -258,8 +258,7 @@
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="7" max="-2" attributes="0"/> <Component id="correlationPropertiesScrollPane" pref="32" max="32767" attributes="0"/>
<Component id="correlationPropertiesScrollPane" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="bnManageTypes" min="-2" max="-2" attributes="0"/> <Component id="bnManageTypes" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/> <EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
@ -301,7 +300,7 @@
<Font name="Tahoma" size="11" style="0"/> <Font name="Tahoma" size="11" style="0"/>
</Property> </Property>
<Property name="lineWrap" type="boolean" value="true"/> <Property name="lineWrap" type="boolean" value="true"/>
<Property name="rows" type="int" value="2"/> <Property name="rows" type="int" value="1"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="GlobalSettingsPanel.correlationPropertiesTextArea.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="GlobalSettingsPanel.correlationPropertiesTextArea.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
@ -334,7 +333,7 @@
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="organizationScrollPane" pref="992" max="32767" attributes="0"/> <Component id="organizationScrollPane" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Component id="manageOrganizationButton" min="-2" max="-2" attributes="0"/> <Component id="manageOrganizationButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>

View File

@ -222,7 +222,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
correlationPropertiesTextArea.setColumns(20); correlationPropertiesTextArea.setColumns(20);
correlationPropertiesTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N correlationPropertiesTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N
correlationPropertiesTextArea.setLineWrap(true); correlationPropertiesTextArea.setLineWrap(true);
correlationPropertiesTextArea.setRows(2); correlationPropertiesTextArea.setRows(1);
correlationPropertiesTextArea.setText(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.correlationPropertiesTextArea.text")); // NOI18N correlationPropertiesTextArea.setText(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.correlationPropertiesTextArea.text")); // NOI18N
correlationPropertiesTextArea.setToolTipText(""); correlationPropertiesTextArea.setToolTipText("");
correlationPropertiesTextArea.setWrapStyleWord(true); correlationPropertiesTextArea.setWrapStyleWord(true);
@ -236,17 +236,16 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
.addGroup(pnCorrelationPropertiesLayout.createSequentialGroup() .addGroup(pnCorrelationPropertiesLayout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(pnCorrelationPropertiesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(pnCorrelationPropertiesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(correlationPropertiesScrollPane)
.addGroup(pnCorrelationPropertiesLayout.createSequentialGroup() .addGroup(pnCorrelationPropertiesLayout.createSequentialGroup()
.addComponent(bnManageTypes) .addComponent(bnManageTypes)
.addGap(0, 0, Short.MAX_VALUE))) .addGap(0, 0, Short.MAX_VALUE))
.addComponent(correlationPropertiesScrollPane))
.addContainerGap()) .addContainerGap())
); );
pnCorrelationPropertiesLayout.setVerticalGroup( pnCorrelationPropertiesLayout.setVerticalGroup(
pnCorrelationPropertiesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) pnCorrelationPropertiesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnCorrelationPropertiesLayout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnCorrelationPropertiesLayout.createSequentialGroup()
.addGap(7, 7, 7) .addComponent(correlationPropertiesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 32, Short.MAX_VALUE)
.addComponent(correlationPropertiesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(bnManageTypes) .addComponent(bnManageTypes)
.addGap(8, 8, 8)) .addGap(8, 8, 8))
@ -281,7 +280,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
.addGroup(organizationPanelLayout.createSequentialGroup() .addGroup(organizationPanelLayout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(organizationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(organizationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(organizationScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 992, Short.MAX_VALUE) .addComponent(organizationScrollPane)
.addGroup(organizationPanelLayout.createSequentialGroup() .addGroup(organizationPanelLayout.createSequentialGroup()
.addComponent(manageOrganizationButton) .addComponent(manageOrganizationButton)
.addGap(0, 0, Short.MAX_VALUE))) .addGap(0, 0, Short.MAX_VALUE)))
@ -356,7 +355,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
jPanel1.setLayout(jPanel1Layout); jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup( jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lbCentralRepository, javax.swing.GroupLayout.DEFAULT_SIZE, 1022, Short.MAX_VALUE) .addComponent(lbCentralRepository, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
@ -366,7 +365,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
.addGroup(jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(cbUseCentralRepo, javax.swing.GroupLayout.PREFERRED_SIZE, 162, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(cbUseCentralRepo, javax.swing.GroupLayout.PREFERRED_SIZE, 162, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(ingestRunningWarningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addComponent(ingestRunningWarningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 844, Short.MAX_VALUE))
.addGroup(jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, 974, javax.swing.GroupLayout.PREFERRED_SIZE))) .addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, 974, javax.swing.GroupLayout.PREFERRED_SIZE)))
@ -382,13 +381,13 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
.addComponent(ingestRunningWarningLabel)) .addComponent(ingestRunningWarningLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGap(0, 0, 0)
.addComponent(pnCorrelationProperties, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGap(0, 0, 0)
.addComponent(organizationPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(organizationPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGap(0, 0, 0)
.addComponent(casesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(casesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGap(0, 0, 0)
.addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap()) .addContainerGap())
); );
@ -441,7 +440,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
private void showCasesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showCasesButtonActionPerformed private void showCasesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showCasesButtonActionPerformed
store(); store();
ShowCasesDialog showCasesDialog = new ShowCasesDialog(); ManageCasesDialog.displayManageCasesDialog();
}//GEN-LAST:event_showCasesButtonActionPerformed }//GEN-LAST:event_showCasesButtonActionPerformed
@Override @Override

View File

@ -0,0 +1,358 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="2"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 400]"/>
</Property>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="casesSplitPane" alignment="1" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="casesSplitPane" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="casesSplitPane">
<Properties>
<Property name="dividerLocation" type="int" value="380"/>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="caseInfoPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Component id="dataSourcesScrollPane" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="1" attributes="0">
<Component id="orgLabel" min="-2" pref="88" max="-2" attributes="0"/>
<Component id="caseNumberLabel" min="-2" pref="88" max="-2" attributes="0"/>
<Component id="examinerNameLabel" alignment="1" min="-2" pref="88" max="-2" attributes="0"/>
<Component id="examinerEmailLabel" alignment="1" min="-2" pref="88" max="-2" attributes="0"/>
<Component id="examinerPhoneLabel" alignment="1" min="-2" pref="88" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="caseNumberValueLabel" max="32767" attributes="0"/>
<Component id="orgValueLabel" alignment="0" max="32767" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="examinerNameValueLabel" max="32767" attributes="0"/>
<Component id="examinerEmailValueLabel" max="32767" attributes="0"/>
<Component id="examinerPhoneValueLabel" alignment="0" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<Component id="notesLabel" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Component id="notesScrollPane" pref="428" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="closeButton" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="caseInfoLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="dataSourcesLabel" alignment="0" min="-2" pref="77" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="caseInfoLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="orgLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="caseNumberLabel" max="32767" attributes="0"/>
<Component id="caseNumberValueLabel" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="examinerNameLabel" max="32767" attributes="0"/>
<Component id="examinerNameValueLabel" max="32767" attributes="0"/>
</Group>
</Group>
<Component id="orgValueLabel" alignment="0" min="-2" pref="14" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="examinerEmailLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="examinerEmailValueLabel" min="-2" pref="14" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="examinerPhoneLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="examinerPhoneValueLabel" alignment="0" min="-2" pref="14" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="notesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="notesScrollPane" pref="55" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="dataSourcesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="dataSourcesScrollPane" pref="129" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="closeButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="dataSourcesScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="dataSourcesTable">
<Properties>
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="dataSourcesTableModel" type="code"/>
</Property>
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
<JTableSelectionModel selectionMode="0"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_SerializeTo" type="java.lang.String" value="CaseManagerDialog_dataSourcesTable"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="notesScrollPane">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextArea" name="notesTextArea">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="f0" green="f0" red="f0" type="rgb"/>
</Property>
<Property name="columns" type="int" value="20"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="11" style="0"/>
</Property>
<Property name="lineWrap" type="boolean" value="true"/>
<Property name="rows" type="int" value="3"/>
<Property name="wrapStyleWord" type="boolean" value="true"/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="caseInfoLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageCasesDialog.caseInfoLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="dataSourcesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageCasesDialog.dataSourcesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="notesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageCasesDialog.notesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="orgLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageCasesDialog.orgLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="caseNumberLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageCasesDialog.caseNumberLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="examinerEmailLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageCasesDialog.examinerEmailLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="examinerNameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageCasesDialog.examinerNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="examinerPhoneLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageCasesDialog.examinerPhoneLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="orgValueLabel">
</Component>
<Component class="javax.swing.JLabel" name="caseNumberValueLabel">
</Component>
<Component class="javax.swing.JLabel" name="examinerNameValueLabel">
</Component>
<Component class="javax.swing.JLabel" name="examinerEmailValueLabel">
</Component>
<Component class="javax.swing.JLabel" name="examinerPhoneValueLabel">
</Component>
<Component class="javax.swing.JButton" name="closeButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ManageCasesDialog.closeButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[65, 23]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[65, 23]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[65, 23]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="closeButtonActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="casesPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="casesScrollPane" pref="379" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="casesScrollPane" pref="361" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="40" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="casesScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="casesTable">
<Properties>
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="casesTableModel" type="code"/>
</Property>
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
<JTableSelectionModel selectionMode="0"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,353 @@
/*
* Central Repository
*
* Copyright 2018 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.centralrepository.optionspanel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.openide.util.NbBundle.Messages;
/**
* A dialog which displays cases existing in the central repository and the
* central repo information associated with them.
*
*/
final class ManageCasesDialog extends javax.swing.JDialog {
private static final long serialVersionUID = 1L;
private final CasesTableModel casesTableModel = new CasesTableModel();
private final DataSourcesTableModel dataSourcesTableModel = new DataSourcesTableModel();
private final static Logger logger = Logger.getLogger(ManageCasesDialog.class.getName());
/**
* Creates new form ManageCasesDialog
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
@Messages({"ManageCasesDialog.title.text=Manage Cases"})
private ManageCasesDialog() {
super(WindowManager.getDefault().getMainWindow(), Bundle.ManageCasesDialog_title_text(),
true);
initComponents();
try {
EamDb dbManager = EamDb.getInstance();
Map<Integer, List<CorrelationDataSource>> dataSourcesByCaseId = new HashMap<>();
for (CorrelationDataSource dataSource : dbManager.getDataSources()) {
int caseID = dataSource.getCaseID();
List<CorrelationDataSource> dataSourceNames = dataSourcesByCaseId.getOrDefault(caseID, new ArrayList<>());
dataSourceNames.add(dataSource);
dataSourcesByCaseId.put(caseID, dataSourceNames);
}
for (CorrelationCase eamCase : dbManager.getCases()) {
casesTableModel.addEamCase(eamCase, dataSourcesByCaseId.getOrDefault(eamCase.getID(), new ArrayList<>()));
}
} catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS
}
casesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
updateSelection();
}
}
});
//sort on first column by default
casesTable.getRowSorter().toggleSortOrder(0);
}
/**
* Create and display the Manage Cases dialog for the currently enabled
* central repository.
*/
static void displayManageCasesDialog() {
ManageCasesDialog caseInfoDialog = new ManageCasesDialog();
caseInfoDialog.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
caseInfoDialog.setVisible(true);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
casesSplitPane = new javax.swing.JSplitPane();
caseInfoPanel = new javax.swing.JPanel();
dataSourcesScrollPane = new javax.swing.JScrollPane();
dataSourcesTable = new javax.swing.JTable();
notesScrollPane = new javax.swing.JScrollPane();
notesTextArea = new javax.swing.JTextArea();
caseInfoLabel = new javax.swing.JLabel();
dataSourcesLabel = new javax.swing.JLabel();
notesLabel = new javax.swing.JLabel();
orgLabel = new javax.swing.JLabel();
caseNumberLabel = new javax.swing.JLabel();
examinerEmailLabel = new javax.swing.JLabel();
examinerNameLabel = new javax.swing.JLabel();
examinerPhoneLabel = new javax.swing.JLabel();
orgValueLabel = new javax.swing.JLabel();
caseNumberValueLabel = new javax.swing.JLabel();
examinerNameValueLabel = new javax.swing.JLabel();
examinerEmailValueLabel = new javax.swing.JLabel();
examinerPhoneValueLabel = new javax.swing.JLabel();
closeButton = new javax.swing.JButton();
casesPanel = new javax.swing.JPanel();
casesScrollPane = new javax.swing.JScrollPane();
casesTable = new javax.swing.JTable();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setMinimumSize(new java.awt.Dimension(400, 400));
casesSplitPane.setDividerLocation(380);
dataSourcesTable.setAutoCreateRowSorter(true);
dataSourcesTable.setModel(dataSourcesTableModel);
dataSourcesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
dataSourcesScrollPane.setViewportView(dataSourcesTable);
notesScrollPane.setBorder(null);
notesTextArea.setEditable(false);
notesTextArea.setBackground(new java.awt.Color(240, 240, 240));
notesTextArea.setColumns(20);
notesTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N
notesTextArea.setLineWrap(true);
notesTextArea.setRows(3);
notesTextArea.setWrapStyleWord(true);
notesTextArea.setBorder(null);
notesScrollPane.setViewportView(notesTextArea);
org.openide.awt.Mnemonics.setLocalizedText(caseInfoLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.caseInfoLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(dataSourcesLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.dataSourcesLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(notesLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.notesLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(orgLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.orgLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(caseNumberLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.caseNumberLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(examinerEmailLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.examinerEmailLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(examinerNameLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.examinerNameLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(examinerPhoneLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.examinerPhoneLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.closeButton.text")); // NOI18N
closeButton.setMaximumSize(new java.awt.Dimension(65, 23));
closeButton.setMinimumSize(new java.awt.Dimension(65, 23));
closeButton.setPreferredSize(new java.awt.Dimension(65, 23));
closeButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
closeButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout caseInfoPanelLayout = new javax.swing.GroupLayout(caseInfoPanel);
caseInfoPanel.setLayout(caseInfoPanelLayout);
caseInfoPanelLayout.setHorizontalGroup(
caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(caseInfoPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(caseInfoPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addComponent(dataSourcesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
.addGroup(caseInfoPanelLayout.createSequentialGroup()
.addGap(6, 6, 6)
.addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(caseInfoPanelLayout.createSequentialGroup()
.addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(orgLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(caseNumberLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(examinerNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(examinerEmailLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(examinerPhoneLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(caseInfoPanelLayout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(caseNumberValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(orgValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addGroup(caseInfoPanelLayout.createSequentialGroup()
.addGap(6, 6, 6)
.addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(examinerNameValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(examinerEmailValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(examinerPhoneValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))))
.addComponent(notesLabel)
.addGroup(caseInfoPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addComponent(notesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 428, Short.MAX_VALUE))))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, caseInfoPanelLayout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addComponent(closeButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(caseInfoPanelLayout.createSequentialGroup()
.addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(caseInfoLabel)
.addComponent(dataSourcesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())
);
caseInfoPanelLayout.setVerticalGroup(
caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, caseInfoPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(caseInfoLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(caseInfoPanelLayout.createSequentialGroup()
.addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(caseInfoPanelLayout.createSequentialGroup()
.addComponent(orgLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(caseNumberLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(caseNumberValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(examinerNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(examinerNameValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addComponent(orgValueLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(examinerEmailLabel))
.addComponent(examinerEmailValueLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(examinerPhoneLabel)
.addComponent(examinerPhoneValueLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(notesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(notesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 55, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(dataSourcesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(dataSourcesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 129, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(closeButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
casesSplitPane.setRightComponent(caseInfoPanel);
casesTable.setAutoCreateRowSorter(true);
casesTable.setModel(casesTableModel);
casesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
casesScrollPane.setViewportView(casesTable);
javax.swing.GroupLayout casesPanelLayout = new javax.swing.GroupLayout(casesPanel);
casesPanel.setLayout(casesPanelLayout);
casesPanelLayout.setHorizontalGroup(
casesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(casesPanelLayout.createSequentialGroup()
.addComponent(casesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 379, Short.MAX_VALUE)
.addGap(0, 0, 0))
);
casesPanelLayout.setVerticalGroup(
casesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(casesPanelLayout.createSequentialGroup()
.addComponent(casesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 361, Short.MAX_VALUE)
.addGap(40, 40, 40))
);
casesSplitPane.setLeftComponent(casesPanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(casesSplitPane, javax.swing.GroupLayout.Alignment.TRAILING)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(casesSplitPane)
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
this.dispose();
}//GEN-LAST:event_closeButtonActionPerformed
/**
* Update the information displayed to reflect the currently selected case.
*/
private void updateSelection() {
dataSourcesTableModel.clearTable();
if (casesTable.getSelectedRow() >= 0 && casesTable.getSelectedRow() < casesTable.getRowCount()) {
CaseDataSourcesWrapper caseWrapper = casesTableModel.getEamCase(casesTable.convertRowIndexToModel(casesTable.getSelectedRow()));
orgValueLabel.setText(caseWrapper.getOrganizationName());
caseNumberValueLabel.setText(caseWrapper.getCaseNumber());
examinerNameValueLabel.setText(caseWrapper.getExaminerName());
examinerPhoneValueLabel.setText(caseWrapper.getExaminerPhone());
examinerEmailValueLabel.setText(caseWrapper.getExaminerEmail());
notesTextArea.setText(caseWrapper.getNotes());
dataSourcesTableModel.addDataSources(caseWrapper.getDataSources());
} else {
orgValueLabel.setText("");
caseNumberValueLabel.setText("");
examinerNameValueLabel.setText("");
examinerPhoneValueLabel.setText("");
examinerEmailValueLabel.setText("");
notesTextArea.setText("");
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel caseInfoLabel;
private javax.swing.JPanel caseInfoPanel;
private javax.swing.JLabel caseNumberLabel;
private javax.swing.JLabel caseNumberValueLabel;
private javax.swing.JPanel casesPanel;
private javax.swing.JScrollPane casesScrollPane;
private javax.swing.JSplitPane casesSplitPane;
private javax.swing.JTable casesTable;
private javax.swing.JButton closeButton;
private javax.swing.JLabel dataSourcesLabel;
private javax.swing.JScrollPane dataSourcesScrollPane;
private javax.swing.JTable dataSourcesTable;
private javax.swing.JLabel examinerEmailLabel;
private javax.swing.JLabel examinerEmailValueLabel;
private javax.swing.JLabel examinerNameLabel;
private javax.swing.JLabel examinerNameValueLabel;
private javax.swing.JLabel examinerPhoneLabel;
private javax.swing.JLabel examinerPhoneValueLabel;
private javax.swing.JLabel notesLabel;
private javax.swing.JScrollPane notesScrollPane;
private javax.swing.JTextArea notesTextArea;
private javax.swing.JLabel orgLabel;
private javax.swing.JLabel orgValueLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,169 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<Properties>
<Property name="title" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ShowCasesDialog.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[545, 415]"/>
</Property>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="showCasesPanel" pref="1188" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Component id="closeButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="showCasesPanel" pref="473" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="closeButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="showCasesPanel">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[527, 407]"/>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="1188" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="showCasesScrollPane" alignment="0" pref="527" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="473" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="showCasesScrollPane" alignment="0" pref="407" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="showCasesScrollPane">
<Properties>
<Property name="verticalScrollBarPolicy" type="int" value="21"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[535, 415]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="outCasesPane">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="1423" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="innerCaseScrollPane" alignment="0" pref="1423" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="500" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="innerCaseScrollPane" alignment="0" pref="500" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="innerCaseScrollPane">
<Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="caseDetailsTable">
<Properties>
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="tableModel" type="code"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ShowCasesDialog.caseDetailsTable.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
<JTableSelectionModel selectionMode="1"/>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="false" resizingAllowed="true"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ShowCasesDialog.caseDetailsTable.AccessibleContext.accessibleDescription" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="closeButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ShowCasesDialog.closeButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ShowCasesDialog.closeButton.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ShowCasesDialog.closeButton.AccessibleContext.accessibleName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="closeButtonActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -1,190 +0,0 @@
/*
* Central Repository
*
* Copyright 2015-2018 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.centralrepository.optionspanel;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JDialog;
import javax.swing.JFrame;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Dialog to display table of CorrelationCase information from the CR tab of options.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class ShowCasesDialog extends JDialog {
private static final long serialVersionUID = 1L;
private final static Logger logger = Logger.getLogger(ShowCasesDialog.class.getName());
private final ShowCasesTableModel tableModel;
@Messages({"ShowCasesDialog.title_text=All Cases Details"})
/**
* Creates new form ShowCases Panel
*/
ShowCasesDialog() {
super((JFrame) WindowManager.getDefault().getMainWindow(),
Bundle.ShowCasesDialog_title_text(),
true);
tableModel = new ShowCasesTableModel();
initComponents();
try {
EamDb dbManager = EamDb.getInstance();
List<CorrelationCase> eamCases = dbManager.getCases();
for(CorrelationCase eamCase : eamCases) {
tableModel.addEamCase(eamCase);
}
} catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS
}
display();
}
private void display() {
this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
setVisible(true);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
showCasesPanel = new javax.swing.JPanel();
showCasesScrollPane = new javax.swing.JScrollPane();
outCasesPane = new javax.swing.JPanel();
innerCaseScrollPane = new javax.swing.JScrollPane();
caseDetailsTable = new javax.swing.JTable();
closeButton = new javax.swing.JButton();
setTitle(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.title")); // NOI18N
setMinimumSize(new java.awt.Dimension(545, 415));
showCasesPanel.setPreferredSize(new java.awt.Dimension(527, 407));
showCasesScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
showCasesScrollPane.setPreferredSize(new java.awt.Dimension(535, 415));
innerCaseScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
caseDetailsTable.setAutoCreateRowSorter(true);
caseDetailsTable.setModel(tableModel);
caseDetailsTable.setToolTipText(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.caseDetailsTable.toolTipText")); // NOI18N
caseDetailsTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
caseDetailsTable.getTableHeader().setReorderingAllowed(false);
innerCaseScrollPane.setViewportView(caseDetailsTable);
caseDetailsTable.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.caseDetailsTable.AccessibleContext.accessibleDescription")); // NOI18N
javax.swing.GroupLayout outCasesPaneLayout = new javax.swing.GroupLayout(outCasesPane);
outCasesPane.setLayout(outCasesPaneLayout);
outCasesPaneLayout.setHorizontalGroup(
outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 1423, Short.MAX_VALUE)
.addGroup(outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(innerCaseScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1423, Short.MAX_VALUE))
);
outCasesPaneLayout.setVerticalGroup(
outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 500, Short.MAX_VALUE)
.addGroup(outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(innerCaseScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE))
);
showCasesScrollPane.setViewportView(outCasesPane);
javax.swing.GroupLayout showCasesPanelLayout = new javax.swing.GroupLayout(showCasesPanel);
showCasesPanel.setLayout(showCasesPanelLayout);
showCasesPanelLayout.setHorizontalGroup(
showCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 1188, Short.MAX_VALUE)
.addGroup(showCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(showCasesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 527, Short.MAX_VALUE))
);
showCasesPanelLayout.setVerticalGroup(
showCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 473, Short.MAX_VALUE)
.addGroup(showCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(showCasesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 407, Short.MAX_VALUE))
);
org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.closeButton.text")); // NOI18N
closeButton.setActionCommand(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.closeButton.actionCommand")); // NOI18N
closeButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
closeButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6)
.addComponent(showCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 1188, Short.MAX_VALUE)
.addGap(6, 6, 6))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(closeButton)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6)
.addComponent(showCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 473, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(closeButton)
.addContainerGap())
);
closeButton.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.closeButton.AccessibleContext.accessibleName")); // NOI18N
pack();
}// </editor-fold>//GEN-END:initComponents
private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
dispose();
}//GEN-LAST:event_closeButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTable caseDetailsTable;
private javax.swing.JButton closeButton;
private javax.swing.JScrollPane innerCaseScrollPane;
private javax.swing.JPanel outCasesPane;
private javax.swing.JPanel showCasesPanel;
private javax.swing.JScrollPane showCasesScrollPane;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,187 +0,0 @@
/*
* Central Repository
*
* Copyright 2018 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.centralrepository.optionspanel;
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
/**
* Model for cells to display correlation case information
*/
class ShowCasesTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
@Messages({"ShowCasesTableModel.case=Case Name",
"ShowCasesTableModel.creationDate=Creation Date",
"ShowCasesTableModel.caseNumber=Case Number",
"ShowCasesTableModel.examinerName=Examiner Name",
"ShowCasesTableModel.examinerEmail=Examiner Email",
"ShowCasesTableModel.examinerPhone=Examiner Phone",
"ShowCasesTableModel.notes=Notes",
"ShowCasesTableModel.noData=No Cases"})
/**
* Enum which lists columns of interest from CorrelationCase.
*/
enum TableColumns {
// Ordering here determines displayed column order in Content Viewer.
// If order is changed, update the CellRenderer to ensure correct row coloring.
CASE_NAME(Bundle.ShowCasesTableModel_case(), 200),
CREATION_DATE(Bundle.ShowCasesTableModel_creationDate(), 150),
CASE_NUMBER(Bundle.ShowCasesTableModel_caseNumber(), 100),
EXAMINER_NAME(Bundle.ShowCasesTableModel_examinerName(), 200),
EXAMINER_EMAIL(Bundle.ShowCasesTableModel_examinerEmail(), 100),
EXAMINER_PHONE(Bundle.ShowCasesTableModel_examinerPhone(), 100),
NOTES(Bundle.ShowCasesTableModel_notes(), 450);
private final String columnName;
private final int columnWidth;
TableColumns(String columnName, int columnWidth) {
this.columnName = columnName;
this.columnWidth = columnWidth;
}
String columnName() {
return columnName;
}
int columnWidth() {
return columnWidth;
}
};
/**
* list of Eam Cases from central repository.
*/
private List<CorrelationCase> eamCases;
ShowCasesTableModel() {
eamCases = new ArrayList<>();
}
@Override
public int getColumnCount() {
return TableColumns.values().length;
}
/**
* Get the preferred width that has been configured for this column.
*
* A value of 0 means that no preferred width has been defined for this
* column.
*
* @param colIdx Column index
*
* @return preferred column width >= 0
*/
int getColumnPreferredWidth(int colIdx) {
return TableColumns.values()[colIdx].columnWidth();
}
@Override
public int getRowCount() {
return eamCases.size();
}
@Override
public String getColumnName(int colIdx) {
return TableColumns.values()[colIdx].columnName();
}
@Override
public Object getValueAt(int rowIdx, int colIdx) {
if (eamCases.isEmpty()) {
return Bundle.ShowCasesTableModel_noData();
}
return mapValueById(rowIdx, TableColumns.values()[colIdx]);
}
Object getRow(int rowIdx) {
return eamCases.get(rowIdx);
}
/**
* Map a rowIdx and colId to the value in that cell.
*
* @param rowIdx Index of row to search
* @param colId ID of column to search
*
* @return value in the cell
*/
private Object mapValueById(int rowIdx, TableColumns colId) {
CorrelationCase eamCase = eamCases.get(rowIdx);
String value = Bundle.ShowCasesTableModel_noData();
switch (colId) {
case CASE_NAME:
value = eamCase.getDisplayName();
break;
case CREATION_DATE:
value = eamCase.getCreationDate();
break;
case CASE_NUMBER:
value = eamCase.getCaseNumber();
break;
case EXAMINER_NAME:
value = eamCase.getExaminerName();
break;
case EXAMINER_EMAIL:
value = eamCase.getExaminerEmail();
break;
case EXAMINER_PHONE:
value = eamCase.getExaminerPhone();
break;
case NOTES:
value = eamCase.getNotes();
break;
default:
break;
}
return value;
}
@Override
public Class<String> getColumnClass(int colIdx) {
return String.class;
}
/**
* Add one local central repository case to the table.
*
* @param eamCase central repository case to add to the
* table
*/
void addEamCase(CorrelationCase eamCase) {
eamCases.add(eamCase);
fireTableDataChanged();
}
void clearTable() {
eamCases.clear();
fireTableDataChanged();
}
}

View File

@ -18,17 +18,16 @@
*/ */
package org.sleuthkit.autopsy.commonfilesearch; package org.sleuthkit.autopsy.commonfilesearch;
import java.util.List; import java.util.Arrays;
import org.apache.commons.lang3.StringUtils; import java.util.HashSet;
import java.util.Set;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.ContentTag;
/** /**
* Node that wraps CaseDBCommonAttributeInstance to represent a file instance * Node that wraps CaseDBCommonAttributeInstance to represent a file instance
@ -75,33 +74,25 @@ public class CaseDBCommonAttributeInstanceNode extends FileNode {
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
Sheet sheet = new Sheet(); Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) { Set<String> keepProps = new HashSet<>(Arrays.asList(
sheetSet = Sheet.createPropertiesSet(); NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"),
sheet.put(sheetSet); NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType")));
for(Property<?> p : sheetSet.getProperties()) {
if(!keepProps.contains(p.getName())){
sheetSet.remove(p.getName());
}
} }
List<ContentTag> tags = getContentTagsFromDatabase();
final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText(); final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText();
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, this.getContent().getName()));
addScoreProperty(sheetSet, tags);
CorrelationAttributeInstance correlationAttribute = null;
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences()== false) {
correlationAttribute = getCorrelationAttributeInstance();
}
addCommentProperty(sheetSet, tags, correlationAttribute);
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences()== false) {
addCountProperty(sheetSet, correlationAttribute);
}
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, this.getContent().getParentPath()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource())); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, StringUtils.defaultString(this.getContent().getMIMEType())));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, caseName)); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, caseName));
return sheet; return sheet;
} }
} }

View File

@ -303,23 +303,25 @@
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Component id="dateRangeLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0"> <Component id="dateRangeLabel" min="-2" max="-2" attributes="0"/>
<Component id="endCheckBox" linkSize="1" min="-2" max="-2" attributes="0"/> <Group type="102" attributes="0">
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="endDatePicker" min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0">
</Group> <Group type="102" alignment="1" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Component id="endCheckBox" linkSize="1" min="-2" max="-2" attributes="0"/>
<Component id="startCheckBox" linkSize="1" min="-2" max="-2" attributes="0"/> <EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/> <Component id="endDatePicker" min="-2" max="-2" attributes="0"/>
<Component id="startDatePicker" min="-2" max="-2" attributes="0"/> </Group>
<Group type="102" alignment="0" attributes="0">
<Component id="startCheckBox" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
<Component id="startDatePicker" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group> </Group>
</Group> </Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>

View File

@ -124,7 +124,8 @@ final public class FiltersPanel extends JPanel {
updateFilters(true); updateFilters(true);
UserPreferences.addChangeListener(preferenceChangeEvent -> { UserPreferences.addChangeListener(preferenceChangeEvent -> {
if (preferenceChangeEvent.getKey().equals(UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME)) { if (preferenceChangeEvent.getKey().equals(UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME) ||
preferenceChangeEvent.getKey().equals(UserPreferences.TIME_ZONE_FOR_DISPLAYS)) {
updateTimeZone(); updateTimeZone();
} }
}); });
@ -175,7 +176,7 @@ final public class FiltersPanel extends JPanel {
} }
private void updateTimeZone() { private void updateTimeZone() {
dateRangeLabel.setText("Date Range ( " + Utils.getUserPreferredZoneId().toString() + "):"); dateRangeLabel.setText("Date Range (" + Utils.getUserPreferredZoneId().toString() + "):");
} }
/** /**

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.communications;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.util.TimeZone;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts; import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
@ -33,7 +34,8 @@ class Utils {
} }
static ZoneId getUserPreferredZoneId() { static ZoneId getUserPreferredZoneId() {
ZoneId zone = UserPreferences.displayTimesInLocalTime() ? ZoneOffset.systemDefault() : ZoneOffset.UTC; ZoneId zone = UserPreferences.displayTimesInLocalTime() ?
ZoneOffset.systemDefault() : TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()).toZoneId();
return zone; return zone;
} }

View File

@ -67,19 +67,19 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
initComponents(); initComponents();
Utilities.configureTextPaneAsHtml(jTextPane1); Utilities.configureTextPaneAsHtml(jTextPane1);
} }
@Override @Override
public void setNode(Node node) { public void setNode(Node node) {
if ((node == null) || (!isSupported(node))) { if ((node == null) || (!isSupported(node))) {
resetComponent(); resetComponent();
return; return;
} }
StringBuilder html = new StringBuilder(); StringBuilder html = new StringBuilder();
BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class); BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class);
Content sourceFile = null; Content sourceFile = null;
try { try {
if (artifact != null) { if (artifact != null) {
/* /*
@ -100,32 +100,32 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
"Exception while trying to retrieve a Content instance from the BlackboardArtifact '%s' (id=%d).", "Exception while trying to retrieve a Content instance from the BlackboardArtifact '%s' (id=%d).",
artifact.getDisplayName(), artifact.getArtifactID()), ex); artifact.getDisplayName(), artifact.getArtifactID()), ex);
} }
if (artifact != null) { if (artifact != null) {
populateTagData(html, artifact, sourceFile); populateTagData(html, artifact, sourceFile);
} else { } else {
populateTagData(html, sourceFile); populateTagData(html, sourceFile);
} }
if (sourceFile instanceof AbstractFile) { if (sourceFile instanceof AbstractFile) {
populateCentralRepositoryData(html, artifact, (AbstractFile) sourceFile); populateCentralRepositoryData(html, artifact, (AbstractFile) sourceFile);
} }
setText(html.toString()); setText(html.toString());
jTextPane1.setCaretPosition(0); jTextPane1.setCaretPosition(0);
} }
/** /**
* Populate the "Selected Item" sections with tag data for the supplied * Populate the "Selected Item" sections with tag data for the supplied
* content. * content.
* *
* @param html The HTML text to update. * @param html The HTML text to update.
* @param content Selected content. * @param content Selected content.
*/ */
private void populateTagData(StringBuilder html, Content content) { private void populateTagData(StringBuilder html, Content content) {
try { try {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
startSection(html, "Selected Item"); startSection(html, "Selected Item");
List<ContentTag> fileTagsList = tskCase.getContentTagsByContent(content); List<ContentTag> fileTagsList = tskCase.getContentTagsByContent(content);
if (fileTagsList.isEmpty()) { if (fileTagsList.isEmpty()) {
@ -142,11 +142,11 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
logger.log(Level.SEVERE, "Exception while getting tags from the case database.", ex); //NON-NLS logger.log(Level.SEVERE, "Exception while getting tags from the case database.", ex); //NON-NLS
} }
} }
/** /**
* Populate the "Selected Item" and "Source File" sections with tag data for * Populate the "Selected Item" and "Source File" sections with tag data for
* a supplied artifact. * a supplied artifact.
* *
* @param html The HTML text to update. * @param html The HTML text to update.
* @param artifact A selected artifact. * @param artifact A selected artifact.
* @param sourceFile The source content of the selected artifact. * @param sourceFile The source content of the selected artifact.
@ -154,7 +154,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
private void populateTagData(StringBuilder html, BlackboardArtifact artifact, Content sourceFile) { private void populateTagData(StringBuilder html, BlackboardArtifact artifact, Content sourceFile) {
try { try {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
startSection(html, "Selected Item"); startSection(html, "Selected Item");
List<BlackboardArtifactTag> artifactTagsList = tskCase.getBlackboardArtifactTagsByArtifact(artifact); List<BlackboardArtifactTag> artifactTagsList = tskCase.getBlackboardArtifactTagsByArtifact(artifact);
if (artifactTagsList.isEmpty()) { if (artifactTagsList.isEmpty()) {
@ -165,7 +165,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
} }
} }
endSection(html); endSection(html);
if (sourceFile != null) { if (sourceFile != null) {
startSection(html, "Source File"); startSection(html, "Source File");
List<ContentTag> fileTagsList = tskCase.getContentTagsByContent(sourceFile); List<ContentTag> fileTagsList = tskCase.getContentTagsByContent(sourceFile);
@ -184,10 +184,10 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
logger.log(Level.SEVERE, "Exception while getting tags from the case database.", ex); //NON-NLS logger.log(Level.SEVERE, "Exception while getting tags from the case database.", ex); //NON-NLS
} }
} }
/** /**
* Populate the "Central Repository Comments" section with data. * Populate the "Central Repository Comments" section with data.
* *
* @param html The HTML text to update. * @param html The HTML text to update.
* @param artifact A selected artifact (can be null). * @param artifact A selected artifact (can be null).
* @param sourceFile A selected file, or a source file of the selected * @param sourceFile A selected file, or a source file of the selected
@ -208,23 +208,24 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
if (attributeType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) { if (attributeType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCase()); CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCase());
instancesList.add(new CorrelationAttributeInstance( instancesList.add(new CorrelationAttributeInstance(
md5,
attributeType, attributeType,
md5,
correlationCase, correlationCase,
CorrelationDataSource.fromTSKDataSource(correlationCase, sourceFile.getDataSource()), CorrelationDataSource.fromTSKDataSource(correlationCase, sourceFile.getDataSource()),
sourceFile.getParentPath() + sourceFile.getName(), sourceFile.getParentPath() + sourceFile.getName(),
"", "",
sourceFile.getKnown())); sourceFile.getKnown(),
sourceFile.getId()));
break; break;
} }
} }
} }
boolean commentDataFound = false; boolean commentDataFound = false;
for (CorrelationAttributeInstance instance : instancesList) { for (CorrelationAttributeInstance instance : instancesList) {
List<CorrelationAttributeInstance> correlatedInstancesList = List<CorrelationAttributeInstance> correlatedInstancesList
EamDb.getInstance().getArtifactInstancesByTypeValue(instance.getCorrelationType(), instance.getCorrelationValue()); = EamDb.getInstance().getArtifactInstancesByTypeValue(instance.getCorrelationType(), instance.getCorrelationValue());
for (CorrelationAttributeInstance correlatedInstance : correlatedInstancesList) { for (CorrelationAttributeInstance correlatedInstance : correlatedInstancesList) {
if (correlatedInstance.getComment() != null && correlatedInstance.getComment().isEmpty() == false) { if (correlatedInstance.getComment() != null && correlatedInstance.getComment().isEmpty() == false) {
commentDataFound = true; commentDataFound = true;
@ -232,7 +233,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
} }
} }
} }
if (commentDataFound == false) { if (commentDataFound == false) {
addMessage(html, "There is no comment data for the selected content in the Central Repository."); addMessage(html, "There is no comment data for the selected content in the Central Repository.");
} }
@ -247,16 +248,16 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
/** /**
* Set the text of the text panel. * Set the text of the text panel.
* *
* @param text The text to set to the text panel. * @param text The text to set to the text panel.
*/ */
private void setText(String text) { private void setText(String text) {
jTextPane1.setText("<html><body>" + text + "</body></html>"); //NON-NLS jTextPane1.setText("<html><body>" + text + "</body></html>"); //NON-NLS
} }
/** /**
* Start a new data section. * Start a new data section.
* *
* @param html The HTML text to add the section to. * @param html The HTML text to add the section to.
* @param sectionName The name of the section. * @param sectionName The name of the section.
*/ */
@ -265,10 +266,10 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
.append(sectionName) .append(sectionName)
.append("</p><br>"); //NON-NLS .append("</p><br>"); //NON-NLS
} }
/** /**
* Add a message. * Add a message.
* *
* @param html The HTML text to add the message to. * @param html The HTML text to add the message to.
* @param message The message text. * @param message The message text.
*/ */
@ -277,10 +278,10 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
.append(message) .append(message)
.append("</p><br>"); //NON-NLS .append("</p><br>"); //NON-NLS
} }
/** /**
* Add a data table containing information about a tag. * Add a data table containing information about a tag.
* *
* @param html The HTML text to add the table to. * @param html The HTML text to add the table to.
* @param tag The tag whose information will be used to populate the table. * @param tag The tag whose information will be used to populate the table.
*/ */
@ -296,11 +297,11 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
addRow(html, Bundle.AnnotationsContentViewer_tagEntryDataLabel_comment(), formatHtmlString(tag.getComment())); addRow(html, Bundle.AnnotationsContentViewer_tagEntryDataLabel_comment(), formatHtmlString(tag.getComment()));
endTable(html); endTable(html);
} }
/** /**
* Add a data table containing information about a correlation attribute * Add a data table containing information about a correlation attribute
* instance in the Central Repository. * instance in the Central Repository.
* *
* @param html The HTML text to add the table to. * @param html The HTML text to add the table to.
* @param attributeInstance The attribute instance whose information will be * @param attributeInstance The attribute instance whose information will be
* used to populate the table. * used to populate the table.
@ -319,10 +320,10 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_path(), attributeInstance.getFilePath()); addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_path(), attributeInstance.getFilePath());
endTable(html); endTable(html);
} }
/** /**
* Start a data table. * Start a data table.
* *
* @param html The HTML text to add the table to. * @param html The HTML text to add the table to.
*/ */
private void startTable(StringBuilder html) { private void startTable(StringBuilder html) {
@ -331,7 +332,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
/** /**
* Add a data row to a table. * Add a data row to a table.
* *
* @param html The HTML text to add the row to. * @param html The HTML text to add the row to.
* @param key The key for the left column of the data row. * @param key The key for the left column of the data row.
* @param value The value for the right column of the data row. * @param value The value for the right column of the data row.
@ -343,10 +344,10 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
html.append(value); html.append(value);
html.append("</td></tr>"); //NON-NLS html.append("</td></tr>"); //NON-NLS
} }
/** /**
* End a data table. * End a data table.
* *
* @param html The HTML text on which to end a table. * @param html The HTML text on which to end a table.
*/ */
private void endTable(StringBuilder html) { private void endTable(StringBuilder html) {
@ -355,18 +356,19 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
/** /**
* End a data section. * End a data section.
* *
* @param html The HTML text on which to end a section. * @param html The HTML text on which to end a section.
*/ */
private void endSection(StringBuilder html) { private void endSection(StringBuilder html) {
html.append("<br>"); //NON-NLS html.append("<br>"); //NON-NLS
} }
/** /**
* Apply escape sequence to special characters. Line feed and carriage * Apply escape sequence to special characters. Line feed and carriage
* return character combinations will be converted to HTML line breaks. * return character combinations will be converted to HTML line breaks.
* *
* @param text The text to format. * @param text The text to format.
*
* @return The formatted text. * @return The formatted text.
*/ */
private String formatHtmlString(String text) { private String formatHtmlString(String text) {
@ -428,7 +430,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
@Override @Override
public boolean isSupported(Node node) { public boolean isSupported(Node node) {
BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class); BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class);
try { try {
if (artifact != null) { if (artifact != null) {
if (artifact.getSleuthkitCase().getAbstractFileById(artifact.getObjectID()) != null) { if (artifact.getSleuthkitCase().getAbstractFileById(artifact.getObjectID()) != null) {
@ -444,7 +446,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
"Exception while trying to retrieve a Content instance from the BlackboardArtifact '%s' (id=%d).", "Exception while trying to retrieve a Content instance from the BlackboardArtifact '%s' (id=%d).",
artifact.getDisplayName(), artifact.getArtifactID()), ex); artifact.getDisplayName(), artifact.getArtifactID()), ex);
} }
return false; return false;
} }

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.contentviewers;
import java.awt.Component; import java.awt.Component;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -43,6 +44,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode;
import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
@ -721,31 +723,23 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
Sheet sheet = new Sheet(); Sheet sheet = super.createSheet();
Set<String> keepProps = new HashSet<>(Arrays.asList(
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.sizeColLbl"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.knownColLbl")));
//Remove all other props except for the ones above
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) { for(Property<?> p : sheetSet.getProperties()) {
sheetSet = Sheet.createPropertiesSet(); if(!keepProps.contains(p.getName())){
sheet.put(sheetSet); sheetSet.remove(p.getName());
}
} }
List<ContentTag> tags = getContentTagsFromDatabase();
AbstractFile file = getContent();
sheetSet.put(new NodeProperty<>("Name", "Name", "Name", file.getName()));
addScoreProperty(sheetSet, tags);
CorrelationAttributeInstance correlationAttribute = null;
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences()== false) {
correlationAttribute = getCorrelationAttributeInstance();
}
addCommentProperty(sheetSet, tags, correlationAttribute);
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences()== false) {
addCountProperty(sheetSet, correlationAttribute);
}
sheetSet.put(new NodeProperty<>("Size", "Size", "Size", file.getSize()));
sheetSet.put(new NodeProperty<>("Mime Type", "Mime Type", "Mime Type", StringUtils.defaultString(file.getMIMEType())));
sheetSet.put(new NodeProperty<>("Known", "Known", "Known", file.getKnown().getName()));
return sheet; return sheet;
} }

View File

@ -74,7 +74,7 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer
private final Outline outline; private final Outline outline;
private ExplorerManager explorerManager; private ExplorerManager explorerManager;
private NSDictionary rootDict; private NSObject rootDict;
/** /**
* Creates new form PListViewer * Creates new form PListViewer
@ -415,22 +415,35 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer
} }
/** /**
* Parses given binary stream and extracts Plist key/value * Parses given binary stream and extracts Plist key/value.
* *
* @param plistbytes * @param plistbytes The byte array containing the Plist data.
* *
* @return list of PropKeyValue * @return list of PropKeyValue
*/ */
private List<PropKeyValue> parsePList(final byte[] plistbytes) throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException { private List<PropKeyValue> parsePList(final byte[] plistbytes) throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException {
final List<PropKeyValue> plist = new ArrayList<>(); final List<PropKeyValue> plist = new ArrayList<>();
rootDict = (NSDictionary) PropertyListParser.parse(plistbytes); rootDict = PropertyListParser.parse(plistbytes);
final String[] keys = rootDict.allKeys(); /*
for (final String key : keys) { * Parse the data if the root is an NSArray or NSDictionary. Anything
final PropKeyValue pkv = parseProperty(key, rootDict.objectForKey(key)); * else is unexpected and will be ignored.
if (null != pkv) { */
plist.add(pkv); if (rootDict instanceof NSArray) {
for (int i=0; i < ((NSArray)rootDict).count(); i++) {
final PropKeyValue pkv = parseProperty("", ((NSArray)rootDict).objectAtIndex(i));
if (null != pkv) {
plist.add(pkv);
}
}
} else if (rootDict instanceof NSDictionary) {
final String[] keys = ((NSDictionary)rootDict).allKeys();
for (final String key : keys) {
final PropKeyValue pkv = parseProperty(key, ((NSDictionary)rootDict).objectForKey(key));
if (null != pkv) {
plist.add(pkv);
}
} }
} }

View File

@ -24,21 +24,15 @@ import java.awt.Cursor;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
@ -48,11 +42,11 @@ import org.apache.commons.io.FilenameUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.SQLiteTableReaderException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.autopsy.coreutils.SQLiteTableReader;
/** /**
* A file content viewer for SQLite database files. * A file content viewer for SQLite database files.
@ -66,8 +60,14 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
private static final Logger logger = Logger.getLogger(FileViewer.class.getName()); private static final Logger logger = Logger.getLogger(FileViewer.class.getName());
private final SQLiteTableView selectedTableView = new SQLiteTableView(); private final SQLiteTableView selectedTableView = new SQLiteTableView();
private AbstractFile sqliteDbFile; private AbstractFile sqliteDbFile;
private File tmpDbFile;
private Connection connection; private SQLiteTableReader viewReader;
private Map<String, Object> row = new LinkedHashMap<>();
private List<Map<String, Object>> pageOfTableRows = new ArrayList<>();
private List<String> currentTableHeader = new ArrayList<>();
private String prevTableName;
private int numRows; // num of rows in the selected table private int numRows; // num of rows in the selected table
private int currPage = 0; // curr page of rows being displayed private int currPage = 0; // curr page of rows being displayed
@ -264,18 +264,18 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
}//GEN-LAST:event_tablesDropdownListActionPerformed }//GEN-LAST:event_tablesDropdownListActionPerformed
/** /**
* The action when the Export Csv button is pressed. The file chooser window will pop * The action when the Export Csv button is pressed. The file chooser window
* up to choose where the user wants to save the csv file. The default location is case export directory. * will pop up to choose where the user wants to save the csv file. The
* default location is case export directory.
* *
* @param evt the action event * @param evt the action event
*/ */
@NbBundle.Messages({"SQLiteViewer.csvExport.fileName.empty=Please input a file name for exporting.", @NbBundle.Messages({"SQLiteViewer.csvExport.fileName.empty=Please input a file name for exporting.",
"SQLiteViewer.csvExport.title=Export to csv file", "SQLiteViewer.csvExport.title=Export to csv file",
"SQLiteViewer.csvExport.confirm.msg=Do you want to overwrite the existing file?"}) "SQLiteViewer.csvExport.confirm.msg=Do you want to overwrite the existing file?"})
private void exportCsvButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCsvButtonActionPerformed private void exportCsvButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCsvButtonActionPerformed
Case openCase = Case.getCurrentCase(); Case openCase = Case.getCurrentCase();
File caseDirectory = new File(openCase.getExportDirectory()); File caseDirectory = new File(openCase.getExportDirectory());
JFileChooser fileChooser = new JFileChooser(); JFileChooser fileChooser = new JFileChooser();
fileChooser.setDragEnabled(false); fileChooser.setDragEnabled(false);
fileChooser.setCurrentDirectory(caseDirectory); fileChooser.setCurrentDirectory(caseDirectory);
@ -292,14 +292,14 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
File file = fileChooser.getSelectedFile(); File file = fileChooser.getSelectedFile();
if (file.exists() && FilenameUtils.getExtension(file.getName()).equalsIgnoreCase("csv")) { if (file.exists() && FilenameUtils.getExtension(file.getName()).equalsIgnoreCase("csv")) {
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(this, if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(this,
Bundle.SQLiteViewer_csvExport_confirm_msg(), Bundle.SQLiteViewer_csvExport_confirm_msg(),
Bundle.SQLiteViewer_csvExport_title(), Bundle.SQLiteViewer_csvExport_title(),
JOptionPane.YES_NO_OPTION)) { JOptionPane.YES_NO_OPTION)) {
} else { } else {
return; return;
} }
} }
exportTableToCsv(file); exportTableToCsv(file);
} }
}//GEN-LAST:event_exportCsvButtonActionPerformed }//GEN-LAST:event_exportCsvButtonActionPerformed
@ -328,6 +328,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
public void setFile(AbstractFile file) { public void setFile(AbstractFile file) {
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
sqliteDbFile = file; sqliteDbFile = file;
initReader();
processSQLiteFile(); processSQLiteFile();
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
} }
@ -343,16 +344,15 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
tablesDropdownList.removeAllItems(); tablesDropdownList.removeAllItems();
numEntriesField.setText(""); numEntriesField.setText("");
// close DB connection to file try {
if (null != connection) { viewReader.close();
try { } catch (SQLiteTableReaderException ex) {
connection.close(); //Could not successfully close the reader, nothing we can do to recover.
connection = null;
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS
}
} }
row = new LinkedHashMap<>();
pageOfTableRows = new ArrayList<>();
currentTableHeader = new ArrayList<>();
viewReader = null;
sqliteDbFile = null; sqliteDbFile = null;
} }
@ -368,17 +368,10 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
"SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.", "SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.",
"# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",}) "# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",})
private void processSQLiteFile() { private void processSQLiteFile() {
tablesDropdownList.removeAllItems();
try { try {
String localDiskPath = SqliteUtil.writeAbstractFileToLocalDisk(sqliteDbFile); tablesDropdownList.removeAllItems();
SqliteUtil.findAndCopySQLiteMetaFile(sqliteDbFile);
// Load the SQLite JDBC driver, if necessary.
Class.forName("org.sqlite.JDBC"); //NON-NLS
connection = DriverManager.getConnection("jdbc:sqlite:" + localDiskPath); //NON-NLS
Collection<String> dbTablesMap = getTables(); Collection<String> dbTablesMap = viewReader.getTableNames();
if (dbTablesMap.isEmpty()) { if (dbTablesMap.isEmpty()) {
tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry()); tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry());
tablesDropdownList.setEnabled(false); tablesDropdownList.setEnabled(false);
@ -387,46 +380,20 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
tablesDropdownList.addItem(tableName); tablesDropdownList.addItem(tableName);
}); });
} }
} catch (ClassNotFoundException ex) { } catch (SQLiteTableReaderException ex) {
logger.log(Level.SEVERE, String.format("Failed to initialize JDBC SQLite '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS logger.log(Level.WARNING, String.format("Unable to get table names "
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver()); + "from sqlite file [%s] with id=[%d].", sqliteDbFile.getName(),
} catch (SQLException ex) { sqliteDbFile.getId(), ex.getMessage()));
logger.log(Level.SEVERE, String.format("Failed to get tables from DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase()); MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
} catch (IOException | NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed to create temp copy of DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToExtractFile());
} }
} }
/**
* Gets a collection of table names from the SQLite database file.
*
* @return A collection of table names
*/
private Collection<String> getTables() throws SQLException {
Collection<String> tableNames = new LinkedList<>();
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT name FROM sqlite_master "
+ " WHERE type= 'table' ")){
while (resultSet.next()) {
tableNames.add(resultSet.getString("name")); //NON-NLS
}
}
return tableNames;
}
@NbBundle.Messages({"# {0} - tableName", @NbBundle.Messages({"# {0} - tableName",
"SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}" "SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}"
}) })
private void selectTable(String tableName) { private void selectTable(String tableName) {
try {
try (Statement statement = connection.createStatement(); numRows = viewReader.getRowCount(tableName);
ResultSet resultSet = statement.executeQuery(
"SELECT count (*) as count FROM " + "\"" + tableName + "\"")) { //NON-NLS{
numRows = resultSet.getInt("count");
numEntriesField.setText(numRows + " entries"); numEntriesField.setText(numRows + " entries");
currPage = 1; currPage = 1;
@ -442,11 +409,19 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
} else { } else {
exportCsvButton.setEnabled(false); exportCsvButton.setEnabled(false);
nextPageButton.setEnabled(false); nextPageButton.setEnabled(false);
selectedTableView.setupTable(Collections.emptyList());
currentTableHeader = new ArrayList<>();
viewReader.read(tableName);
Map<String, Object> columnRow = new LinkedHashMap<>();
for(int i = 0; i< currentTableHeader.size(); i++){
columnRow.put(currentTableHeader.get(i), "");
}
selectedTableView.setupTable(Collections.singletonList(columnRow));
} }
} catch (SQLiteTableReaderException ex) {
} catch (SQLException ex) { logger.log(Level.WARNING, String.format("Failed to load table %s " //NON-NLS
logger.log(Level.SEVERE, String.format("Failed to load table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS + "from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), //NON-NLS
sqliteDbFile.getId()), ex.getMessage());
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName)); MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName));
} }
} }
@ -454,110 +429,192 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
@NbBundle.Messages({"# {0} - tableName", @NbBundle.Messages({"# {0} - tableName",
"SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"}) "SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"})
private void readTable(String tableName, int startRow, int numRowsToRead) { private void readTable(String tableName, int startRow, int numRowsToRead) {
try {
try ( //If the table name has changed, then clear our table header. SQLiteTableReader
Statement statement = connection.createStatement(); //will also detect the table name has changed and begin reading it as if it
ResultSet resultSet = statement.executeQuery( //were a brand new table.
"SELECT * FROM " + "\"" + tableName + "\"" if (!tableName.equals(prevTableName)) {
+ " LIMIT " + Integer.toString(numRowsToRead) prevTableName = tableName;
+ " OFFSET " + Integer.toString(startRow - 1))) {
List<Map<String, Object>> rows = resultSetToArrayList(resultSet);
if (Objects.nonNull(rows)) {
selectedTableView.setupTable(rows);
} else {
selectedTableView.setupTable(Collections.emptyList());
} }
} catch (SQLException ex) { currentTableHeader = new ArrayList<>();
logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS viewReader.read(tableName, numRowsToRead, startRow - 1);
selectedTableView.setupTable(pageOfTableRows);
pageOfTableRows = new ArrayList<>();
} catch (SQLiteTableReaderException ex) {
logger.log(Level.WARNING, String.format("Failed to read table %s from DB file '%s' " //NON-NLS
+ "(objId=%d) starting at row [%d] and limit [%d]", //NON-NLS
tableName, sqliteDbFile.getName(), sqliteDbFile.getId(),
startRow - 1, numRowsToRead), ex.getMessage());
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName)); MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
} }
} }
@NbBundle.Messages("SQLiteViewer.BlobNotShown.message=BLOB Data not shown") /**
private List<Map<String, Object>> resultSetToArrayList(ResultSet resultSet) throws SQLException { * Creates a new SQLiteTableReader. This class will iterate through the
ResultSetMetaData metaData = resultSet.getMetaData(); * table row by row and pass each value to the correct function based on its
int columns = metaData.getColumnCount(); * data type. For our use, we want to define an action when encountering
ArrayList<Map<String, Object>> rowlist = new ArrayList<>(); * column names and an action for all other data types.
while (resultSet.next()) { */
Map<String, Object> row = new LinkedHashMap<>(columns); private void initReader() {
for (int i = 1; i <= columns; ++i) { viewReader = new SQLiteTableReader.Builder(sqliteDbFile)
if (resultSet.getObject(i) == null) { .onColumnNames((columnName) -> {
row.put(metaData.getColumnName(i), ""); currentTableHeader.add(columnName);
} else { })
if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) { .forAll(getForAllStrategy()).build();
row.put(metaData.getColumnName(i), Bundle.SQLiteViewer_BlobNotShown_message());
} else {
row.put(metaData.getColumnName(i), resultSet.getObject(i));
}
}
}
rowlist.add(row);
}
return rowlist;
} }
/**
* For every database value we encounter on our read of the table do the
* following: 1) Get the string representation of the value 2) Collect the
* values until we have a full database row. 3) If we have the full row,
* write it to the UI.
*
* rowIndex is purely for indicating if we have read the full row.
*
* @return Consumer that will perform the actions above. When the
* SQLiteTableReader is reading, values will be passed to this
* consumer.
*/
private Consumer<Object> getForAllStrategy() {
return new Consumer<Object>() {
private int rowIndex = 0;
@Override
public void accept(Object t) {
rowIndex++;
String objectStr = (t instanceof byte[]) ? "BLOB Data not shown"
: Objects.toString(t, "");
row.put(currentTableHeader.get(rowIndex - 1), objectStr);
//If we have built up a full database row, then add it to our page
//of rows to be displayed in the UI.
if (rowIndex == currentTableHeader.size()) {
pageOfTableRows.add(row);
row = new LinkedHashMap<>();
}
rowIndex %= currentTableHeader.size();
}
};
}
private int totalColumnCount;
@NbBundle.Messages({"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.", @NbBundle.Messages({"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
"SQLiteViewer.exportTableToCsv.FileName=File name: ", "SQLiteViewer.exportTableToCsv.FileName=File name: ",
"SQLiteViewer.exportTableToCsv.TableName=Table name: " "SQLiteViewer.exportTableToCsv.TableName=Table name: "
}) })
private void exportTableToCsv(File file) { private void exportTableToCsv(File file) {
File csvFile = new File(file.toString() + ".csv");
String tableName = (String) this.tablesDropdownList.getSelectedItem(); String tableName = (String) this.tablesDropdownList.getSelectedItem();
try ( try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
Statement statement = connection.createStatement(); try (SQLiteTableReader sqliteStream = new SQLiteTableReader.Builder(sqliteDbFile)
ResultSet resultSet = statement.executeQuery("SELECT * FROM " + "\"" + tableName + "\"")) { .onColumnNames(getColumnNameCSVStrategy(out))
List<Map<String, Object>> currentTableRows = resultSetToArrayList(resultSet); .forAll(getForAllCSVStrategy(out)).build()) {
totalColumnCount = sqliteStream.getColumnCount(tableName);
if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) { sqliteStream.read(tableName);
logger.log(Level.INFO, String.format("The table %s is empty. (objId=%d)", tableName, sqliteDbFile.getId())); //NON-NLS
} else {
File csvFile;
String fileName = file.getName();
if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) {
csvFile = file;
} else {
csvFile = new File(file.toString() + ".csv");
}
try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes());
out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes());
// Set up the column names
Map<String, Object> row = currentTableRows.get(0);
StringBuffer header = new StringBuffer();
for (Map.Entry<String, Object> col : row.entrySet()) {
String colName = col.getKey();
if (header.length() > 0) {
header.append(',').append(colName);
} else {
header.append(colName);
}
}
out.write(header.append('\n').toString().getBytes());
for (Map<String, Object> maps : currentTableRows) {
StringBuffer valueLine = new StringBuffer();
maps.values().forEach((value) -> {
if (valueLine.length() > 0) {
valueLine.append(',').append(value.toString());
} else {
valueLine.append(value.toString());
}
});
out.write(valueLine.append('\n').toString().getBytes());
}
}
} }
} catch (SQLException ex) { } catch (IOException | SQLiteTableReaderException | RuntimeException ex) {
logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS logger.log(Level.WARNING, String.format("Failed to export table [%s]"
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName)); + " to CSV in sqlite file '%s' (objId=%d)", tableName, sqliteDbFile.getName(),
} catch (IOException ex) { sqliteDbFile.getId()), ex.getMessage()); //NON-NLS
logger.log(Level.SEVERE, String.format("Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText()); MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText());
} }
} }
/**
* For every column name we encounter on our read of the table do the
* following: 1) Format the name so that it is comma seperated 2) Write the
* value to the output stream.
*
* columnIndex is purely for keeping track of where the column name is in
* the table so the value can be correctly formatted.
*
* @param out Output stream that this database table is being written to.
*
* @return Consumer that will perform the actions above. When the
* SQLiteTableReader is reading, values will be passed to this
* consumer.
*/
private Consumer<String> getColumnNameCSVStrategy(FileOutputStream out) {
return new Consumer<String>() {
private int columnIndex = 0;
@Override
public void accept(String columnName) {
columnIndex++;
//Format the value to adhere to the format of a CSV file
if (columnIndex == 1) {
columnName = "\"" + columnName + "\"";
} else {
columnName = ",\"" + columnName + "\"";
}
if (columnIndex == totalColumnCount) {
columnName += "\n";
}
try {
out.write(columnName.getBytes());
} catch (IOException ex) {
/*
* If we can no longer write to the output stream, toss a
* runtime exception to get out of iteration. We explicitly
* catch this in exportTableToCsv() above.
*/
throw new RuntimeException(ex);
}
}
};
}
/**
* For every database value we encounter on our read of the table do the
* following: 1) Get the string representation of the value 2) Format it so
* that it adheres to the CSV format. 3) Write it to the output file.
*
* rowIndex is purely for keeping track of positioning of the database value
* in the row, so that it can be properly formatted.
*
* @param out Output file
*
* @return Consumer that will perform the actions above. When the
* SQLiteTableReader is reading, values will be passed to this
* consumer.
*/
private Consumer<Object> getForAllCSVStrategy(FileOutputStream out) {
return new Consumer<Object>() {
private int rowIndex = 0;
@Override
public void accept(Object tableValue) {
rowIndex++;
//Substitute string representation of blob with placeholder text.
//Automatically wrap the value in quotes in case it contains commas.
String objectStr = (tableValue instanceof byte[])
? "BLOB Data not shown" : Objects.toString(tableValue, "");
objectStr = "\"" + objectStr + "\"";
if (rowIndex > 1) {
objectStr = "," + objectStr;
}
if (rowIndex == totalColumnCount) {
objectStr += "\n";
}
try {
out.write(objectStr.getBytes());
} catch (IOException ex) {
/*
* If we can no longer write to the output stream, toss a
* runtime exception to get out of iteration. We explicitly
* catch this in exportTableToCsv() above.
*/
throw new RuntimeException(ex);
}
rowIndex = rowIndex % totalColumnCount;
}
};
}
} }

View File

@ -1,130 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2018 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.contentviewers;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Sqlite utility class. Find and copy metafiles, write sqlite abstract files to
* temp directory, and generate unique temp directory paths.
*/
final class SqliteUtil {
private SqliteUtil() {
}
/**
* Overloaded implementation of
* {@link #findAndCopySQLiteMetaFile(AbstractFile, String) findAndCopySQLiteMetaFile}
* , automatically tries to copy -wal and -shm files without needing to know
* their existence.
*
* @param sqliteFile file which has -wal and -shm meta files
*
* @throws NoCurrentCaseException Case has been closed.
* @throws TskCoreException fileManager cannot find AbstractFile
* files.
* @throws IOException Issue during writing to file.
*/
public static void findAndCopySQLiteMetaFile(AbstractFile sqliteFile)
throws NoCurrentCaseException, TskCoreException, IOException {
findAndCopySQLiteMetaFile(sqliteFile, sqliteFile.getName() + "-wal");
findAndCopySQLiteMetaFile(sqliteFile, sqliteFile.getName() + "-shm");
}
/**
* Searches for a meta file associated with the give SQLite database. If
* found, it copies this file into the temp directory of the current case.
*
* @param sqliteFile file being processed
* @param metaFileName name of meta file to look for
*
* @throws NoCurrentCaseException Case has been closed.
* @throws TskCoreException fileManager cannot find AbstractFile
* files.
* @throws IOException Issue during writing to file.
*/
public static void findAndCopySQLiteMetaFile(AbstractFile sqliteFile,
String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException {
Case openCase = Case.getCurrentCaseThrows();
SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
Services services = new Services(sleuthkitCase);
FileManager fileManager = services.getFileManager();
List<AbstractFile> metaFiles = fileManager.findFiles(
sqliteFile.getDataSource(), metaFileName,
sqliteFile.getParent().getName());
if (metaFiles != null) {
for (AbstractFile metaFile : metaFiles) {
writeAbstractFileToLocalDisk(metaFile);
}
}
}
/**
* Copies the file contents into a unique path in the current case temp
* directory.
*
* @param file AbstractFile from the data source
*
* @return The path of the file on disk
*
* @throws IOException Exception writing file contents
* @throws NoCurrentCaseException Current case closed during file copying
*/
public static String writeAbstractFileToLocalDisk(AbstractFile file)
throws IOException, NoCurrentCaseException {
String localDiskPath = getUniqueTempDirectoryPath(file);
File localDatabaseFile = new File(localDiskPath);
if (!localDatabaseFile.exists()) {
ContentUtils.writeToFile(file, localDatabaseFile);
}
return localDiskPath;
}
/**
* Generates a unique local disk path that resides in the temp directory of
* the current case.
*
* @param file The database abstract file
*
* @return Unique local disk path living in the temp directory of the case
*
* @throws org.sleuthkit.autopsy.casemodule.NoCurrentCaseException
*/
public static String getUniqueTempDirectoryPath(AbstractFile file) throws NoCurrentCaseException {
return Case.getCurrentCaseThrows().getTempDirectory()
+ File.separator + file.getId() + file.getName();
}
}

View File

@ -24,6 +24,7 @@ import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
import java.util.prefs.PreferenceChangeListener; import java.util.prefs.PreferenceChangeListener;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import org.openide.util.NbPreferences; import org.openide.util.NbPreferences;
import org.python.icu.util.TimeZone;
import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.TextConverterException; import org.sleuthkit.autopsy.coreutils.TextConverterException;
@ -45,6 +46,7 @@ public final class UserPreferences {
public static final String HIDE_SLACK_FILES_IN_DATA_SRCS_TREE = "HideSlackFilesInDataSourcesTree"; //NON-NLS public static final String HIDE_SLACK_FILES_IN_DATA_SRCS_TREE = "HideSlackFilesInDataSourcesTree"; //NON-NLS
public static final String HIDE_SLACK_FILES_IN_VIEWS_TREE = "HideSlackFilesInViewsTree"; //NON-NLS public static final String HIDE_SLACK_FILES_IN_VIEWS_TREE = "HideSlackFilesInViewsTree"; //NON-NLS
public static final String DISPLAY_TIMES_IN_LOCAL_TIME = "DisplayTimesInLocalTime"; //NON-NLS public static final String DISPLAY_TIMES_IN_LOCAL_TIME = "DisplayTimesInLocalTime"; //NON-NLS
public static final String TIME_ZONE_FOR_DISPLAYS = "TimeZoneForDisplays"; //NON-NLS
public static final String NUMBER_OF_FILE_INGEST_THREADS = "NumberOfFileIngestThreads"; //NON-NLS public static final String NUMBER_OF_FILE_INGEST_THREADS = "NumberOfFileIngestThreads"; //NON-NLS
public static final String IS_MULTI_USER_MODE_ENABLED = "IsMultiUserModeEnabled"; //NON-NLS public static final String IS_MULTI_USER_MODE_ENABLED = "IsMultiUserModeEnabled"; //NON-NLS
public static final String EXTERNAL_DATABASE_HOSTNAME_OR_IP = "ExternalDatabaseHostnameOrIp"; //NON-NLS public static final String EXTERNAL_DATABASE_HOSTNAME_OR_IP = "ExternalDatabaseHostnameOrIp"; //NON-NLS
@ -72,7 +74,8 @@ public final class UserPreferences {
public static final String GROUP_ITEMS_IN_TREE_BY_DATASOURCE = "GroupItemsInTreeByDataSource"; //NON-NLS public static final String GROUP_ITEMS_IN_TREE_BY_DATASOURCE = "GroupItemsInTreeByDataSource"; //NON-NLS
public static final String SHOW_ONLY_CURRENT_USER_TAGS = "ShowOnlyCurrentUserTags"; public static final String SHOW_ONLY_CURRENT_USER_TAGS = "ShowOnlyCurrentUserTags";
public static final String HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES = "HideCentralRepoCommentsAndOccurrences"; public static final String HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES = "HideCentralRepoCommentsAndOccurrences";
public static final String DISPLAY_TRANSLATED_NAMES = "DisplayTranslatedNames";
// Prevent instantiation. // Prevent instantiation.
private UserPreferences() { private UserPreferences() {
} }
@ -181,6 +184,14 @@ public final class UserPreferences {
public static void setDisplayTimesInLocalTime(boolean value) { public static void setDisplayTimesInLocalTime(boolean value) {
preferences.putBoolean(DISPLAY_TIMES_IN_LOCAL_TIME, value); preferences.putBoolean(DISPLAY_TIMES_IN_LOCAL_TIME, value);
} }
public static String getTimeZoneForDisplays() {
return preferences.get(TIME_ZONE_FOR_DISPLAYS, TimeZone.GMT_ZONE.getID());
}
public static void setTimeZoneForDisplays(String timeZone) {
preferences.put(TIME_ZONE_FOR_DISPLAYS, timeZone);
}
public static int numberOfFileIngestThreads() { public static int numberOfFileIngestThreads() {
return preferences.getInt(NUMBER_OF_FILE_INGEST_THREADS, 2); return preferences.getInt(NUMBER_OF_FILE_INGEST_THREADS, 2);
@ -244,6 +255,14 @@ public final class UserPreferences {
public static void setHideCentralRepoCommentsAndOccurrences(boolean value) { public static void setHideCentralRepoCommentsAndOccurrences(boolean value) {
preferences.putBoolean(HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES, value); preferences.putBoolean(HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES, value);
} }
public static void setDisplayTranslatedFileNames(boolean value) {
preferences.putBoolean(DISPLAY_TRANSLATED_NAMES, value);
}
public static boolean displayTranslatedFileNames() {
return preferences.getBoolean(DISPLAY_TRANSLATED_NAMES, false);
}
/** /**
* Reads persisted case database connection info. * Reads persisted case database connection info.

View File

@ -165,30 +165,33 @@ AutopsyOptionsPanel.runtimePanel.border.title=Runtime
DataResultPanel.matchLabel.text=Results DataResultPanel.matchLabel.text=Results
DataResultPanel.numberOfChildNodesLabel.text=0 DataResultPanel.numberOfChildNodesLabel.text=0
DataResultPanel.descriptionLabel.text=directoryPath DataResultPanel.descriptionLabel.text=directoryPath
ViewPreferencesPanel.selectFileLabel.text=When selecting a file:
ViewPreferencesPanel.globalSettingsPanel.border.title=Global Settings
ViewPreferencesPanel.displayTimeLabel.text=When displaying times:
ViewPreferencesPanel.hideSlackFilesLabel.text=Hide slack files in the:
ViewPreferencesPanel.groupByDataSourceCheckbox.text=Group by data source ViewPreferencesPanel.groupByDataSourceCheckbox.text=Group by data source
ViewPreferencesPanel.hideKnownFilesLabel.text=Hide known files (i.e. those in the NIST NSRL) in the:
ViewPreferencesPanel.hideOtherUsersTagsCheckbox.text=Tags area in the tree
ViewPreferencesPanel.currentCaseSettingsPanel.border.title=Current Case Settings ViewPreferencesPanel.currentCaseSettingsPanel.border.title=Current Case Settings
OptionsCategory_Name_View=View OptionsCategory_Name_View=View
OptionsCategory_Keywords_View=View OptionsCategory_Keywords_View=View
ViewPreferencesPanel.useBestViewerRadioButton.toolTipText=For example, change from Hex to Media when a JPEG is selected.
ViewPreferencesPanel.useBestViewerRadioButton.text=Change to the most specific file viewer
ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=For example, stay in Hex view when a JPEG is selected.
ViewPreferencesPanel.keepCurrentViewerRadioButton.text=Stay on the same file viewer
ViewPreferencesPanel.useLocalTimeRadioButton.text=Use local time zone
ViewPreferencesPanel.useGMTTimeRadioButton.text=Use GMT
ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.viewsHideKnownCheckbox.text=Views area
ViewPreferencesPanel.dataSourcesHideSlackCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.viewsHideSlackCheckbox.text=Views area
ViewPreferencesPanel.currentSessionSettingsPanel.border.title=Current Session Settings ViewPreferencesPanel.currentSessionSettingsPanel.border.title=Current Session Settings
ViewPreferencesPanel.hideRejectedResultsCheckbox.text=Hide rejected results ViewPreferencesPanel.hideRejectedResultsCheckbox.text=Hide rejected results
ViewPreferencesPanel.hideOtherUsersTagsLabel.text=Hide other users' tags in the: ViewPreferencesPanel.translateTextLabel.text=Translate text in the:
ViewPreferencesPanel.centralRepoLabel.text=Do not use Central Repository for: ViewPreferencesPanel.globalSettingsPanel.border.title=Global Settings
ViewPreferencesPanel.commentsOccurencesColumnsCheckbox.text=C(omments) and O(ccurences) columns to reduce loading times ViewPreferencesPanel.translateNamesInTableRadioButton.text=Table
ViewPreferencesPanel.deletedFilesLimitCheckbox.text=Limit to 10,000
ViewPreferencesPanel.deletedFilesLimitLabel.text=Limit number of deleted files displayed: ViewPreferencesPanel.deletedFilesLimitLabel.text=Limit number of deleted files displayed:
ViewPreferencesPanel.deletedFilesLimitCheckbox.text=Limit to 10,000
ViewPreferencesPanel.commentsOccurencesColumnsCheckbox.text=C(omments) and O(ccurences) columns
ViewPreferencesPanel.centralRepoLabel.text=Do not use Central Repository for:
ViewPreferencesPanel.hideOtherUsersTagsLabel.text=Hide other users' tags in the:
ViewPreferencesPanel.hideOtherUsersTagsCheckbox.text=Tags area in the tree
ViewPreferencesPanel.useAnotherTimeRadioButton.text=Use another time zone
ViewPreferencesPanel.useLocalTimeRadioButton.text=Use local time zone
ViewPreferencesPanel.displayTimeLabel.text=When displaying times:
ViewPreferencesPanel.viewsHideSlackCheckbox.text=Views area
ViewPreferencesPanel.dataSourcesHideSlackCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.hideSlackFilesLabel.text=Hide slack files in the:
ViewPreferencesPanel.viewsHideKnownCheckbox.text=Views area
ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.hideKnownFilesLabel.text=Hide known files (i.e. those in the NIST NSRL) in the:
ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=For example, stay in Hex view when a JPEG is selected.
ViewPreferencesPanel.keepCurrentViewerRadioButton.text=Stay on the same file viewer
ViewPreferencesPanel.useBestViewerRadioButton.toolTipText=For example, change from Hex to Media when a JPEG is selected.
ViewPreferencesPanel.useBestViewerRadioButton.text=Change to the most specific file viewer
ViewPreferencesPanel.selectFileLabel.text=When selecting a file:
ViewPreferencesPanel.commentsOccurencesColumnWrapAroundText.text=to reduce loading times

View File

@ -121,13 +121,12 @@ DataResultPanel.matchLabel.text=\u7d50\u679c
DataResultPanel.numberOfChildNodesLabel.text=0 DataResultPanel.numberOfChildNodesLabel.text=0
DataResultPanel.descriptionLabel.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30d1\u30b9 DataResultPanel.descriptionLabel.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30d1\u30b9
ViewPreferencesPanel.selectFileLabel.text=\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u629e\u3059\u308b\u5834\u5408\uff1a ViewPreferencesPanel.selectFileLabel.text=\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u629e\u3059\u308b\u5834\u5408\uff1a
ViewPreferencesPanel.useLocalTimeRadioButton.text=\u30ed\u30fc\u30ab\u30eb\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u4f7f\u7528
ViewPreferencesPanel.displayTimeLabel.text=\u6642\u9593\u3092\u8868\u793a\u3059\u308b\u5834\u5408\uff1a ViewPreferencesPanel.displayTimeLabel.text=\u6642\u9593\u3092\u8868\u793a\u3059\u308b\u5834\u5408\uff1a
ViewPreferencesPanel.viewsHideKnownCheckbox.text=\u30d3\u30e5\u30fc\u30a8\u30ea\u30a2
ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30a8\u30ea\u30a2\uff08\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u968e\u5c64\uff09
ViewPreferencesPanel.hideKnownFilesLabel.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\uff08NIST NSRL\u5185\u306e\uff09\u3092\u6b21\u306b\u96a0\u3059\uff1a ViewPreferencesPanel.hideKnownFilesLabel.text=\u65e2\u77e5\u30d5\u30a1\u30a4\u30eb\uff08NIST NSRL\u5185\u306e\uff09\u3092\u6b21\u306b\u96a0\u3059\uff1a
ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=\u4f8b\u3048\u3070\u3001JPEG\u304c\u9078\u629e\u3055\u308c\u305f\u5834\u5408\u306b\u305d\u306e\u307e\u307eHEX\u30d3\u30e5\u30fc\u3092\u4f7f\u7528\u3002
ViewPreferencesPanel.keepCurrentViewerRadioButton.text=\u305d\u306e\u307e\u307e\u540c\u3058\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u30a2\u3092\u4f7f\u7528
ViewPreferencesPanel.useBestViewerRadioButton.toolTipText=\u4f8b\u3048\u3070\u3001JPEG\u304c\u9078\u629e\u3055\u308c\u305f\u5834\u5408\u306b\u306fHEX\u304b\u3089\u30e1\u30c7\u30a3\u30a2\u306b\u5909\u66f4\u3059\u308b\u3002 ViewPreferencesPanel.useBestViewerRadioButton.toolTipText=\u4f8b\u3048\u3070\u3001JPEG\u304c\u9078\u629e\u3055\u308c\u305f\u5834\u5408\u306b\u306fHEX\u304b\u3089\u30e1\u30c7\u30a3\u30a2\u306b\u5909\u66f4\u3059\u308b\u3002
ViewPreferencesPanel.useBestViewerRadioButton.text=\u6700\u3082\u5c02\u9580\u7684\u306a\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u30a2\u306b\u5909\u66f4 ViewPreferencesPanel.useBestViewerRadioButton.text=\u6700\u3082\u5c02\u9580\u7684\u306a\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u30a2\u306b\u5909\u66f4
ViewPreferencesPanel.keepCurrentViewerRadioButton.text=\u305d\u306e\u307e\u307e\u540c\u3058\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u30a2\u3092\u4f7f\u7528
ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=\u4f8b\u3048\u3070\u3001JPEG\u304c\u9078\u629e\u3055\u308c\u305f\u5834\u5408\u306b\u305d\u306e\u307e\u307eHEX\u30d3\u30e5\u30fc\u3092\u4f7f\u7528\u3002
ViewPreferencesPanel.useLocalTimeRadioButton.text=\u30ed\u30fc\u30ab\u30eb\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u4f7f\u7528
ViewPreferencesPanel.useGMTTimeRadioButton.text=GMT\u3092\u4f7f\u7528
ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30a8\u30ea\u30a2\uff08\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u968e\u5c64\uff09
ViewPreferencesPanel.viewsHideKnownCheckbox.text=\u30d3\u30e5\u30fc\u30a8\u30ea\u30a2

View File

@ -487,7 +487,8 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()) || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID()) || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID())) { || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID())) {
return 3; return 3;
} else { } else {
return 6; return 6;

View File

@ -40,6 +40,7 @@ public final class TextPrompt extends JLabel
public TextPrompt(String text, JTextComponent component, Show show) { public TextPrompt(String text, JTextComponent component, Show show) {
this.component = component; this.component = component;
component.removeAll();
setShow(show); setShow(show);
document = component.getDocument(); document = component.getDocument();

View File

@ -31,11 +31,19 @@
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/> <Border info="null"/>
</Property> </Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[625, 452]"/>
</Property>
</Properties> </Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Container class="javax.swing.JPanel" name="viewPreferencesPanel"> <Container class="javax.swing.JPanel" name="viewPreferencesPanel">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[625, 452]"/>
</Property>
</Properties>
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
@ -82,60 +90,68 @@
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="centralRepoLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="135" max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="272" max="-2" attributes="0"/>
</Group>
<Component id="hideOtherUsersTagsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="hideKnownFilesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="103" alignment="0" groupAlignment="1" attributes="0">
<Group type="103" alignment="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="dataSourcesHideSlackCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="viewsHideSlackCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="hideSlackFilesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="dataSourcesHideKnownCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="viewsHideKnownCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Component id="commentsOccurencesColumnsCheckbox" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
<Component id="commentsOccurencesColumnWrapAroundText" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="displayTimeLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="selectFileLabel" min="-2" max="-2" attributes="0"/>
<Component id="translateTextLabel" alignment="0" min="-2" pref="120" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="keepCurrentViewerRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useBestViewerRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useLocalTimeRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useAnotherTimeRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="translateNamesInTableRadioButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<Component id="deletedFilesLimitLabel" min="-2" pref="215" max="-2" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/> <EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="commentsOccurencesColumnsCheckbox" min="-2" max="-2" attributes="0"/>
<Component id="hideOtherUsersTagsCheckbox" min="-2" max="-2" attributes="0"/> <Component id="hideOtherUsersTagsCheckbox" min="-2" max="-2" attributes="0"/>
<Component id="deletedFilesLimitCheckbox" alignment="0" max="32767" attributes="0"/> <Component id="deletedFilesLimitCheckbox" pref="567" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="hideKnownFilesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="103" alignment="0" groupAlignment="1" attributes="0">
<Group type="103" alignment="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="dataSourcesHideSlackCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="viewsHideSlackCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="hideSlackFilesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="dataSourcesHideKnownCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="viewsHideKnownCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="displayTimeLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="keepCurrentViewerRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useBestViewerRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useGMTTimeRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useLocalTimeRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="selectFileLabel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="hideOtherUsersTagsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="centralRepoLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="deletedFilesLimitLabel" alignment="0" min="-2" pref="215" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="10" max="32767" attributes="0"/>
</Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -158,6 +174,18 @@
<Component id="dataSourcesHideSlackCheckbox" min="-2" max="-2" attributes="0"/> <Component id="dataSourcesHideSlackCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="viewsHideSlackCheckbox" min="-2" max="-2" attributes="0"/> <Component id="viewsHideSlackCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="hideOtherUsersTagsLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="hideOtherUsersTagsCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="centralRepoLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
<Component id="commentsOccurencesColumnsCheckbox" min="-2" pref="18" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="commentsOccurencesColumnWrapAroundText" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
<Component id="deletedFilesLimitLabel" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="selectFileLabel" min="-2" max="-2" attributes="0"/> <Component id="selectFileLabel" min="-2" max="-2" attributes="0"/>
@ -169,23 +197,19 @@
<Component id="displayTimeLabel" min="-2" max="-2" attributes="0"/> <Component id="displayTimeLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="useLocalTimeRadioButton" min="-2" max="-2" attributes="0"/> <Component id="useLocalTimeRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="useGMTTimeRadioButton" min="-2" max="-2" attributes="0"/> <Component id="useAnotherTimeRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="67" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="translateTextLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="translateNamesInTableRadioButton" min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="hideOtherUsersTagsLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="hideOtherUsersTagsCheckbox" min="-2" max="-2" attributes="0"/> <Component id="deletedFilesLimitCheckbox" min="-2" pref="26" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace pref="8" max="32767" attributes="0"/>
<Component id="centralRepoLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="commentsOccurencesColumnsCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="deletedFilesLimitLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="deletedFilesLimitCheckbox" min="-2" pref="33" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -295,14 +319,14 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="useLocalTimeRadioButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="useLocalTimeRadioButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="useGMTTimeRadioButton"> <Component class="javax.swing.JRadioButton" name="useAnotherTimeRadioButton">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="ViewPreferencesPanel.useGMTTimeRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="ViewPreferencesPanel.useAnotherTimeRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="useGMTTimeRadioButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="useAnotherTimeRadioButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="hideOtherUsersTagsCheckbox"> <Component class="javax.swing.JCheckBox" name="hideOtherUsersTagsCheckbox">
@ -322,16 +346,6 @@
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="commentsOccurencesColumnsCheckbox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="ViewPreferencesPanel.commentsOccurencesColumnsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="commentsOccurencesColumnsCheckboxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="centralRepoLabel"> <Component class="javax.swing.JLabel" name="centralRepoLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
@ -339,6 +353,17 @@
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="commentsOccurencesColumnsCheckbox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="ViewPreferencesPanel.commentsOccurencesColumnsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalAlignment" type="int" value="11"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="commentsOccurencesColumnsCheckboxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="deletedFilesLimitCheckbox"> <Component class="javax.swing.JCheckBox" name="deletedFilesLimitCheckbox">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
@ -356,6 +381,52 @@
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="timeZoneList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="0"/>
</Property>
</Properties>
<Events>
<EventHandler event="valueChanged" listener="javax.swing.event.ListSelectionListener" parameters="javax.swing.event.ListSelectionEvent" handler="timeZoneListValueChanged"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="translateTextLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="ViewPreferencesPanel.translateTextLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="commentsOccurencesColumnWrapAroundText">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="ViewPreferencesPanel.commentsOccurencesColumnWrapAroundText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="translateNamesInTableRadioButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="ViewPreferencesPanel.translateNamesInTableRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="translateNamesInTableRadioButtonActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Container class="javax.swing.JPanel" name="currentCaseSettingsPanel"> <Container class="javax.swing.JPanel" name="currentCaseSettingsPanel">

View File

@ -19,14 +19,17 @@
package org.sleuthkit.autopsy.corecomponents; package org.sleuthkit.autopsy.corecomponents;
import java.util.Objects; import java.util.Objects;
import java.util.TimeZone;
import javax.swing.JPanel; import javax.swing.JPanel;
import org.netbeans.spi.options.OptionsPanelController; import org.netbeans.spi.options.OptionsPanelController;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.deletedFiles.DeletedFilePreferences; import org.sleuthkit.autopsy.deletedFiles.DeletedFilePreferences;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
/** /**
* Panel for configuring view preferences. * Panel for configuring view preferences.
@ -44,6 +47,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
public ViewPreferencesPanel(boolean immediateUpdates) { public ViewPreferencesPanel(boolean immediateUpdates) {
initComponents(); initComponents();
this.immediateUpdates = immediateUpdates; this.immediateUpdates = immediateUpdates;
this.timeZoneList.setListData(TimeZoneUtils.createTimeZoneList().stream().toArray(String[]::new));
} }
@Override @Override
@ -54,8 +59,10 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
useBestViewerRadioButton.setSelected(!keepPreferredViewer); useBestViewerRadioButton.setSelected(!keepPreferredViewer);
boolean useLocalTime = UserPreferences.displayTimesInLocalTime(); boolean useLocalTime = UserPreferences.displayTimesInLocalTime();
timeZoneList.setEnabled(!useLocalTime);
timeZoneList.setSelectedValue(TimeZoneUtils.createTimeZoneString(TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays())), true);
useLocalTimeRadioButton.setSelected(useLocalTime); useLocalTimeRadioButton.setSelected(useLocalTime);
useGMTTimeRadioButton.setSelected(!useLocalTime); useAnotherTimeRadioButton.setSelected(!useLocalTime);
dataSourcesHideKnownCheckbox.setSelected(UserPreferences.hideKnownFilesInDataSourcesTree()); dataSourcesHideKnownCheckbox.setSelected(UserPreferences.hideKnownFilesInDataSourcesTree());
viewsHideKnownCheckbox.setSelected(UserPreferences.hideKnownFilesInViewsTree()); viewsHideKnownCheckbox.setSelected(UserPreferences.hideKnownFilesInViewsTree());
@ -64,9 +71,14 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
viewsHideSlackCheckbox.setSelected(UserPreferences.hideSlackFilesInViewsTree()); viewsHideSlackCheckbox.setSelected(UserPreferences.hideSlackFilesInViewsTree());
commentsOccurencesColumnsCheckbox.setEnabled(EamDbUtil.useCentralRepo()); commentsOccurencesColumnsCheckbox.setEnabled(EamDbUtil.useCentralRepo());
commentsOccurencesColumnWrapAroundText.setEnabled(EamDbUtil.useCentralRepo());
commentsOccurencesColumnsCheckbox.setSelected(UserPreferences.hideCentralRepoCommentsAndOccurrences()); commentsOccurencesColumnsCheckbox.setSelected(UserPreferences.hideCentralRepoCommentsAndOccurrences());
deletedFilesLimitCheckbox.setSelected(DeletedFilePreferences.getDefault().getShouldLimitDeletedFiles()); deletedFilesLimitCheckbox.setSelected(DeletedFilePreferences.getDefault().getShouldLimitDeletedFiles());
translateNamesInTableRadioButton.setSelected(UserPreferences.displayTranslatedFileNames());
TextTranslationService tts = TextTranslationService.getInstance();
translateNamesInTableRadioButton.setEnabled(tts.hasProvider());
// Current Case Settings // Current Case Settings
boolean caseIsOpen = Case.isCaseOpen(); boolean caseIsOpen = Case.isCaseOpen();
@ -84,12 +96,16 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
public void store() { public void store() {
UserPreferences.setKeepPreferredContentViewer(keepCurrentViewerRadioButton.isSelected()); UserPreferences.setKeepPreferredContentViewer(keepCurrentViewerRadioButton.isSelected());
UserPreferences.setDisplayTimesInLocalTime(useLocalTimeRadioButton.isSelected()); UserPreferences.setDisplayTimesInLocalTime(useLocalTimeRadioButton.isSelected());
if (useAnotherTimeRadioButton.isSelected()) {
UserPreferences.setTimeZoneForDisplays(timeZoneList.getSelectedValue().substring(11).trim());
}
UserPreferences.setHideKnownFilesInDataSourcesTree(dataSourcesHideKnownCheckbox.isSelected()); UserPreferences.setHideKnownFilesInDataSourcesTree(dataSourcesHideKnownCheckbox.isSelected());
UserPreferences.setHideKnownFilesInViewsTree(viewsHideKnownCheckbox.isSelected()); UserPreferences.setHideKnownFilesInViewsTree(viewsHideKnownCheckbox.isSelected());
UserPreferences.setHideSlackFilesInDataSourcesTree(dataSourcesHideSlackCheckbox.isSelected()); UserPreferences.setHideSlackFilesInDataSourcesTree(dataSourcesHideSlackCheckbox.isSelected());
UserPreferences.setHideSlackFilesInViewsTree(viewsHideSlackCheckbox.isSelected()); UserPreferences.setHideSlackFilesInViewsTree(viewsHideSlackCheckbox.isSelected());
UserPreferences.setShowOnlyCurrentUserTags(hideOtherUsersTagsCheckbox.isSelected()); UserPreferences.setShowOnlyCurrentUserTags(hideOtherUsersTagsCheckbox.isSelected());
UserPreferences.setHideCentralRepoCommentsAndOccurrences(commentsOccurencesColumnsCheckbox.isSelected()); UserPreferences.setHideCentralRepoCommentsAndOccurrences(commentsOccurencesColumnsCheckbox.isSelected());
UserPreferences.setDisplayTranslatedFileNames(translateNamesInTableRadioButton.isSelected());
storeGroupItemsInTreeByDataSource(); storeGroupItemsInTreeByDataSource();
@ -135,19 +151,27 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
viewsHideSlackCheckbox = new javax.swing.JCheckBox(); viewsHideSlackCheckbox = new javax.swing.JCheckBox();
displayTimeLabel = new javax.swing.JLabel(); displayTimeLabel = new javax.swing.JLabel();
useLocalTimeRadioButton = new javax.swing.JRadioButton(); useLocalTimeRadioButton = new javax.swing.JRadioButton();
useGMTTimeRadioButton = new javax.swing.JRadioButton(); useAnotherTimeRadioButton = new javax.swing.JRadioButton();
hideOtherUsersTagsCheckbox = new javax.swing.JCheckBox(); hideOtherUsersTagsCheckbox = new javax.swing.JCheckBox();
hideOtherUsersTagsLabel = new javax.swing.JLabel(); hideOtherUsersTagsLabel = new javax.swing.JLabel();
commentsOccurencesColumnsCheckbox = new javax.swing.JCheckBox();
centralRepoLabel = new javax.swing.JLabel(); centralRepoLabel = new javax.swing.JLabel();
commentsOccurencesColumnsCheckbox = new javax.swing.JCheckBox();
deletedFilesLimitCheckbox = new javax.swing.JCheckBox(); deletedFilesLimitCheckbox = new javax.swing.JCheckBox();
deletedFilesLimitLabel = new javax.swing.JLabel(); deletedFilesLimitLabel = new javax.swing.JLabel();
jScrollPane1 = new javax.swing.JScrollPane();
timeZoneList = new javax.swing.JList<>();
translateTextLabel = new javax.swing.JLabel();
commentsOccurencesColumnWrapAroundText = new javax.swing.JLabel();
translateNamesInTableRadioButton = new javax.swing.JRadioButton();
currentCaseSettingsPanel = new javax.swing.JPanel(); currentCaseSettingsPanel = new javax.swing.JPanel();
groupByDataSourceCheckbox = new javax.swing.JCheckBox(); groupByDataSourceCheckbox = new javax.swing.JCheckBox();
currentSessionSettingsPanel = new javax.swing.JPanel(); currentSessionSettingsPanel = new javax.swing.JPanel();
hideRejectedResultsCheckbox = new javax.swing.JCheckBox(); hideRejectedResultsCheckbox = new javax.swing.JCheckBox();
viewPreferencesScrollPane.setBorder(null); viewPreferencesScrollPane.setBorder(null);
viewPreferencesScrollPane.setPreferredSize(new java.awt.Dimension(625, 452));
viewPreferencesPanel.setPreferredSize(new java.awt.Dimension(625, 452));
globalSettingsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.globalSettingsPanel.border.title"))); // NOI18N globalSettingsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.globalSettingsPanel.border.title"))); // NOI18N
@ -210,10 +234,10 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(useGMTTimeRadioButton, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.useGMTTimeRadioButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(useAnotherTimeRadioButton, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.useAnotherTimeRadioButton.text")); // NOI18N
useGMTTimeRadioButton.addActionListener(new java.awt.event.ActionListener() { useAnotherTimeRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
useGMTTimeRadioButtonActionPerformed(evt); useAnotherTimeRadioButtonActionPerformed(evt);
} }
}); });
@ -226,15 +250,16 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
org.openide.awt.Mnemonics.setLocalizedText(hideOtherUsersTagsLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.hideOtherUsersTagsLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(hideOtherUsersTagsLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.hideOtherUsersTagsLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(centralRepoLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.centralRepoLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(commentsOccurencesColumnsCheckbox, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.commentsOccurencesColumnsCheckbox.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(commentsOccurencesColumnsCheckbox, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.commentsOccurencesColumnsCheckbox.text")); // NOI18N
commentsOccurencesColumnsCheckbox.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
commentsOccurencesColumnsCheckbox.addActionListener(new java.awt.event.ActionListener() { commentsOccurencesColumnsCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
commentsOccurencesColumnsCheckboxActionPerformed(evt); commentsOccurencesColumnsCheckboxActionPerformed(evt);
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(centralRepoLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.centralRepoLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(deletedFilesLimitCheckbox, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.deletedFilesLimitCheckbox.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(deletedFilesLimitCheckbox, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.deletedFilesLimitCheckbox.text")); // NOI18N
deletedFilesLimitCheckbox.addActionListener(new java.awt.event.ActionListener() { deletedFilesLimitCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
@ -244,6 +269,24 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
org.openide.awt.Mnemonics.setLocalizedText(deletedFilesLimitLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.deletedFilesLimitLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(deletedFilesLimitLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.deletedFilesLimitLabel.text")); // NOI18N
timeZoneList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
timeZoneListValueChanged(evt);
}
});
jScrollPane1.setViewportView(timeZoneList);
org.openide.awt.Mnemonics.setLocalizedText(translateTextLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.translateTextLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(commentsOccurencesColumnWrapAroundText, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.commentsOccurencesColumnWrapAroundText.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(translateNamesInTableRadioButton, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.translateNamesInTableRadioButton.text")); // NOI18N
translateNamesInTableRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
translateNamesInTableRadioButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout globalSettingsPanelLayout = new javax.swing.GroupLayout(globalSettingsPanel); javax.swing.GroupLayout globalSettingsPanelLayout = new javax.swing.GroupLayout(globalSettingsPanel);
globalSettingsPanel.setLayout(globalSettingsPanelLayout); globalSettingsPanel.setLayout(globalSettingsPanelLayout);
globalSettingsPanelLayout.setHorizontalGroup( globalSettingsPanelLayout.setHorizontalGroup(
@ -252,44 +295,51 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addContainerGap() .addContainerGap()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup() .addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10) .addComponent(centralRepoLabel)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(135, 135, 135)
.addComponent(commentsOccurencesColumnsCheckbox) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 272, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(hideOtherUsersTagsCheckbox) .addComponent(hideOtherUsersTagsLabel)
.addComponent(deletedFilesLimitCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addGroup(globalSettingsPanelLayout.createSequentialGroup() .addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup() .addComponent(hideKnownFilesLabel)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(hideKnownFilesLabel)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(dataSourcesHideSlackCheckbox)
.addComponent(viewsHideSlackCheckbox)))
.addComponent(hideSlackFilesLabel))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(dataSourcesHideKnownCheckbox)
.addComponent(viewsHideKnownCheckbox)))))
.addGap(18, 18, 18)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(displayTimeLabel)
.addGroup(globalSettingsPanelLayout.createSequentialGroup() .addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10) .addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(keepCurrentViewerRadioButton) .addComponent(dataSourcesHideSlackCheckbox)
.addComponent(useBestViewerRadioButton) .addComponent(viewsHideSlackCheckbox)))
.addComponent(useGMTTimeRadioButton) .addComponent(hideSlackFilesLabel))
.addComponent(useLocalTimeRadioButton))) .addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addComponent(selectFileLabel))) .addGap(10, 10, 10)
.addComponent(hideOtherUsersTagsLabel) .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(centralRepoLabel) .addComponent(dataSourcesHideKnownCheckbox)
.addComponent(deletedFilesLimitLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(viewsHideKnownCheckbox))))
.addGap(0, 10, Short.MAX_VALUE))) .addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addComponent(commentsOccurencesColumnsCheckbox))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(32, 32, 32)
.addComponent(commentsOccurencesColumnWrapAroundText)))
.addGap(18, 18, 18)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(displayTimeLabel)
.addComponent(selectFileLabel)
.addComponent(translateTextLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(keepCurrentViewerRadioButton)
.addComponent(useBestViewerRadioButton)
.addComponent(useLocalTimeRadioButton)
.addComponent(useAnotherTimeRadioButton)
.addComponent(translateNamesInTableRadioButton)))))
.addComponent(deletedFilesLimitLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(hideOtherUsersTagsCheckbox)
.addComponent(deletedFilesLimitCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, 567, Short.MAX_VALUE))))
.addContainerGap()) .addContainerGap())
); );
globalSettingsPanelLayout.setVerticalGroup( globalSettingsPanelLayout.setVerticalGroup(
@ -308,7 +358,19 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(dataSourcesHideSlackCheckbox) .addComponent(dataSourcesHideSlackCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(viewsHideSlackCheckbox)) .addComponent(viewsHideSlackCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(hideOtherUsersTagsLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(hideOtherUsersTagsCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(centralRepoLabel)
.addGap(3, 3, 3)
.addComponent(commentsOccurencesColumnsCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(commentsOccurencesColumnWrapAroundText)
.addGap(11, 11, 11)
.addComponent(deletedFilesLimitLabel))
.addGroup(globalSettingsPanelLayout.createSequentialGroup() .addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addComponent(selectFileLabel) .addComponent(selectFileLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
@ -320,20 +382,16 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(useLocalTimeRadioButton) .addComponent(useLocalTimeRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(useGMTTimeRadioButton))) .addComponent(useAnotherTimeRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(hideOtherUsersTagsLabel) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 67, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(translateTextLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(translateNamesInTableRadioButton)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(hideOtherUsersTagsCheckbox) .addComponent(deletedFilesLimitCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addContainerGap(8, Short.MAX_VALUE))
.addComponent(centralRepoLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(commentsOccurencesColumnsCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(deletedFilesLimitLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(deletedFilesLimitCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0))
); );
currentCaseSettingsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.currentCaseSettingsPanel.border.title"))); // NOI18N currentCaseSettingsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.currentCaseSettingsPanel.border.title"))); // NOI18N
@ -415,94 +473,14 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(viewPreferencesScrollPane) .addComponent(viewPreferencesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(viewPreferencesScrollPane) .addComponent(viewPreferencesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void useBestViewerRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useBestViewerRadioButtonActionPerformed
useBestViewerRadioButton.setSelected(true);
keepCurrentViewerRadioButton.setSelected(false);
if (immediateUpdates) {
UserPreferences.setKeepPreferredContentViewer(false);
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_useBestViewerRadioButtonActionPerformed
private void keepCurrentViewerRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_keepCurrentViewerRadioButtonActionPerformed
useBestViewerRadioButton.setSelected(false);
keepCurrentViewerRadioButton.setSelected(true);
if (immediateUpdates) {
UserPreferences.setKeepPreferredContentViewer(true);
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_keepCurrentViewerRadioButtonActionPerformed
private void useLocalTimeRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useLocalTimeRadioButtonActionPerformed
useLocalTimeRadioButton.setSelected(true);
useGMTTimeRadioButton.setSelected(false);
if (immediateUpdates) {
UserPreferences.setDisplayTimesInLocalTime(true);
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_useLocalTimeRadioButtonActionPerformed
private void useGMTTimeRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useGMTTimeRadioButtonActionPerformed
useLocalTimeRadioButton.setSelected(false);
useGMTTimeRadioButton.setSelected(true);
if (immediateUpdates) {
UserPreferences.setDisplayTimesInLocalTime(false);
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_useGMTTimeRadioButtonActionPerformed
private void dataSourcesHideKnownCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataSourcesHideKnownCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setHideKnownFilesInDataSourcesTree(dataSourcesHideKnownCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_dataSourcesHideKnownCheckboxActionPerformed
private void viewsHideKnownCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewsHideKnownCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setHideKnownFilesInViewsTree(viewsHideKnownCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_viewsHideKnownCheckboxActionPerformed
private void dataSourcesHideSlackCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataSourcesHideSlackCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setHideSlackFilesInDataSourcesTree(dataSourcesHideSlackCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_dataSourcesHideSlackCheckboxActionPerformed
private void viewsHideSlackCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewsHideSlackCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setHideSlackFilesInViewsTree(viewsHideSlackCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_viewsHideSlackCheckboxActionPerformed
private void hideOtherUsersTagsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hideOtherUsersTagsCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setShowOnlyCurrentUserTags(hideOtherUsersTagsCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_hideOtherUsersTagsCheckboxActionPerformed
private void groupByDataSourceCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_groupByDataSourceCheckboxActionPerformed private void groupByDataSourceCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_groupByDataSourceCheckboxActionPerformed
if (immediateUpdates) { if (immediateUpdates) {
storeGroupItemsInTreeByDataSource(); storeGroupItemsInTreeByDataSource();
@ -519,13 +497,21 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
} }
}//GEN-LAST:event_hideRejectedResultsCheckboxActionPerformed }//GEN-LAST:event_hideRejectedResultsCheckboxActionPerformed
private void commentsOccurencesColumnsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_commentsOccurencesColumnsCheckboxActionPerformed private void translateNamesInTableRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_translateNamesInTableRadioButtonActionPerformed
if (immediateUpdates) { if (immediateUpdates) {
UserPreferences.setHideCentralRepoCommentsAndOccurrences(commentsOccurencesColumnsCheckbox.isSelected()); UserPreferences.setDisplayTranslatedFileNames(translateNamesInTableRadioButton.isSelected());
} else { } else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
} }
}//GEN-LAST:event_commentsOccurencesColumnsCheckboxActionPerformed }//GEN-LAST:event_translateNamesInTableRadioButtonActionPerformed
private void timeZoneListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_timeZoneListValueChanged
if (immediateUpdates && useAnotherTimeRadioButton.isSelected()) {
UserPreferences.setTimeZoneForDisplays(timeZoneList.getSelectedValue().substring(11).trim());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_timeZoneListValueChanged
private void deletedFilesLimitCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deletedFilesLimitCheckboxActionPerformed private void deletedFilesLimitCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deletedFilesLimitCheckboxActionPerformed
if (immediateUpdates) { if (immediateUpdates) {
@ -535,9 +521,100 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
} }
}//GEN-LAST:event_deletedFilesLimitCheckboxActionPerformed }//GEN-LAST:event_deletedFilesLimitCheckboxActionPerformed
private void commentsOccurencesColumnsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_commentsOccurencesColumnsCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setHideCentralRepoCommentsAndOccurrences(commentsOccurencesColumnsCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_commentsOccurencesColumnsCheckboxActionPerformed
private void hideOtherUsersTagsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hideOtherUsersTagsCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setShowOnlyCurrentUserTags(hideOtherUsersTagsCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_hideOtherUsersTagsCheckboxActionPerformed
private void useAnotherTimeRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useAnotherTimeRadioButtonActionPerformed
useLocalTimeRadioButton.setSelected(false);
useAnotherTimeRadioButton.setSelected(true);
timeZoneList.setEnabled(true);
if (immediateUpdates) {
UserPreferences.setDisplayTimesInLocalTime(false);
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_useAnotherTimeRadioButtonActionPerformed
private void useLocalTimeRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useLocalTimeRadioButtonActionPerformed
useLocalTimeRadioButton.setSelected(true);
useAnotherTimeRadioButton.setSelected(false);
timeZoneList.setEnabled(false);
if (immediateUpdates) {
UserPreferences.setDisplayTimesInLocalTime(true);
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_useLocalTimeRadioButtonActionPerformed
private void viewsHideSlackCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewsHideSlackCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setHideSlackFilesInViewsTree(viewsHideSlackCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_viewsHideSlackCheckboxActionPerformed
private void dataSourcesHideSlackCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataSourcesHideSlackCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setHideSlackFilesInDataSourcesTree(dataSourcesHideSlackCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_dataSourcesHideSlackCheckboxActionPerformed
private void viewsHideKnownCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewsHideKnownCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setHideKnownFilesInViewsTree(viewsHideKnownCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_viewsHideKnownCheckboxActionPerformed
private void dataSourcesHideKnownCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataSourcesHideKnownCheckboxActionPerformed
if (immediateUpdates) {
UserPreferences.setHideKnownFilesInDataSourcesTree(dataSourcesHideKnownCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_dataSourcesHideKnownCheckboxActionPerformed
private void keepCurrentViewerRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_keepCurrentViewerRadioButtonActionPerformed
useBestViewerRadioButton.setSelected(false);
keepCurrentViewerRadioButton.setSelected(true);
if (immediateUpdates) {
UserPreferences.setKeepPreferredContentViewer(true);
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_keepCurrentViewerRadioButtonActionPerformed
private void useBestViewerRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useBestViewerRadioButtonActionPerformed
useBestViewerRadioButton.setSelected(true);
keepCurrentViewerRadioButton.setSelected(false);
if (immediateUpdates) {
UserPreferences.setKeepPreferredContentViewer(false);
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_useBestViewerRadioButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel centralRepoLabel; private javax.swing.JLabel centralRepoLabel;
private javax.swing.JLabel commentsOccurencesColumnWrapAroundText;
private javax.swing.JCheckBox commentsOccurencesColumnsCheckbox; private javax.swing.JCheckBox commentsOccurencesColumnsCheckbox;
private javax.swing.JPanel currentCaseSettingsPanel; private javax.swing.JPanel currentCaseSettingsPanel;
private javax.swing.JPanel currentSessionSettingsPanel; private javax.swing.JPanel currentSessionSettingsPanel;
@ -553,14 +630,18 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
private javax.swing.JLabel hideOtherUsersTagsLabel; private javax.swing.JLabel hideOtherUsersTagsLabel;
private javax.swing.JCheckBox hideRejectedResultsCheckbox; private javax.swing.JCheckBox hideRejectedResultsCheckbox;
private javax.swing.JLabel hideSlackFilesLabel; private javax.swing.JLabel hideSlackFilesLabel;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JRadioButton keepCurrentViewerRadioButton; private javax.swing.JRadioButton keepCurrentViewerRadioButton;
private javax.swing.JLabel selectFileLabel; private javax.swing.JLabel selectFileLabel;
private javax.swing.JList<String> timeZoneList;
private javax.swing.JRadioButton translateNamesInTableRadioButton;
private javax.swing.JLabel translateTextLabel;
private javax.swing.JRadioButton useAnotherTimeRadioButton;
private javax.swing.JRadioButton useBestViewerRadioButton; private javax.swing.JRadioButton useBestViewerRadioButton;
private javax.swing.JRadioButton useGMTTimeRadioButton;
private javax.swing.JRadioButton useLocalTimeRadioButton; private javax.swing.JRadioButton useLocalTimeRadioButton;
private javax.swing.JPanel viewPreferencesPanel; private javax.swing.JPanel viewPreferencesPanel;
private javax.swing.JScrollPane viewPreferencesScrollPane; private javax.swing.JScrollPane viewPreferencesScrollPane;
private javax.swing.JCheckBox viewsHideKnownCheckbox; private javax.swing.JCheckBox viewsHideKnownCheckbox;
private javax.swing.JCheckBox viewsHideSlackCheckbox; private javax.swing.JCheckBox viewsHideSlackCheckbox;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2012-2015 Basis Technology Corp. * Copyright 2012-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,14 +18,22 @@
*/ */
package org.sleuthkit.autopsy.coreutils; package org.sleuthkit.autopsy.coreutils;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.StringTokenizer;
public class NetworkUtils { public class NetworkUtils {
private NetworkUtils() {
}
/** /**
* Set the host name variable. Sometimes the network can be finicky, so the * Set the host name variable. Sometimes the network can be finicky, so the
* answer returned by getHostName() could throw an exception or be null. * answer returned by getHostName() could throw an exception or be null.
* Have it read the environment variable if getHostName() is unsuccessful. * Have it read the environment variable if getHostName() is unsuccessful.
*
* @return the local host name
*/ */
public static String getLocalHostName() { public static String getLocalHostName() {
String hostName = ""; String hostName = "";
@ -41,4 +49,78 @@ public class NetworkUtils {
} }
return hostName; return hostName;
} }
/**
* Attempt to manually extract the domain from a URL.
*
* @param url
* @return empty string if no domain could be found
*/
private static String getBaseDomain(String url) {
String host = null;
//strip protocol
String cleanUrl = url.replaceFirst(".*:\\/\\/", "");
//strip after slashes
String dirToks[] = cleanUrl.split("\\/");
if (dirToks.length > 0) {
host = dirToks[0];
} else {
host = cleanUrl;
}
//get the domain part from host (last 2)
StringTokenizer tok = new StringTokenizer(host, ".");
StringBuilder hostB = new StringBuilder();
int toks = tok.countTokens();
for (int count = 0; count < toks; ++count) {
String part = tok.nextToken();
int diff = toks - count;
if (diff < 3) {
hostB.append(part);
}
if (diff == 2) {
hostB.append(".");
}
}
String base = hostB.toString();
// verify there are no special characters in there
if (base.matches(".*[~`!@#$%^&\\*\\(\\)\\+={}\\[\\];:\\?<>,/ ].*")) {
return "";
}
return base;
}
/**
* Attempt to extract the domain from a URL.
* Will start by using the built-in URL class, and if that fails will
* try to extract it manually.
*
* @param urlString The URL to extract the domain from
* @return empty string if no domain name was found
*/
public static String extractDomain(String urlString) {
if (urlString == null) {
return "";
}
String result = "";
try {
URL url = new URL(urlString);
result = url.getHost();
} catch (MalformedURLException ex) {
//do not log if not a valid URL - we will try to extract it ourselves
}
//was not a valid URL, try a less picky method
if (result == null || result.trim().isEmpty()) {
return getBaseDomain(urlString);
}
return result;
}
} }

View File

@ -0,0 +1,561 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2018 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.coreutils;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Reads row by row through SQLite tables and performs user-defined actions on
* the row values. Table values are processed by data type. Users configure
* these actions for certain data types in the Builder. Example usage:
*
* SQLiteTableReader reader = new SQLiteTableReader.Builder(file)
* .onInteger((i)
* -> { System.out.println(i); })
* .build();
*
* reader.read(tableName);
*
* or
*
* SQLiteTableReader reader = new SQLiteTableReader.Builder(file)
* .onInteger(new Consumer<Integer>() {
* @Override public void accept(Integer i) {
* System.out.println(i);
* }
* }).build();
*
* reader.reader(tableName);
*
* Invocation of read(String tableName) reads row by row. When an Integer is
* encountered, its value will be passed to the Consumer that was defined above.
*/
public class SQLiteTableReader implements AutoCloseable {
/**
* Builder patten for configuring SQLiteTableReader instances.
*/
public static class Builder {
private final AbstractFile file;
private Consumer<String> onColumnNameAction;
private Consumer<String> onStringAction;
private Consumer<Long> onLongAction;
private Consumer<Integer> onIntegerAction;
private Consumer<Double> onFloatAction;
private Consumer<byte[]> onBlobAction;
private Consumer<Object> forAllAction;
static <T> Consumer<T> doNothing() {
return NOOP -> {};
}
/**
* Creates a Builder for this abstract file.
*
* @param file
*/
public Builder(AbstractFile file) {
this.file = file;
this.onColumnNameAction = Builder.doNothing();
this.onStringAction = Builder.doNothing();
this.onLongAction = Builder.doNothing();
this.onIntegerAction = Builder.doNothing();
this.onFloatAction = Builder.doNothing();
this.onBlobAction = Builder.doNothing();
this.forAllAction = Builder.doNothing();
}
/**
* Specify a function to do on column names. Column names will be read
* from left to right.
*
* @param action Consumer of column name strings
*
* @return Builder reference
*/
public Builder onColumnNames(Consumer<String> action) {
this.onColumnNameAction = action;
return this;
}
/**
* Specify a function to do when encountering a database value that is
* of java type String.
*
* @param action Consumer of strings
*
* @return Builder reference
*/
public Builder onString(Consumer<String> action) {
this.onStringAction = action;
return this;
}
/**
* Specify a function to do when encountering a database value that is
* of java type Integer.
*
* @param action Consumer of integer
*
* @return Builder reference
*/
public Builder onInteger(Consumer<Integer> action) {
this.onIntegerAction = action;
return this;
}
/**
* Specify a function to do when encountering a database value that is
* of java type Double.
*
* @param action Consumer of doubles
*
* @return Builder reference
*/
public Builder onFloat(Consumer<Double> action) {
this.onFloatAction = action;
return this;
}
/**
* Specify a function to do when encountering a database value that is
* of java type Long.
*
* @param action Consumer of longs
*
* @return Builder reference
*/
public Builder onLong(Consumer<Long> action) {
this.onLongAction = action;
return this;
}
/**
* Specify a function to do when encountering a database value that is
* of java type byte[] aka blob.
*
* @param action Consumer of blobs
*
* @return Builder reference
*/
public Builder onBlob(Consumer<byte[]> action) {
this.onBlobAction = action;
return this;
}
/**
* Specify a function to do when encountering any database value,
* regardless of type. This function only captures database values, not
* column names.
*
* @param action Consumer of objects
*
* @return Builder reference
*/
public Builder forAll(Consumer<Object> action) {
this.forAllAction = action;
return this;
}
/**
* Creates a SQLiteTableReader instance given this Builder
* configuration.
*
* @return SQLiteTableReader instance
*/
public SQLiteTableReader build() {
return new SQLiteTableReader(this);
}
}
private final AbstractFile file;
private final Builder builder;
private static final String SELECT_ALL_QUERY = "SELECT * FROM \"%s\"";
private static final Logger logger = Logger.getLogger(SQLiteTableReader.class.getName());
private Connection conn;
private PreparedStatement statement;
private ResultSet queryResults;
private ResultSetMetaData currentMetadata;
//Iteration state
private int currRowColumnIndex;
private int columnNameIndex;
private int totalColumnCount;
private boolean unfinishedRow;
private boolean liveResultSet;
private String prevTableName;
/**
* Holds reference to the builder instance so that we can use its actions
* during iteration.
*/
private SQLiteTableReader(Builder builder) {
this.builder = builder;
this.file = builder.file;
}
/**
* Fetches all table names from the database.
*
* @return List of all table names found while querying the sqlite_master
* table
*
* @throws SQLiteTableReaderException
*/
public List<String> getTableNames() throws SQLiteTableReaderException {
ensureOpen();
try (ResultSet tableNameResult = conn.createStatement()
.executeQuery("SELECT name FROM sqlite_master "
+ " WHERE type= 'table' ")) {
List<String> tableNames = new ArrayList<>();
while (tableNameResult.next()) {
tableNames.add(tableNameResult.getString("name")); //NON-NLS
}
return tableNames;
} catch (SQLException ex) {
throw new SQLiteTableReaderException(ex);
}
}
/**
* Fetches the row count.
*
* @param tableName Source table to count
*
* @return Count as an integer
*
* @throws SQLiteTableReaderException
*/
public int getRowCount(String tableName) throws SQLiteTableReaderException {
ensureOpen();
try (ResultSet countResult = conn.createStatement()
.executeQuery("SELECT count (*) as count FROM "
+ "\"" + tableName + "\"")) {
return countResult.getInt("count");
} catch (SQLException ex) {
throw new SQLiteTableReaderException(ex);
}
}
/**
* Fetches the column count of the table.
*
* @param tableName Source table to count
*
* @return Count as an integer
*
* @throws SQLiteTableReaderException
*/
public int getColumnCount(String tableName) throws SQLiteTableReaderException {
ensureOpen();
try (ResultSet columnCount = conn.createStatement()
.executeQuery(String.format(SELECT_ALL_QUERY, tableName))) {
return columnCount.getMetaData().getColumnCount();
} catch (SQLException ex) {
throw new SQLiteTableReaderException(ex);
}
}
/**
* Reads column names and values from the table. Only actions that were
* configured in the Builder will be invoked during iteration. Iteration
* will stop when the table read has completed or an exception was
* encountered.
*
* @param tableName Source table to read
*
* @throws SQLiteTableReaderException
*/
public void read(String tableName) throws SQLiteTableReaderException {
readHelper(String.format(SELECT_ALL_QUERY, tableName), () -> false);
}
/**
* Reads column names and values from the table. Only actions that were
* configured in the Builder will be invoked during iteration. Iteration
* will stop when the table read has completed or an exception was
* encountered.
*
* @param tableName Source table to perform a read
* @param limit Number of rows to read from the table
* @param offset Starting row to read from in the table
*
* @throws SQLiteTableReaderException
*
*/
public void read(String tableName, int limit, int offset) throws SQLiteTableReaderException {
readHelper(String.format(SELECT_ALL_QUERY, tableName) + " LIMIT " + limit
+ " OFFSET " + offset, () -> false);
}
/**
* Reads column names and values from the table. Iteration will stop when
* the condition is true.
*
* @param tableName Source table to perform a read
* @param condition Condition to stop iteration when true
*
* @throws SQLiteTableReaderException
*
*/
public void read(String tableName, BooleanSupplier condition) throws SQLiteTableReaderException {
if (Objects.isNull(prevTableName) || !prevTableName.equals(tableName)) {
prevTableName = tableName;
closeTableResources();
}
readHelper(String.format(SELECT_ALL_QUERY, tableName), condition);
}
/**
* Performs the result set iteration and is responsible for maintaining
* state of the read over multiple invocations.
*
* @throws SQLiteTableReaderException
*/
private void readHelper(String query, BooleanSupplier condition) throws SQLiteTableReaderException {
try {
if (!liveResultSet) {
openTableResources(query);
columnNameIndex = 0;
}
//Process column names before reading the database table values
while (columnNameIndex < totalColumnCount) {
if (condition.getAsBoolean()) {
return;
}
builder.onColumnNameAction.accept(currentMetadata
.getColumnName(++columnNameIndex));
}
while (unfinishedRow || queryResults.next()) {
while (currRowColumnIndex < totalColumnCount) {
if (condition.getAsBoolean()) {
unfinishedRow = true;
return;
}
Object item = queryResults.getObject(++currRowColumnIndex);
if (item instanceof String) {
builder.onStringAction.accept((String) item);
} else if (item instanceof Integer) {
builder.onIntegerAction.accept((Integer) item);
} else if (item instanceof Double) {
builder.onFloatAction.accept((Double) item);
} else if (item instanceof Long) {
builder.onLongAction.accept((Long) item);
} else if (item instanceof byte[]) {
builder.onBlobAction.accept((byte[]) item);
}
builder.forAllAction.accept(item);
}
unfinishedRow = false;
//Wrap column index back around if we've reached the end of the row
currRowColumnIndex = currRowColumnIndex % totalColumnCount;
}
closeTableResources();
} catch (SQLException ex) {
closeTableResources();
throw new SQLiteTableReaderException(ex);
}
}
/**
* Ensures that the underlying database connection is open. This entails
* copying the abstract file contents to temp directory, copying over any
* WAL or SHM files and getting the connection from the DriverManager.
*
* @throws SQLiteTableReaderException
*/
private void ensureOpen() throws SQLiteTableReaderException {
if (Objects.isNull(conn)) {
try {
Class.forName("org.sqlite.JDBC"); //NON-NLS
String localDiskPath = copyFileToTempDirectory(file, file.getId());
//Find and copy both WAL and SHM meta files
findAndCopySQLiteMetaFile(file, file.getName() + "-wal");
findAndCopySQLiteMetaFile(file, file.getName() + "-shm");
conn = DriverManager.getConnection("jdbc:sqlite:" + localDiskPath);
} catch (NoCurrentCaseException | TskCoreException | IOException
| ClassNotFoundException | SQLException ex) {
throw new SQLiteTableReaderException(ex);
}
}
}
/**
* Searches for a meta file associated with the give SQLite database. If
* found, it copies this file into the temp directory of the current case.
*
* @param sqliteFile file being processed
* @param metaFileName name of meta file to look for
*
* @throws NoCurrentCaseException Case has been closed.
* @throws TskCoreException fileManager cannot find AbstractFile
* files.
* @throws IOException Issue during writing to file.
*/
private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile,
String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException {
Case openCase = Case.getCurrentCaseThrows();
SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
Services services = new Services(sleuthkitCase);
FileManager fileManager = services.getFileManager();
List<AbstractFile> metaFiles = fileManager.findFiles(
sqliteFile.getDataSource(), metaFileName,
sqliteFile.getParent().getName());
if (metaFiles != null) {
for (AbstractFile metaFile : metaFiles) {
copyFileToTempDirectory(metaFile, sqliteFile.getId());
}
}
}
/**
* Copies the file contents into a unique path in the current case temp
* directory.
*
* @param file AbstractFile from the data source
* @param id The input files id value
*
* @return The path of the file on disk
*
* @throws IOException Exception writing file contents
* @throws NoCurrentCaseException Current case closed during file copying
*/
private String copyFileToTempDirectory(AbstractFile file, long fileId)
throws IOException, NoCurrentCaseException {
String localDiskPath = Case.getCurrentCaseThrows().getTempDirectory()
+ File.separator + fileId + file.getName();
File localDatabaseFile = new File(localDiskPath);
if (!localDatabaseFile.exists()) {
ContentUtils.writeToFile(file, localDatabaseFile);
}
return localDiskPath;
}
/**
* Executes the query and assigns resource references to instance variables.
*
* @param query Input query to execute
*
* @throws SQLiteTableReaderException
*/
private void openTableResources(String query) throws SQLiteTableReaderException {
try {
ensureOpen();
statement = conn.prepareStatement(query);
queryResults = statement.executeQuery();
currentMetadata = queryResults.getMetaData();
totalColumnCount = currentMetadata.getColumnCount();
liveResultSet = true;
} catch (SQLException ex) {
throw new SQLiteTableReaderException(ex);
}
}
/**
* Ensures both the statement and the result set for a table are closed.
*/
private void closeTableResources() {
try {
if (Objects.nonNull(statement)) {
statement.close();
}
if (Objects.nonNull(queryResults)) {
queryResults.close();
}
liveResultSet = false;
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Failed to close table resources", ex);
}
}
/**
* Closes all resources attached to the database file.
*
* @throws SQLiteTableReaderException
*/
@Override
public void close() throws SQLiteTableReaderException {
try {
if (Objects.nonNull(conn)) {
conn.close();
}
} catch (SQLException ex) {
throw new SQLiteTableReaderException(ex);
}
}
/**
* Provides status of the current read operation.
*
* @return
*/
public boolean isFinished() {
return !liveResultSet;
}
/**
* Last ditch effort to close the connections during garbage collection.
*
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
try {
close();
} catch (SQLiteTableReaderException ex) {
logger.log(Level.SEVERE, "Failed to close reader in finalizer", ex);
}
super.finalize();
}
}

View File

@ -0,0 +1,44 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2018 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.coreutils;
/**
* Provides a system exception for the SQLiteTableReader class.
*/
public class SQLiteTableReaderException extends Exception {
/**
* Accepts both a message and a parent exception.
*
* @param msg Message detailing the cause
* @param parentEx Parent exception
*/
public SQLiteTableReaderException(String msg, Throwable parentEx) {
super(msg, parentEx);
}
/**
* Accepts only a parent exception.
*
* @param parentEx Parent exception
*/
public SQLiteTableReaderException(Throwable parentEx) {
super(parentEx);
}
}

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2016 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -20,7 +20,13 @@ package org.sleuthkit.autopsy.coreutils;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.List;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
/** /**
* Utility methods for workig with time zones. * Utility methods for workig with time zones.
@ -41,7 +47,7 @@ public class TimeZoneUtils {
java.util.TimeZone zone = java.util.TimeZone.getTimeZone(timeZoneId); java.util.TimeZone zone = java.util.TimeZone.getTimeZone(timeZoneId);
int offset = zone.getRawOffset() / 1000; int offset = zone.getRawOffset() / 1000;
int hour = offset / 3600; int hour = offset / 3600;
int min = (offset % 3600) / 60; int min = Math.abs((offset % 3600) / 60);
DateFormat dfm = new SimpleDateFormat("z"); DateFormat dfm = new SimpleDateFormat("z");
dfm.setTimeZone(zone); dfm.setTimeZone(zone);
@ -59,6 +65,73 @@ public class TimeZoneUtils {
return result; return result;
} }
/**
* Generate a time zone string containing the GMT offset and ID.
*
* @param timeZone The time zone.
*
* @return The time zone string.
*/
public static String createTimeZoneString(TimeZone timeZone) {
int offset = timeZone.getRawOffset() / 1000;
int hour = offset / 3600;
int minutes = Math.abs((offset % 3600) / 60);
return String.format("(GMT%+d:%02d) %s", hour, minutes, timeZone.getID()); //NON-NLS
}
/**
* Generates a list of time zones.
*/
public static List<String> createTimeZoneList() {
/*
* Create a list of time zones.
*/
List<TimeZone> timeZoneList = new ArrayList<>();
String[] ids = SimpleTimeZone.getAvailableIDs();
for (String id : ids) {
/*
* DateFormat dfm = new SimpleDateFormat("z");
* dfm.setTimeZone(zone); boolean hasDaylight =
* zone.useDaylightTime(); String first = dfm.format(new Date(2010,
* 1, 1)); String second = dfm.format(new Date(2011, 6, 6)); int mid
* = hour * -1; String result = first + Integer.toString(mid);
* if(hasDaylight){ result = result + second; }
* timeZoneComboBox.addItem(item + " (" + result + ")");
*/
timeZoneList.add(TimeZone.getTimeZone(id));
}
/*
* Sort the list of time zones first by offset, then by ID.
*/
Collections.sort(timeZoneList, new Comparator<TimeZone>(){
@Override
public int compare(TimeZone o1, TimeZone o2){
int offsetDelta = Integer.compare(o1.getRawOffset(), o2.getRawOffset());
if (offsetDelta == 0) {
return o1.getID().compareToIgnoreCase(o2.getID());
}
return offsetDelta;
}
});
/*
* Create a list of Strings encompassing both the GMT offset and the
* time zone ID.
*/
List<String> outputList = new ArrayList<>();
for (TimeZone timeZone : timeZoneList) {
outputList.add(createTimeZoneString(timeZone));
}
return outputList;
}
/** /**
* Prevents instantiation. * Prevents instantiation.

View File

@ -18,16 +18,22 @@
*/ */
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
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.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumSet; import java.util.EnumSet;
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.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
@ -43,13 +49,18 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*;
import static org.sleuthkit.autopsy.datamodel.Bundle.*; import static org.sleuthkit.autopsy.datamodel.Bundle.*;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus; import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent; import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
import org.sleuthkit.autopsy.texttranslation.TranslationException;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
@ -71,6 +82,9 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE, private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE,
Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CR_COMMENT_CHANGED); Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CR_COMMENT_CHANGED);
private static final ExecutorService translationPool;
private static final Integer MAX_POOL_SIZE = 10;
/** /**
* @param abstractFile file to wrap * @param abstractFile file to wrap
*/ */
@ -90,6 +104,13 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl); Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
} }
static {
//Initialize this pool only once! This will be used by every instance of AAFN
//to do their heavy duty SCO column and translation updates.
translationPool = Executors.newFixedThreadPool(MAX_POOL_SIZE,
new ThreadFactoryBuilder().setNameFormat("translation-task-thread-%d").build());
}
/** /**
* The finalizer removes event listeners as the BlackboardArtifactNode is * The finalizer removes event listeners as the BlackboardArtifactNode is
* being garbage collected. Yes, we know that finalizers are considered to * being garbage collected. Yes, we know that finalizers are considered to
@ -110,6 +131,16 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl); Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
} }
/**
* Event signals to indicate the background tasks have completed processing.
* Currently, we have one property task in the background:
*
* 1) Retreiving the translation of the file name
*/
enum NodeSpecificEvents {
TRANSLATION_AVAILABLE,
}
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
@ -146,24 +177,51 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
// case was closed. Remove listeners so that we don't get called with a stale case handle // case was closed. Remove listeners so that we don't get called with a stale case handle
removeListeners(); removeListeners();
} }
/*
* No need to do any asynchrony around tag added, deleted or CR
* change events, they are so infrequent and user driven that we can
* just keep a simple blocking approach, where we go out to the
* database ourselves.
*/
} else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) { } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
ContentTagAddedEvent event = (ContentTagAddedEvent) evt; ContentTagAddedEvent event = (ContentTagAddedEvent) evt;
if (event.getAddedTag().getContent().equals(content)) { if (event.getAddedTag().getContent().equals(content)) {
updateSheet(); List<ContentTag> tags = getContentTagsFromDatabase();
Pair<Score, String> scorePropAndDescr = getScorePropertyAndDescription(tags);
Score value = scorePropAndDescr.getLeft();
String descr = scorePropAndDescr.getRight();
CorrelationAttributeInstance attribute = getCorrelationAttributeInstance();
updateSheet(new NodeProperty<>(SCORE.toString(),SCORE.toString(),descr,value),
new NodeProperty<>(COMMENT.toString(),COMMENT.toString(),NO_DESCR,getCommentProperty(tags, attribute))
);
} }
} else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) { } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt; ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt;
if (event.getDeletedTagInfo().getContentID() == content.getId()) { if (event.getDeletedTagInfo().getContentID() == content.getId()) {
updateSheet(); List<ContentTag> tags = getContentTagsFromDatabase();
Pair<Score, String> scorePropAndDescr = getScorePropertyAndDescription(tags);
Score value = scorePropAndDescr.getLeft();
String descr = scorePropAndDescr.getRight();
CorrelationAttributeInstance attribute = getCorrelationAttributeInstance();
updateSheet(new NodeProperty<>(SCORE.toString(), SCORE.toString(),descr,value),
new NodeProperty<>(COMMENT.toString(), COMMENT.toString(),NO_DESCR,getCommentProperty(tags, attribute))
);
} }
} else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) { } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
CommentChangedEvent event = (CommentChangedEvent) evt; CommentChangedEvent event = (CommentChangedEvent) evt;
if (event.getContentID() == content.getId()) { if (event.getContentID() == content.getId()) {
updateSheet(); List<ContentTag> tags = getContentTagsFromDatabase();
CorrelationAttributeInstance attribute = getCorrelationAttributeInstance();
updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(),NO_DESCR,getCommentProperty(tags, attribute)));
} }
/*
* Data that was being computed in the background task. Kicked off by a
* call to createSheet().
*/
} else if (eventType.equals(NodeSpecificEvents.TRANSLATION_AVAILABLE.toString())) {
updateSheet(new NodeProperty<>(TRANSLATION.toString(),TRANSLATION.toString(),NO_DESCR,evt.getNewValue()));
} }
}; };
/** /**
* We pass a weak reference wrapper around the listener to the event * We pass a weak reference wrapper around the listener to the event
* publisher. This allows Netbeans to delete the node when the user * publisher. This allows Netbeans to delete the node when the user
@ -174,11 +232,70 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
*/ */
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null); private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
private void updateSheet() { /**
this.setSheet(createSheet()); * Updates the values of the properties in the current property sheet with
* the new properties being passed in. Only if that property exists in the
* current sheet will it be applied. That way, we allow for subclasses to
* add their own (or omit some!) properties and we will not accidentally
* disrupt their UI.
*
* Race condition if not synchronized. Only one update should be applied at
* a time.
*
* @param newProps New file property instances to be updated in the current
* sheet.
*/
private synchronized void updateSheet(NodeProperty<?>... newProps) {
//Refresh ONLY those properties in the sheet currently. Subclasses may have
//only added a subset of our properties or their own props. Let's keep their UI correct.
Sheet visibleSheet = this.getSheet();
Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES);
Property<?>[] visibleProps = visibleSheetSet.getProperties();
for(NodeProperty<?> newProp: newProps) {
for(int i = 0; i < visibleProps.length; i++) {
if(visibleProps[i].getName().equals(newProp.getName())) {
visibleProps[i] = newProp;
}
}
}
visibleSheetSet.put(visibleProps);
visibleSheet.put(visibleSheetSet);
//setSheet() will notify Netbeans to update this node in the UI.
this.setSheet(visibleSheet);
}
/*
* This is called when the node is first initialized. Any new updates or
* changes happen by directly manipulating the sheet. That means we can fire
* off background events everytime this method is called and not worry about
* duplicated jobs.
*/
@Override
protected synchronized Sheet createSheet() {
Sheet sheet = new Sheet();
Sheet.Set sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
//This will fire off fresh background tasks.
List<NodeProperty<?>> newProperties = getProperties();
newProperties.forEach((property) -> {
sheetSet.put(property);
});
/*
* Submit the translation task ASAP. Keep all weak references so
* this task doesn't block the ability of this node to be GC'd.
*/
translationPool.submit(new TranslationTask(new WeakReference<>(this), weakPcl));
return sheet;
} }
@NbBundle.Messages({"AbstractAbstractFileNode.nameColLbl=Name", @NbBundle.Messages({"AbstractAbstractFileNode.nameColLbl=Name",
"AbstractAbstractFileNode.translateFileName=Translated Name",
"AbstractAbstractFileNode.createSheet.score.name=S",
"AbstractAbstractFileNode.createSheet.comment.name=C",
"AbstractAbstractFileNode.createSheet.count.name=O",
"AbstractAbstractFileNode.locationColLbl=Location", "AbstractAbstractFileNode.locationColLbl=Location",
"AbstractAbstractFileNode.modifiedTimeColLbl=Modified Time", "AbstractAbstractFileNode.modifiedTimeColLbl=Modified Time",
"AbstractAbstractFileNode.changeTimeColLbl=Change Time", "AbstractAbstractFileNode.changeTimeColLbl=Change Time",
@ -202,6 +319,10 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
public enum AbstractFilePropertyType { public enum AbstractFilePropertyType {
NAME(AbstractAbstractFileNode_nameColLbl()), NAME(AbstractAbstractFileNode_nameColLbl()),
TRANSLATION(AbstractAbstractFileNode_translateFileName()),
SCORE(AbstractAbstractFileNode_createSheet_score_name()),
COMMENT(AbstractAbstractFileNode_createSheet_comment_name()),
OCCURRENCES(AbstractAbstractFileNode_createSheet_count_name()),
LOCATION(AbstractAbstractFileNode_locationColLbl()), LOCATION(AbstractAbstractFileNode_locationColLbl()),
MOD_TIME(AbstractAbstractFileNode_modifiedTimeColLbl()), MOD_TIME(AbstractAbstractFileNode_modifiedTimeColLbl()),
CHANGED_TIME(AbstractAbstractFileNode_changeTimeColLbl()), CHANGED_TIME(AbstractAbstractFileNode_changeTimeColLbl()),
@ -235,12 +356,270 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
} }
} }
/**
* Creates and populates a list of properties for this nodes property sheet.
*/
private List<NodeProperty<?>> getProperties() {
List<NodeProperty<?>> properties = new ArrayList<>();
properties.add(new NodeProperty<>(NAME.toString(), NAME.toString(), NO_DESCR, getContentDisplayName(content)));
/*
* Initialize an empty place holder value. At the bottom, we kick off a
* background task that promises to update these values.
*/
if (UserPreferences.displayTranslatedFileNames()) {
properties.add(new NodeProperty<>(TRANSLATION.toString(), TRANSLATION.toString(), NO_DESCR, ""));
}
//SCO column prereq info..
List<ContentTag> tags = getContentTagsFromDatabase();
CorrelationAttributeInstance attribute = getCorrelationAttributeInstance();
Pair<DataResultViewerTable.Score, String> scoreAndDescription = getScorePropertyAndDescription(tags);
properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
DataResultViewerTable.HasCommentStatus comment = getCommentProperty(tags, attribute);
properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, comment));
if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute);
properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), countAndDescription.getRight(), countAndDescription.getLeft()));
}
properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content)));
properties.add(new NodeProperty<>(MOD_TIME.toString(), MOD_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getMtime(), content)));
properties.add(new NodeProperty<>(CHANGED_TIME.toString(), CHANGED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCtime(), content)));
properties.add(new NodeProperty<>(ACCESS_TIME.toString(), ACCESS_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getAtime(), content)));
properties.add(new NodeProperty<>(CREATED_TIME.toString(), CREATED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCrtime(), content)));
properties.add(new NodeProperty<>(SIZE.toString(), SIZE.toString(), NO_DESCR, content.getSize()));
properties.add(new NodeProperty<>(FLAGS_DIR.toString(), FLAGS_DIR.toString(), NO_DESCR, content.getDirFlagAsString()));
properties.add(new NodeProperty<>(FLAGS_META.toString(), FLAGS_META.toString(), NO_DESCR, content.getMetaFlagsAsString()));
properties.add(new NodeProperty<>(MODE.toString(), MODE.toString(), NO_DESCR, content.getModesAsString()));
properties.add(new NodeProperty<>(USER_ID.toString(), USER_ID.toString(), NO_DESCR, content.getUid()));
properties.add(new NodeProperty<>(GROUP_ID.toString(), GROUP_ID.toString(), NO_DESCR, content.getGid()));
properties.add(new NodeProperty<>(META_ADDR.toString(), META_ADDR.toString(), NO_DESCR, content.getMetaAddr()));
properties.add(new NodeProperty<>(ATTR_ADDR.toString(), ATTR_ADDR.toString(), NO_DESCR, content.getAttrType().getValue() + "-" + content.getAttributeId()));
properties.add(new NodeProperty<>(TYPE_DIR.toString(), TYPE_DIR.toString(), NO_DESCR, content.getDirType().getLabel()));
properties.add(new NodeProperty<>(TYPE_META.toString(), TYPE_META.toString(), NO_DESCR, content.getMetaType().toString()));
properties.add(new NodeProperty<>(KNOWN.toString(), KNOWN.toString(), NO_DESCR, content.getKnown().getName()));
properties.add(new NodeProperty<>(MD5HASH.toString(), MD5HASH.toString(), NO_DESCR, StringUtils.defaultString(content.getMd5Hash())));
properties.add(new NodeProperty<>(ObjectID.toString(), ObjectID.toString(), NO_DESCR, content.getId()));
properties.add(new NodeProperty<>(MIMETYPE.toString(), MIMETYPE.toString(), NO_DESCR, StringUtils.defaultString(content.getMIMEType())));
properties.add(new NodeProperty<>(EXTENSION.toString(), EXTENSION.toString(), NO_DESCR, content.getNameExtension()));
return properties;
}
/**
* Used by subclasses of AbstractAbstractFileNode to add the tags property
* to their sheets.
*
* @param sheetSet the modifiable Sheet.Set returned by
* Sheet.get(Sheet.PROPERTIES)
*
* @deprecated
*/
@NbBundle.Messages("AbstractAbstractFileNode.tagsProperty.displayName=Tags")
@Deprecated
protected void addTagProperty(Sheet.Set sheetSet) {
List<ContentTag> tags = getContentTagsFromDatabase();
sheetSet.put(new NodeProperty<>("Tags", AbstractAbstractFileNode_tagsProperty_displayName(),
NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName())
.distinct()
.collect(Collectors.joining(", "))));
}
/**
* Gets a comma-separated values list of the names of the hash sets
* currently identified as including a given file.
*
* @param file The file.
*
* @return The CSV list of hash set names.
*
* @deprecated
*/
@Deprecated
protected static String getHashSetHitsCsvList(AbstractFile file) {
try {
return StringUtils.join(file.getHashSetNames(), ", ");
} catch (TskCoreException tskCoreException) {
logger.log(Level.WARNING, "Error getting hashset hits: ", tskCoreException); //NON-NLS
return "";
}
}
@NbBundle.Messages({
"AbstractAbstractFileNode.createSheet.count.displayName=O",
"AbstractAbstractFileNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated",
"AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated",
"# {0} - occuranceCount",
"AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting
String description = Bundle.AbstractAbstractFileNode_createSheet_count_noCentralRepo_description();
try {
//don't perform the query if there is no correlation value
if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attribute.getCorrelationType(), attribute.getCorrelationValue());
description = Bundle.AbstractAbstractFileNode_createSheet_count_description(count);
} else if (attribute != null) {
description = Bundle.AbstractAbstractFileNode_createSheet_count_hashLookupNotRun_description();
}
} catch (EamDbException ex) {
logger.log(Level.WARNING, "Error getting count of datasources with correlation attribute", ex);
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex);
}
return Pair.of(count, description);
}
@NbBundle.Messages({
"AbstractAbstractFileNode.createSheet.score.displayName=S",
"AbstractAbstractFileNode.createSheet.notableFile.description=File recognized as notable.",
"AbstractAbstractFileNode.createSheet.interestingResult.description=File has interesting result associated with it.",
"AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.",
"AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag.",
"AbstractAbstractFileNode.createSheet.noScore.description=No score"})
Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<ContentTag> tags) {
DataResultViewerTable.Score score = DataResultViewerTable.Score.NO_SCORE;
String description = "";
if (content.getKnown() == TskData.FileKnown.BAD) {
score = DataResultViewerTable.Score.NOTABLE_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_notableFile_description();
}
try {
if (score == DataResultViewerTable.Score.NO_SCORE && !content.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT).isEmpty()) {
score = DataResultViewerTable.Score.INTERESTING_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_interestingResult_description();
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting artifacts for file: " + content.getName(), ex);
}
if (!tags.isEmpty() && (score == DataResultViewerTable.Score.NO_SCORE || score == DataResultViewerTable.Score.INTERESTING_SCORE)) {
score = DataResultViewerTable.Score.INTERESTING_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_taggedFile_description();
for (ContentTag tag : tags) {
if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
score = DataResultViewerTable.Score.NOTABLE_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_notableTaggedFile_description();
break;
}
}
}
return Pair.of(score, description);
}
@NbBundle.Messages({
"AbstractAbstractFileNode.createSheet.comment.displayName=C"})
HasCommentStatus getCommentProperty(List<ContentTag> tags, CorrelationAttributeInstance attribute) {
DataResultViewerTable.HasCommentStatus status = !tags.isEmpty() ? DataResultViewerTable.HasCommentStatus.TAG_NO_COMMENT : DataResultViewerTable.HasCommentStatus.NO_COMMENT;
for (ContentTag tag : tags) {
if (!StringUtils.isBlank(tag.getComment())) {
//if the tag is null or empty or contains just white space it will indicate there is not a comment
status = DataResultViewerTable.HasCommentStatus.TAG_COMMENT;
break;
}
}
if (attribute != null && !StringUtils.isBlank(attribute.getComment())) {
if (status == DataResultViewerTable.HasCommentStatus.TAG_COMMENT) {
status = DataResultViewerTable.HasCommentStatus.CR_AND_TAG_COMMENTS;
} else {
status = DataResultViewerTable.HasCommentStatus.CR_COMMENT;
}
}
return status;
}
/**
* Translates this nodes content name. Doesn't attempt translation if
* the name is in english or if there is now translation service available.
*/
String getTranslatedFileName() {
//If already in complete English, don't translate.
if (content.getName().matches("^\\p{ASCII}+$")) {
return "";
}
TextTranslationService tts = TextTranslationService.getInstance();
if (tts.hasProvider()) {
//Seperate out the base and ext from the contents file name.
String base = FilenameUtils.getBaseName(content.getName());
try {
String translation = tts.translate(base);
String ext = FilenameUtils.getExtension(content.getName());
//If we have no extension, then we shouldn't add the .
String extensionDelimiter = (ext.isEmpty()) ? "" : ".";
//Talk directly to this nodes pcl, fire an update when the translation
//is complete.
if (!translation.isEmpty()) {
return translation + extensionDelimiter + ext;
}
} catch (NoServiceProviderException noServiceEx) {
logger.log(Level.WARNING, "Translate unsuccessful because no TextTranslator "
+ "implementation was provided.", noServiceEx.getMessage());
} catch (TranslationException noTranslationEx) {
logger.log(Level.WARNING, "Could not successfully translate file name "
+ content.getName(), noTranslationEx.getMessage());
}
}
return "";
}
/**
* Get all tags from the case database that are associated with the file
*
* @return a list of tags that are associated with the file
*/
List<ContentTag> getContentTagsFromDatabase() {
List<ContentTag> tags = new ArrayList<>();
try {
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(content));
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Failed to get tags for content " + content.getName(), ex);
}
return tags;
}
CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance attribute = null;
if (EamDbUtil.useCentralRepo()) {
attribute = EamArtifactUtil.getInstanceFromContent(content);
}
return attribute;
}
static String getContentPath(AbstractFile file) {
try {
return file.getUniquePath();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Except while calling Content.getUniquePath() on " + file.getName(), ex); //NON-NLS
return ""; //NON-NLS
}
}
static String getContentDisplayName(AbstractFile file) {
String name = file.getName();
switch (name) {
case "..":
return DirectoryNode.DOTDOTDIR;
case ".":
return DirectoryNode.DOTDIR;
default:
return name;
}
}
/** /**
* Fill map with AbstractFile properties * Fill map with AbstractFile properties
* *
* @param map map with preserved ordering, where property names/values * @param map map with preserved ordering, where property names/values
* are put * are put
* @param content The content to get properties for. * @param content The content to get properties for.
*
* TODO JIRA-4421: Deprecate this method and resolve warnings that appear
* in other locations.
*/ */
static public void fillPropertyMap(Map<String, Object> map, AbstractFile content) { static public void fillPropertyMap(Map<String, Object> map, AbstractFile content) {
map.put(NAME.toString(), getContentDisplayName(content)); map.put(NAME.toString(), getContentDisplayName(content));
@ -265,196 +644,4 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
map.put(MIMETYPE.toString(), StringUtils.defaultString(content.getMIMEType())); map.put(MIMETYPE.toString(), StringUtils.defaultString(content.getMIMEType()));
map.put(EXTENSION.toString(), content.getNameExtension()); map.put(EXTENSION.toString(), content.getNameExtension());
} }
}
/**
* Get all tags from the case database that are associated with the file
*
* @return a list of tags that are associated with the file
*/
protected final List<ContentTag> getContentTagsFromDatabase() {
List<ContentTag> tags = new ArrayList<>();
try {
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(content));
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Failed to get tags for content " + content.getName(), ex);
}
return tags;
}
protected final CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance correlationAttribute = null;
if (EamDbUtil.useCentralRepo()) {
correlationAttribute = EamArtifactUtil.getInstanceFromContent(content);
}
return correlationAttribute;
}
/**
* Used by subclasses of AbstractAbstractFileNode to add the comment
* property to their sheets.
*
* @param sheetSet the modifiable Sheet.Set returned by
* Sheet.get(Sheet.PROPERTIES)
* @param tags the list of tags associated with the file
* @param attribute the correlation attribute associated with this file,
* null if central repo is not enabled
*/
@NbBundle.Messages({"AbstractAbstractFileNode.createSheet.comment.name=C",
"AbstractAbstractFileNode.createSheet.comment.displayName=C"})
protected final void addCommentProperty(Sheet.Set sheetSet, List<ContentTag> tags, CorrelationAttributeInstance attribute) {
HasCommentStatus status = tags.size() > 0 ? HasCommentStatus.TAG_NO_COMMENT : HasCommentStatus.NO_COMMENT;
for (ContentTag tag : tags) {
if (!StringUtils.isBlank(tag.getComment())) {
//if the tag is null or empty or contains just white space it will indicate there is not a comment
status = HasCommentStatus.TAG_COMMENT;
break;
}
}
if (attribute != null && !StringUtils.isBlank(attribute.getComment())) {
if (status == HasCommentStatus.TAG_COMMENT) {
status = HasCommentStatus.CR_AND_TAG_COMMENTS;
} else {
status = HasCommentStatus.CR_COMMENT;
}
}
sheetSet.put(new NodeProperty<>(AbstractAbstractFileNode_createSheet_comment_name(), AbstractAbstractFileNode_createSheet_comment_displayName(), NO_DESCR,
status));
}
/**
* Used by subclasses of AbstractAbstractFileNode to add the Score property
* to their sheets.
*
* @param sheetSet the modifiable Sheet.Set returned by
* Sheet.get(Sheet.PROPERTIES)
* @param tags the list of tags associated with the file
*/
@NbBundle.Messages({"AbstractAbstractFileNode.createSheet.score.name=S",
"AbstractAbstractFileNode.createSheet.score.displayName=S",
"AbstractAbstractFileNode.createSheet.notableFile.description=File recognized as notable.",
"AbstractAbstractFileNode.createSheet.interestingResult.description=File has interesting result associated with it.",
"AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.",
"AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag.",
"AbstractAbstractFileNode.createSheet.noScore.description=No score"})
protected final void addScoreProperty(Sheet.Set sheetSet, List<ContentTag> tags) {
Score score = Score.NO_SCORE;
String description = Bundle.AbstractAbstractFileNode_createSheet_noScore_description();
if (content.getKnown() == TskData.FileKnown.BAD) {
score = Score.NOTABLE_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_notableFile_description();
}
try {
if (score == Score.NO_SCORE && !content.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT).isEmpty()) {
score = Score.INTERESTING_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_interestingResult_description();
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting artifacts for file: " + content.getName(), ex);
}
if (tags.size() > 0 && (score == Score.NO_SCORE || score == Score.INTERESTING_SCORE)) {
score = Score.INTERESTING_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_taggedFile_description();
for (ContentTag tag : tags) {
if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
score = Score.NOTABLE_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_notableTaggedFile_description();
break;
}
}
}
sheetSet.put(new NodeProperty<>(Bundle.AbstractAbstractFileNode_createSheet_score_name(), Bundle.AbstractAbstractFileNode_createSheet_score_displayName(), description, score));
}
@NbBundle.Messages({"AbstractAbstractFileNode.createSheet.count.name=O",
"AbstractAbstractFileNode.createSheet.count.displayName=O",
"AbstractAbstractFileNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated",
"AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated",
"# {0} - occuranceCount",
"AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting
String description = Bundle.AbstractAbstractFileNode_createSheet_count_noCentralRepo_description();
try {
//don't perform the query if there is no correlation value
if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attribute.getCorrelationType(), attribute.getCorrelationValue());
description = Bundle.AbstractAbstractFileNode_createSheet_count_description(count);
} else if (attribute != null) {
description = Bundle.AbstractAbstractFileNode_createSheet_count_hashLookupNotRun_description();
}
} catch (EamDbException ex) {
logger.log(Level.WARNING, "Error getting count of datasources with correlation attribute", ex);
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex);
}
sheetSet.put(
new NodeProperty<>(Bundle.AbstractAbstractFileNode_createSheet_count_name(), Bundle.AbstractAbstractFileNode_createSheet_count_displayName(), description, count));
}
/**
* Used by subclasses of AbstractAbstractFileNode to add the tags property
* to their sheets.
*
* @param sheetSet the modifiable Sheet.Set returned by
* Sheet.get(Sheet.PROPERTIES)
* @deprecated
*/
@NbBundle.Messages("AbstractAbstractFileNode.tagsProperty.displayName=Tags")
@Deprecated
protected void addTagProperty(Sheet.Set sheetSet) {
List<ContentTag> tags = new ArrayList<>();
try {
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(content));
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Failed to get tags for content " + content.getName(), ex);
}
sheetSet.put(new NodeProperty<>("Tags", AbstractAbstractFileNode_tagsProperty_displayName(),
NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName())
.distinct()
.collect(Collectors.joining(", "))));
}
private static String getContentPath(AbstractFile file) {
try {
return file.getUniquePath();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Except while calling Content.getUniquePath() on " + file, ex); //NON-NLS
return ""; //NON-NLS
}
}
static String getContentDisplayName(AbstractFile file) {
String name = file.getName();
switch (name) {
case "..":
return DirectoryNode.DOTDOTDIR;
case ".":
return DirectoryNode.DOTDIR;
default:
return name;
}
}
/**
* Gets a comma-separated values list of the names of the hash sets
* currently identified as including a given file.
*
* @param file The file.
*
* @return The CSV list of hash set names.
* @deprecated
*/
@Deprecated
protected static String getHashSetHitsCsvList(AbstractFile file) {
try {
return StringUtils.join(file.getHashSetNames(), ", ");
} catch (TskCoreException tskCoreException) {
logger.log(Level.WARNING, "Error getting hashset hits: ", tskCoreException); //NON-NLS
return "";
}
}
}

View File

@ -18,17 +18,10 @@
*/ */
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.ContentTag;
/** /**
* Abstract class that implements the commonality between File and Directory * Abstract class that implements the commonality between File and Directory
@ -58,7 +51,7 @@ public abstract class AbstractFsContentNode<T extends AbstractFile> extends Abst
*/ */
AbstractFsContentNode(T content, boolean directoryBrowseMode) { AbstractFsContentNode(T content, boolean directoryBrowseMode) {
super(content); super(content);
this.setDisplayName(AbstractAbstractFileNode.getContentDisplayName(content)); this.setDisplayName(getContentDisplayName(content));
this.directoryBrowseMode = directoryBrowseMode; this.directoryBrowseMode = directoryBrowseMode;
} }
@ -71,37 +64,6 @@ public abstract class AbstractFsContentNode<T extends AbstractFile> extends Abst
protected Sheet createSheet() { protected Sheet createSheet() {
Sheet sheet = super.createSheet(); Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
List<ContentTag> tags = getContentTagsFromDatabase();
Map<String, Object> map = new LinkedHashMap<>();
fillPropertyMap(map, getContent());
final String NO_DESCR = Bundle.AbstractFsContentNode_noDesc_text();
//add the name property before the comment property to ensure it is first column
sheetSet.put(new NodeProperty<>(AbstractFilePropertyType.NAME.toString(),
AbstractFilePropertyType.NAME.toString(),
NO_DESCR,
getName()));
addScoreProperty(sheetSet, tags);
//add the comment property before the propertyMap to ensure it is early in column order
CorrelationAttributeInstance correlationAttribute = null;
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences()== false) {
correlationAttribute = getCorrelationAttributeInstance();
}
addCommentProperty(sheetSet, tags, correlationAttribute);
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences()== false) {
addCountProperty(sheetSet, correlationAttribute);
}
for (AbstractFilePropertyType propType : AbstractFilePropertyType.values()) {
final String propString = propType.toString();
sheetSet.put(new NodeProperty<>(propString, propString, NO_DESCR, map.get(propString)));
}
if (directoryBrowseMode) { if (directoryBrowseMode) {
sheetSet.put(new NodeProperty<>(HIDE_PARENT, HIDE_PARENT, HIDE_PARENT, HIDE_PARENT)); sheetSet.put(new NodeProperty<>(HIDE_PARENT, HIDE_PARENT, HIDE_PARENT, HIDE_PARENT));
} }

View File

@ -320,7 +320,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} }
return srcName; return srcName;
} }
@NbBundle.Messages({ @NbBundle.Messages({
"BlackboardArtifactNode.createSheet.artifactType.displayName=Artifact Type", "BlackboardArtifactNode.createSheet.artifactType.displayName=Artifact Type",
"BlackboardArtifactNode.createSheet.artifactType.name=Artifact Type", "BlackboardArtifactNode.createSheet.artifactType.name=Artifact Type",

View File

@ -145,7 +145,7 @@ public final class ContentUtils {
try { try {
if (!shouldDisplayTimesInLocalTime()) { if (!shouldDisplayTimesInLocalTime()) {
return TimeZone.getTimeZone("GMT"); return TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays());
} else { } else {
final Content dataSource = content.getDataSource(); final Content dataSource = content.getDataSource();
if ((dataSource != null) && (dataSource instanceof Image)) { if ((dataSource != null) && (dataSource instanceof Image)) {

View File

@ -157,6 +157,8 @@ public class ExtractedContent implements AutopsyVisitableItem {
return filePath + "drive_network.png"; //NON-NLS return filePath + "drive_network.png"; //NON-NLS
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_FACE_DETECTED.getTypeID()) { } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_FACE_DETECTED.getTypeID()) {
return filePath + "face.png"; //NON-NLS return filePath + "face.png"; //NON-NLS
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID()) {
return filePath + "network-wifi.png"; //NON-NLS
} }
return filePath + "artifact-icon.png"; //NON-NLS return filePath + "artifact-icon.png"; //NON-NLS
} }

View File

@ -22,24 +22,18 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.swing.Action; import javax.swing.Action;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.Utilities; import org.openide.util.Utilities;
import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -75,45 +69,6 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
} }
} }
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
List<ContentTag> tags = getContentTagsFromDatabase();
Map<String, Object> map = new LinkedHashMap<>();
fillPropertyMap(map);
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "LayoutFileNode.createSheet.name.name"),
NbBundle.getMessage(this.getClass(), "LayoutFileNode.createSheet.name.displayName"),
NbBundle.getMessage(this.getClass(), "LayoutFileNode.createSheet.name.desc"),
getName()));
addScoreProperty(sheetSet, tags);
CorrelationAttributeInstance correlationAttribute = null;
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
correlationAttribute = getCorrelationAttributeInstance();
}
addCommentProperty(sheetSet, tags, correlationAttribute);
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
addCountProperty(sheetSet, correlationAttribute);
}
final String NO_DESCR = NbBundle.getMessage(this.getClass(), "LayoutFileNode.createSheet.noDescr.text");
for (Map.Entry<String, Object> entry : map.entrySet()) {
sheetSet.put(new NodeProperty<>(entry.getKey(), entry.getKey(), NO_DESCR, entry.getValue()));
}
return sheet;
}
@Override
public <T> T accept(ContentNodeVisitor<T> visitor) { public <T> T accept(ContentNodeVisitor<T> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }
@ -151,10 +106,6 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
return actionsList.toArray(new Action[actionsList.size()]); return actionsList.toArray(new Action[actionsList.size()]);
} }
void fillPropertyMap(Map<String, Object> map) {
AbstractAbstractFileNode.fillPropertyMap(map, getContent());
}
@Override @Override
public String getItemType() { public String getItemType() {
return getClass().getName(); return getClass().getName();

View File

@ -18,15 +18,6 @@
*/ */
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.LocalDirectory; import org.sleuthkit.datamodel.LocalDirectory;
/** /**
@ -46,51 +37,6 @@ public class LocalDirectoryNode extends SpecialDirectoryNode {
} }
@Override
@NbBundle.Messages({
"LocalDirectoryNode.createSheet.name.name=Name",
"LocalDirectoryNode.createSheet.name.displayName=Name",
"LocalDirectoryNode.createSheet.name.desc=no description",
"LocalDirectoryNode.createSheet.noDesc=no description"})
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
List<ContentTag> tags = getContentTagsFromDatabase();
sheetSet.put(new NodeProperty<>(Bundle.LocalDirectoryNode_createSheet_name_name(),
Bundle.LocalDirectoryNode_createSheet_name_displayName(),
Bundle.LocalDirectoryNode_createSheet_name_desc(),
getName()));
addScoreProperty(sheetSet, tags);
CorrelationAttributeInstance correlationAttribute = null;
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
correlationAttribute = getCorrelationAttributeInstance();
}
addCommentProperty(sheetSet, tags, correlationAttribute);
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
addCountProperty(sheetSet, correlationAttribute);
}
// At present, a LocalDirectory will never be a datasource - the top level of a logical
// file set is a VirtualDirectory
Map<String, Object> map = new LinkedHashMap<>();
fillPropertyMap(map, getContent());
final String NO_DESCR = Bundle.LocalDirectoryNode_createSheet_noDesc();
for (Map.Entry<String, Object> entry : map.entrySet()) {
sheetSet.put(new NodeProperty<>(entry.getKey(), entry.getKey(), NO_DESCR, entry.getValue()));
}
return sheet;
}
@Override
public <T> T accept(ContentNodeVisitor<T> visitor) { public <T> T accept(ContentNodeVisitor<T> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }

View File

@ -22,19 +22,13 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.Action; import javax.swing.Action;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.Utilities; import org.openide.util.Utilities;
import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
@ -44,7 +38,6 @@ import org.sleuthkit.autopsy.directorytree.ViewContextAction;
import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction; import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -68,43 +61,6 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
} }
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
List<ContentTag> tags = getContentTagsFromDatabase();
Map<String, Object> map = new LinkedHashMap<>();
fillPropertyMap(map, getContent());
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "LocalFileNode.createSheet.name.name"),
NbBundle.getMessage(this.getClass(), "LocalFileNode.createSheet.name.displayName"),
NbBundle.getMessage(this.getClass(), "LocalFileNode.createSheet.name.desc"),
getName()));
addScoreProperty(sheetSet, tags);
CorrelationAttributeInstance correlationAttribute = null;
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
correlationAttribute = getCorrelationAttributeInstance();
}
addCommentProperty(sheetSet, tags, correlationAttribute);
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
addCountProperty(sheetSet, correlationAttribute);
}
final String NO_DESCR = NbBundle.getMessage(this.getClass(), "LocalFileNode.createSheet.noDescr.text");
for (Map.Entry<String, Object> entry : map.entrySet()) {
sheetSet.put(new NodeProperty<>(entry.getKey(), entry.getKey(), NO_DESCR, entry.getValue()));
}
return sheet;
}
@Override
public Action[] getActions(boolean context) { public Action[] getActions(boolean context) {
List<Action> actionsList = new ArrayList<>(); List<Action> actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(super.getActions(true))); actionsList.addAll(Arrays.asList(super.getActions(true)));

View File

@ -0,0 +1,59 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2018 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.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import org.sleuthkit.autopsy.events.AutopsyEvent;
/**
* Completes the tasks needed to populate the Translation columns in the
* background so that the UI is not blocked while waiting for responses from the
* translation service. Once the event is done, it fires a PropertyChangeEvent
* to let the AbstractAbstractFileNode know it's time to update.
*/
class TranslationTask implements Runnable {
private final WeakReference<AbstractAbstractFileNode<?>> weakNodeRef;
private final PropertyChangeListener listener;
public TranslationTask(WeakReference<AbstractAbstractFileNode<?>> weakContentRef, PropertyChangeListener listener) {
this.weakNodeRef = weakContentRef;
this.listener = listener;
}
@Override
public void run() {
AbstractAbstractFileNode<?> fileNode = weakNodeRef.get();
//Check for stale reference
if (fileNode == null) {
return;
}
String translatedFileName = fileNode.getTranslatedFileName();
if (!translatedFileName.isEmpty() && listener != null) {
//Only fire if the result is meaningful and the listener is not a stale reference
listener.propertyChange(new PropertyChangeEvent(
AutopsyEvent.SourceType.LOCAL.toString(),
AbstractAbstractFileNode.NodeSpecificEvents.TRANSLATION_AVAILABLE.toString(),
null, translatedFileName));
}
}
}

View File

@ -21,18 +21,13 @@ package org.sleuthkit.autopsy.datamodel;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
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.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.VirtualDirectory; import org.sleuthkit.datamodel.VirtualDirectory;
@ -78,39 +73,18 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode {
"VirtualDirectoryNode.createSheet.deviceId.displayName=Device ID", "VirtualDirectoryNode.createSheet.deviceId.displayName=Device ID",
"VirtualDirectoryNode.createSheet.deviceId.desc=Device ID of the image"}) "VirtualDirectoryNode.createSheet.deviceId.desc=Device ID of the image"})
protected Sheet createSheet() { protected Sheet createSheet() {
Sheet sheet = super.createSheet(); //Do a special strategy for virtual directories..
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); if(this.content.isDataSource()){
if (sheetSet == null) { Sheet sheet = new Sheet();
sheetSet = Sheet.createPropertiesSet(); Sheet.Set sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet); sheet.put(sheetSet);
}
List<ContentTag> tags = getContentTagsFromDatabase(); sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "VirtualDirectoryNode.createSheet.name.name"),
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "VirtualDirectoryNode.createSheet.name.name"),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"VirtualDirectoryNode.createSheet.name.displayName"), "VirtualDirectoryNode.createSheet.name.displayName"),
NbBundle.getMessage(this.getClass(), "VirtualDirectoryNode.createSheet.name.desc"), NbBundle.getMessage(this.getClass(), "VirtualDirectoryNode.createSheet.name.desc"),
getName())); getName()));
if (!this.content.isDataSource()) {
addScoreProperty(sheetSet, tags);
CorrelationAttributeInstance correlationAttribute = null;
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
correlationAttribute = getCorrelationAttributeInstance();
}
addCommentProperty(sheetSet, tags, correlationAttribute);
if (EamDbUtil.useCentralRepo() && UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
addCountProperty(sheetSet, correlationAttribute);
}
Map<String, Object> map = new LinkedHashMap<>();
fillPropertyMap(map, getContent());
final String NO_DESCR = NbBundle.getMessage(this.getClass(), "VirtualDirectoryNode.createSheet.noDesc");
for (Map.Entry<String, Object> entry : map.entrySet()) {
sheetSet.put(new NodeProperty<>(entry.getKey(), entry.getKey(), NO_DESCR, entry.getValue()));
}
} else {
sheetSet.put(new NodeProperty<>(Bundle.VirtualDirectoryNode_createSheet_type_name(), sheetSet.put(new NodeProperty<>(Bundle.VirtualDirectoryNode_createSheet_type_name(),
Bundle.VirtualDirectoryNode_createSheet_type_displayName(), Bundle.VirtualDirectoryNode_createSheet_type_displayName(),
Bundle.VirtualDirectoryNode_createSheet_type_desc(), Bundle.VirtualDirectoryNode_createSheet_type_desc(),
@ -141,10 +115,11 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode {
} catch (SQLException | TskCoreException | NoCurrentCaseException ex) { } catch (SQLException | TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Failed to get device id for the following image: " + this.content.getId(), ex); logger.log(Level.SEVERE, "Failed to get device id for the following image: " + this.content.getId(), ex);
} }
return sheet;
} }
return sheet; //Otherwise default to the AAFN createSheet method.
return super.createSheet();
} }
@Override @Override

View File

@ -20,8 +20,7 @@ package org.sleuthkit.autopsy.datasourceprocessors;
import java.io.File; import java.io.File;
import java.util.Calendar; import java.util.Calendar;
import java.util.SimpleTimeZone; import java.util.List;
import java.util.TimeZone;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
@ -32,6 +31,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PathValidator; import org.sleuthkit.autopsy.coreutils.PathValidator;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
/** /**
* Allows examiner to supply a raw data source. * Allows examiner to supply a raw data source.
@ -82,26 +82,13 @@ final class RawDSInputPanel extends JPanel implements DocumentListener {
* machine time zone to be selected. * machine time zone to be selected.
*/ */
private void createTimeZoneList() { private void createTimeZoneList() {
// load and add all timezone List<String> timeZoneList = TimeZoneUtils.createTimeZoneList();
String[] ids = SimpleTimeZone.getAvailableIDs(); for (String timeZone : timeZoneList) {
for (String id : ids) { timeZoneComboBox.addItem(timeZone);
TimeZone zone = TimeZone.getTimeZone(id);
int offset = zone.getRawOffset() / 1000;
int hour = offset / 3600;
int minutes = (offset % 3600) / 60;
String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id);
timeZoneComboBox.addItem(item);
} }
// get the current timezone
TimeZone thisTimeZone = Calendar.getInstance().getTimeZone();
int thisOffset = thisTimeZone.getRawOffset() / 1000;
int thisHour = thisOffset / 3600;
int thisMinutes = (thisOffset % 3600) / 60;
String formatted = String.format("(GMT%+d:%02d) %s", thisHour, thisMinutes, thisTimeZone.getID());
// set the selected timezone // set the selected timezone
timeZoneComboBox.setSelectedItem(formatted); timeZoneComboBox.setSelectedItem(TimeZoneUtils.createTimeZoneString(Calendar.getInstance().getTimeZone()));
} }
/** /**

View File

@ -167,9 +167,11 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
public void preferenceChange(PreferenceChangeEvent evt) { public void preferenceChange(PreferenceChangeEvent evt) {
switch (evt.getKey()) { switch (evt.getKey()) {
case UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME: case UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME:
case UserPreferences.TIME_ZONE_FOR_DISPLAYS:
case UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE: case UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE:
case UserPreferences.HIDE_SLACK_FILES_IN_DATA_SRCS_TREE: case UserPreferences.HIDE_SLACK_FILES_IN_DATA_SRCS_TREE:
case UserPreferences.HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES: case UserPreferences.HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES:
case UserPreferences.DISPLAY_TRANSLATED_NAMES:
case UserPreferences.KEEP_PREFERRED_VIEWER: case UserPreferences.KEEP_PREFERRED_VIEWER:
refreshContentTreeSafe(); refreshContentTreeSafe();
break; break;

View File

@ -29,7 +29,6 @@ import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.SimpleTimeZone; import java.util.SimpleTimeZone;
@ -44,6 +43,7 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
/** /**
* Filters file date properties (modified/created/etc.. times) * Filters file date properties (modified/created/etc.. times)
@ -144,30 +144,15 @@ class DateSearchFilter extends AbstractFileSearchFilter<DateSearchPanel> {
Case currentCase = Case.getCurrentCaseThrows(); // get the most updated case Case currentCase = Case.getCurrentCaseThrows(); // get the most updated case
Set<TimeZone> caseTimeZones = currentCase.getTimeZones(); Set<TimeZone> caseTimeZones = currentCase.getTimeZones();
Iterator<TimeZone> iterator = caseTimeZones.iterator(); for (TimeZone timeZone : caseTimeZones) {
while (iterator.hasNext()) { timeZones.add(TimeZoneUtils.createTimeZoneString(timeZone));
TimeZone zone = iterator.next();
int offset = zone.getRawOffset() / 1000;
int hour = offset / 3600;
int minutes = (offset % 3600) / 60;
String item = String.format("(GMT%+d:%02d) %s", hour, minutes, zone.getID()); //NON-NLS
timeZones.add(item);
} }
if (caseTimeZones.size() > 0) { if (caseTimeZones.size() > 0) {
timeZones.add(SEPARATOR); timeZones.add(SEPARATOR);
} }
// load and add all timezone timeZones.addAll(TimeZoneUtils.createTimeZoneList());
String[] ids = SimpleTimeZone.getAvailableIDs();
for (String id : ids) {
TimeZone zone = TimeZone.getTimeZone(id);
int offset = zone.getRawOffset() / 1000;
int hour = offset / 3600;
int minutes = (offset % 3600) / 60;
String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id); //NON-NLS
timeZones.add(item);
}
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
// No current case. // No current case.
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

View File

@ -269,8 +269,7 @@ class MSOfficeEmbeddedContentExtractor {
HWPFDocument doc = new HWPFDocument(new ReadContentInputStream(af)); HWPFDocument doc = new HWPFDocument(new ReadContentInputStream(af));
PicturesTable pictureTable = doc.getPicturesTable(); PicturesTable pictureTable = doc.getPicturesTable();
listOfAllPictures = pictureTable.getAllPictures(); listOfAllPictures = pictureTable.getAllPictures();
} catch (IOException | IllegalArgumentException } catch (Exception ex) {
| IndexOutOfBoundsException | NullPointerException ex) {
// IOException: // IOException:
// Thrown when the document has issues being read. // Thrown when the document has issues being read.
@ -286,10 +285,9 @@ class MSOfficeEmbeddedContentExtractor {
// These get thrown in certain images. The reason is unknown. It is // These get thrown in certain images. The reason is unknown. It is
// likely due to problems with the file formats that POI is poorly // likely due to problems with the file formats that POI is poorly
// handling. // handling.
return null;
} catch (Throwable ex) { //Any runtime exception escaping
// instantiating POI containers throw RuntimeExceptions LOGGER.log(Level.WARNING, "Word document container could not be initialized. Reason: {0}", ex.getMessage()); //NON-NLS
LOGGER.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.docContainer.init.err", af.getName()), ex); //NON-NLS
return null; return null;
} }
@ -333,8 +331,7 @@ class MSOfficeEmbeddedContentExtractor {
try { try {
HSLFSlideShow ppt = new HSLFSlideShow(new ReadContentInputStream(af)); HSLFSlideShow ppt = new HSLFSlideShow(new ReadContentInputStream(af));
listOfAllPictures = ppt.getPictureData(); listOfAllPictures = ppt.getPictureData();
} catch (IOException | IllegalArgumentException } catch (Exception ex) {
| IndexOutOfBoundsException ex) {
// IllegalArgumentException: // IllegalArgumentException:
// This will catch OldFileFormatException, which is thrown when the // This will catch OldFileFormatException, which is thrown when the
// document version is unsupported. The IllegalArgumentException may // document version is unsupported. The IllegalArgumentException may
@ -346,10 +343,7 @@ class MSOfficeEmbeddedContentExtractor {
// This gets thrown in certain images. The reason is unknown. It is // This gets thrown in certain images. The reason is unknown. It is
// likely due to problems with the file formats that POI is poorly // likely due to problems with the file formats that POI is poorly
// handling. // handling.
return null; LOGGER.log(Level.WARNING, "PPT container could not be initialized. Reason: {0}", ex.getMessage()); //NON-NLS
} catch (Throwable ex) {
// instantiating POI containers throw RuntimeExceptions
LOGGER.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.pptContainer.init.err", af.getName()), ex); //NON-NLS
return null; return null;
} }
@ -422,9 +416,7 @@ class MSOfficeEmbeddedContentExtractor {
try { try {
Workbook xls = new HSSFWorkbook(new ReadContentInputStream(af)); Workbook xls = new HSSFWorkbook(new ReadContentInputStream(af));
listOfAllPictures = xls.getAllPictures(); listOfAllPictures = xls.getAllPictures();
} catch (IOException | LeftoverDataException } catch (Exception ex) {
| RecordFormatException | IllegalArgumentException
| IndexOutOfBoundsException ex) {
// IllegalArgumentException: // IllegalArgumentException:
// This will catch OldFileFormatException, which is thrown when the // This will catch OldFileFormatException, which is thrown when the
// document version is unsupported. The IllegalArgumentException may // document version is unsupported. The IllegalArgumentException may
@ -443,10 +435,7 @@ class MSOfficeEmbeddedContentExtractor {
// These get thrown in certain images. The reason is unknown. It is // These get thrown in certain images. The reason is unknown. It is
// likely due to problems with the file formats that POI is poorly // likely due to problems with the file formats that POI is poorly
// handling. // handling.
return null; LOGGER.log(Level.WARNING, "Excel (.xls) document container could not be initialized. Reason: {0}", ex.getMessage()); //NON-NLS
} catch (Throwable ex) {
// instantiating POI containers throw RuntimeExceptions
LOGGER.log(Level.SEVERE, String.format("%s%s", NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.xlsContainer.init.err", af.getName()), af.getName()), ex); //NON-NLS
return null; return null;
} }

View File

@ -21,9 +21,6 @@ package org.sleuthkit.autopsy.modules.embeddedfileextractor;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -92,7 +89,7 @@ class SevenZipExtractor {
private String moduleDirAbsolute; private String moduleDirAbsolute;
private Blackboard blackboard; private Blackboard blackboard;
private ProgressHandle progress; private ProgressHandle progress;
private int numItems; private int numItems;
private String currentArchiveName; private String currentArchiveName;
@ -180,6 +177,15 @@ class SevenZipExtractor {
* @return true if potential zip bomb, false otherwise * @return true if potential zip bomb, false otherwise
*/ */
private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISevenZipInArchive inArchive, int inArchiveItemIndex, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) { private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISevenZipInArchive inArchive, int inArchiveItemIndex, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) {
//If a file is corrupted as a result of reconstructing it from unallocated space, then
//7zip does a poor job estimating the original uncompressed file size.
//As a result, many corrupted files have wonky compression ratios and could flood the UI
//with false zip bomb notifications. The decision was made to skip compression ratio checks
//for unallocated zip files. Instead, we let the depth be an indicator of a zip bomb.
if(archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC)) {
return false;
}
try { try {
final Long archiveItemSize = (Long) inArchive.getProperty( final Long archiveItemSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.SIZE); inArchiveItemIndex, PropID.SIZE);
@ -540,10 +546,9 @@ class SevenZipExtractor {
inArchive = SevenZip.openInArchive(options, stream, password); inArchive = SevenZip.openInArchive(options, stream, password);
} }
numItems = inArchive.getNumberOfItems(); numItems = inArchive.getNumberOfItems();
logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{escapedArchiveFilePath, numItems}); //NON-NLS
progress.start(numItems); progress.start(numItems);
progressStarted = true; progressStarted = true;
//setup the archive local root folder //setup the archive local root folder
final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)); final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile));
try { try {
@ -669,7 +674,7 @@ class SevenZipExtractor {
inArchive.extract(extractionIndices, false, archiveCallBack); inArchive.extract(extractionIndices, false, archiveCallBack);
unpackSuccessful &= archiveCallBack.wasSuccessful(); unpackSuccessful &= archiveCallBack.wasSuccessful();
archiveDetailsMap = null; archiveDetailsMap = null;
@ -791,140 +796,57 @@ class SevenZipExtractor {
.mapToInt(Integer::intValue) .mapToInt(Integer::intValue)
.toArray(); .toArray();
} }
/** /**
* Stream used to unpack the archive to local file * UnpackStream used by the SevenZipBindings to do archive extraction. A memory
* leak exists in the SevenZip library that will not let go of the streams until
* the entire archive extraction is complete. Instead of creating a new UnpackStream
* for every file in the archive, instead we just rebase our EncodedFileOutputStream pointer
* for every new file.
*/ */
private abstract static class UnpackStream implements ISequentialOutStream { private final static class UnpackStream implements ISequentialOutStream {
private OutputStream output; private EncodedFileOutputStream output;
private String localAbsPath; private String localAbsPath;
private int bytesWritten;
UnpackStream(String localAbsPath) {
UnpackStream(String localAbsPath) throws IOException {
this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
this.localAbsPath = localAbsPath; this.localAbsPath = localAbsPath;
try { this.bytesWritten = 0;
output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1); }
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error writing extracted file: " + localAbsPath, ex); //NON-NLS public void setNewOutputStream(String localAbsPath) throws IOException {
} this.output.close();
this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
this.localAbsPath = localAbsPath;
this.bytesWritten = 0;
} }
public abstract long getSize(); public int getSize() {
return bytesWritten;
OutputStream getOutput() {
return output;
} }
String getLocalAbsPath() {
return localAbsPath;
}
public void close() {
if (output != null) {
try {
output.flush();
output.close();
output = null;
} catch (IOException e) {
logger.log(Level.SEVERE, "Error closing unpack stream for file: {0}", localAbsPath); //NON-NLS
}
}
}
}
/**
* Stream used to unpack the archive of unknown size to local file
*/
private static class UnknownSizeUnpackStream extends UnpackStream {
private long freeDiskSpace;
private boolean outOfSpace = false;
private long bytesWritten = 0;
UnknownSizeUnpackStream(String localAbsPath, long freeDiskSpace) {
super(localAbsPath);
this.freeDiskSpace = freeDiskSpace;
}
@Override
public long getSize() {
return this.bytesWritten;
}
@Override @Override
public int write(byte[] bytes) throws SevenZipException { public int write(byte[] bytes) throws SevenZipException {
try { try {
// If the content size is unknown, cautiously write to disk. output.write(bytes);
// Write only if byte array is less than 80% of the current this.bytesWritten += bytes.length;
// free disk space.
if (freeDiskSpace == IngestMonitor.DISK_FREE_SPACE_UNKNOWN || bytes.length < 0.8 * freeDiskSpace) {
getOutput().write(bytes);
// NOTE: this method is called multiple times for a
// single extractSlow() call. Update bytesWritten and
// freeDiskSpace after every write operation.
this.bytesWritten += bytes.length;
this.freeDiskSpace -= bytes.length;
} else {
this.outOfSpace = true;
logger.log(Level.INFO, NbBundle.getMessage(
SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.noSpace.msg"));
throw new SevenZipException(
NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.noSpace.msg"));
}
} catch (IOException ex) { } catch (IOException ex) {
throw new SevenZipException( throw new SevenZipException(
NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg", NbBundle.getMessage(SevenZipExtractor.class,
getLocalAbsPath()), ex); "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
localAbsPath), ex);
} }
return bytes.length; return bytes.length;
} }
@Override public void close() throws IOException {
public void close() { try(EncodedFileOutputStream out = output) {
if (getOutput() != null) { out.flush();
try { }
getOutput().flush();
getOutput().close();
if (this.outOfSpace) {
Files.delete(Paths.get(getLocalAbsPath()));
}
} catch (IOException e) {
logger.log(Level.SEVERE, "Error closing unpack stream for file: {0}", getLocalAbsPath()); //NON-NLS
}
}
}
}
/**
* Stream used to unpack the archive of known size to local file
*/
private static class KnownSizeUnpackStream extends UnpackStream {
private long size;
KnownSizeUnpackStream(String localAbsPath, long size) {
super(localAbsPath);
this.size = size;
}
@Override
public long getSize() {
return this.size;
}
@Override
public int write(byte[] bytes) throws SevenZipException {
try {
getOutput().write(bytes);
} catch (IOException ex) {
throw new SevenZipException(
NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg",
getLocalAbsPath()), ex);
}
return bytes.length;
} }
} }
/** /**
@ -971,7 +893,6 @@ class SevenZipExtractor {
private final ProgressHandle progressHandle; private final ProgressHandle progressHandle;
private int inArchiveItemIndex; private int inArchiveItemIndex;
private final long freeDiskSpace;
private long createTimeInSeconds; private long createTimeInSeconds;
private long modTimeInSeconds; private long modTimeInSeconds;
@ -988,7 +909,6 @@ class SevenZipExtractor {
String password, long freeDiskSpace) { String password, long freeDiskSpace) {
this.inArchive = inArchive; this.inArchive = inArchive;
this.freeDiskSpace = freeDiskSpace;
this.progressHandle = progressHandle; this.progressHandle = progressHandle;
this.archiveFile = archiveFile; this.archiveFile = archiveFile;
this.archiveDetailsMap = archiveDetailsMap; this.archiveDetailsMap = archiveDetailsMap;
@ -1021,17 +941,24 @@ class SevenZipExtractor {
return null; return null;
} }
final Long archiveItemSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.SIZE);
final String localAbsPath = archiveDetailsMap.get( final String localAbsPath = archiveDetailsMap.get(
inArchiveItemIndex).getLocalAbsPath(); inArchiveItemIndex).getLocalAbsPath();
if (archiveItemSize != null) { //If the Unpackstream has been allocated, then set the Outputstream
unpackStream = new SevenZipExtractor.KnownSizeUnpackStream( //to another file rather than creating a new unpack stream. The 7Zip
localAbsPath, archiveItemSize); //binding has a memory leak, so creating new unpack streams will not be
} else { //dereferenced. As a fix, we create one UnpackStream, and mutate its state,
unpackStream = new SevenZipExtractor.UnknownSizeUnpackStream( //so that there only exists one 8192 byte buffer in memory per archive.
localAbsPath, freeDiskSpace); try {
if (unpackStream != null) {
unpackStream.setNewOutputStream(localAbsPath);
} else {
unpackStream = new UnpackStream(localAbsPath);
}
} catch (IOException ex) {
logger.log(Level.WARNING, String.format("Error opening or setting new stream " //NON-NLS
+ "for archive file at %s", localAbsPath), ex.getMessage()); //NON-NLS
return null;
} }
return unpackStream; return unpackStream;
@ -1060,18 +987,18 @@ class SevenZipExtractor {
: writeTime.getTime() / 1000; : writeTime.getTime() / 1000;
accessTimeInSeconds = accessTime == null ? 0L accessTimeInSeconds = accessTime == null ? 0L
: accessTime.getTime() / 1000; : accessTime.getTime() / 1000;
progressHandle.progress(archiveFile.getName() + ": " progressHandle.progress(archiveFile.getName() + ": "
+ (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH), + (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH),
inArchiveItemIndex); inArchiveItemIndex);
} }
/** /**
* Updates the unpackedNode data in the tree after the archive has been * Updates the unpackedNode data in the tree after the archive has been
* expanded to local disk. * expanded to local disk.
* *
* @param result - ExtractOperationResult * @param result - ExtractOperationResult
* *
* @throws SevenZipException * @throws SevenZipException
*/ */
@ -1103,7 +1030,11 @@ class SevenZipExtractor {
!(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER), !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds, localRelPath); 0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds, localRelPath);
unpackStream.close(); try {
unpackStream.close();
} catch (IOException e) {
logger.log(Level.WARNING, "Error closing unpack stream for file: {0}", localAbsPath); //NON-NLS
}
} }
@Override @Override
@ -1214,9 +1145,9 @@ class SevenZipExtractor {
*/ */
List<AbstractFile> getRootFileObjects() { List<AbstractFile> getRootFileObjects() {
List<AbstractFile> ret = new ArrayList<>(); List<AbstractFile> ret = new ArrayList<>();
for (UnpackedNode child : rootNode.getChildren()) { rootNode.getChildren().forEach((child) -> {
ret.add(child.getFile()); ret.add(child.getFile());
} });
return ret; return ret;
} }
@ -1228,17 +1159,17 @@ class SevenZipExtractor {
*/ */
List<AbstractFile> getAllFileObjects() { List<AbstractFile> getAllFileObjects() {
List<AbstractFile> ret = new ArrayList<>(); List<AbstractFile> ret = new ArrayList<>();
for (UnpackedNode child : rootNode.getChildren()) { rootNode.getChildren().forEach((child) -> {
getAllFileObjectsRec(ret, child); getAllFileObjectsRec(ret, child);
} });
return ret; return ret;
} }
private void getAllFileObjectsRec(List<AbstractFile> list, UnpackedNode parent) { private void getAllFileObjectsRec(List<AbstractFile> list, UnpackedNode parent) {
list.add(parent.getFile()); list.add(parent.getFile());
for (UnpackedNode child : parent.getChildren()) { parent.getChildren().forEach((child) -> {
getAllFileObjectsRec(list, child); getAllFileObjectsRec(list, child);
} });
} }
/** /**

View File

@ -28,6 +28,7 @@ import com.healthmarketscience.jackcess.util.MemFileChannel;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.BufferUnderflowException;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.tika.exception.EncryptedDocumentException; import org.apache.tika.exception.EncryptedDocumentException;
import org.apache.tika.exception.TikaException; import org.apache.tika.exception.TikaException;
@ -313,7 +314,12 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
DatabaseBuilder databaseBuilder = new DatabaseBuilder(); DatabaseBuilder databaseBuilder = new DatabaseBuilder();
databaseBuilder.setChannel(memFileChannel); databaseBuilder.setChannel(memFileChannel);
databaseBuilder.setCodecProvider(codecProvider); databaseBuilder.setCodecProvider(codecProvider);
Database accessDatabase = databaseBuilder.open(); Database accessDatabase;
try {
accessDatabase = databaseBuilder.open();
} catch (IOException | BufferUnderflowException | IndexOutOfBoundsException ignored) {
return passwordProtected;
}
/* /*
* No exception has been thrown at this point, so the file * No exception has been thrown at this point, so the file
* is either a JET database, or an unprotected ACE database. * is either a JET database, or an unprotected ACE database.

View File

@ -91,7 +91,7 @@ HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg=Failed to open has
HashLookupModuleFactory.moduleName.text=Hash Lookup HashLookupModuleFactory.moduleName.text=Hash Lookup
HashLookupModuleFactory.moduleDescription.text=Identifies known and notable files using supplied hash sets, such as a standard NSRL hash set. HashLookupModuleFactory.moduleDescription.text=Identifies known and notable files using supplied hash sets, such as a standard NSRL hash set.
HashDbIngestModule.fileReadErrorMsg=Read Error\: {0} HashDbIngestModule.fileReadErrorMsg=Read Error\: {0}
HashDbIngestModule.calcHashValueErr=Error encountered while calculating the hash value for {0}. HashDbIngestModule.calcHashValueErr=Error encountered while calculating the hash value for {0} ({1}).
HashDbIngestModule.hashLookupErrorMsg=Hash Lookup Error\: {0} HashDbIngestModule.hashLookupErrorMsg=Hash Lookup Error\: {0}
HashDbIngestModule.settingKnownBadStateErr=Error encountered while setting notable state for {0}. HashDbIngestModule.settingKnownBadStateErr=Error encountered while setting notable state for {0}.
HashDbIngestModule.lookingUpKnownBadHashValueErr=Error encountered while looking up notable hash value for {0}. HashDbIngestModule.lookingUpKnownBadHashValueErr=Error encountered while looking up notable hash value for {0}.

View File

@ -227,7 +227,9 @@ public class HashDbIngestModule implements FileIngestModule {
services.postMessage(IngestMessage.createErrorMessage( services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(), HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", name), NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", name),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr", name))); NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr",
file.getParentPath() + file.getName(),
file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)?"Allocated File" : "Deleted File")));
return ProcessResult.ERROR; return ProcessResult.ERROR;
} }
} }

View File

@ -648,6 +648,15 @@ public final class FilesSet implements Serializable {
AbstractTextCondition(Pattern regex) { AbstractTextCondition(Pattern regex) {
this.textMatcher = new FilesSet.Rule.RegexMatcher(regex); this.textMatcher = new FilesSet.Rule.RegexMatcher(regex);
} }
/**
* Construct a case-insensitive multi-value text condition.
*
* @param values The list of values in which to look for a match.
*/
AbstractTextCondition(List<String> values) {
this.textMatcher = new FilesSet.Rule.CaseInsensitiveMultiValueStringComparisionMatcher(values);
}
/** /**
* Get the text the condition matches. * Get the text the condition matches.
@ -820,11 +829,11 @@ public final class FilesSet implements Serializable {
* *
* @param extension The file name extension to be matched. * @param extension The file name extension to be matched.
*/ */
public ExtensionCondition(String extension) { public ExtensionCondition(List<String> extensions) {
// If there is a leading ".", strip it since // If there is a leading ".", strip it since
// AbstractFile.getFileNameExtension() returns just the // AbstractFile.getFileNameExtension() returns just the
// extension chars and not the dot. // extension chars and not the dot.
super(extension.startsWith(".") ? extension.substring(1) : extension, false); super(extensions);
} }
/** /**
@ -948,6 +957,60 @@ public final class FilesSet implements Serializable {
} }
} }
/**
* A text matcher that looks for a single case-insensitive string match
* in a multi-value list.
*/
private static class CaseInsensitiveMultiValueStringComparisionMatcher implements TextMatcher {
private static final long serialVersionUID = 1L;
private final List<String> valuesToMatch;
/**
* Construct a text matcher that looks for a single case-insensitive
* string match in a multi-value list.
*
* @param valuesToMatch The list of values in which to look for a
* match.
*/
CaseInsensitiveMultiValueStringComparisionMatcher(List<String> valuesToMatch) {
List<String> values = new ArrayList<>(valuesToMatch);
for (int i=0; i < values.size(); i++) {
// Remove leading and trailing whitespace.
String tempValue = values.get(i).trim();
// Strip "." from the start of the extension if it exists.
if (tempValue.startsWith(".")) {
tempValue = tempValue.substring(1);
}
values.set(i, tempValue);
}
this.valuesToMatch = values;
}
@Override
public String getTextToMatch() {
return String.join(",", this.valuesToMatch);
}
@Override
public boolean isRegex() {
return false;
}
@Override
public boolean textMatches(String subject) {
for (String value : valuesToMatch) {
if (value.equalsIgnoreCase(subject)) {
return true;
}
}
return false;
}
}
/** /**
* A text matcher that does regular expression matching. * A text matcher that does regular expression matching.
*/ */

View File

@ -29,7 +29,7 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" alignment="0" max="32767" attributes="0"/> <Component id="jScrollPane1" alignment="0" pref="800" max="32767" attributes="0"/>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
@ -173,7 +173,7 @@
<Component id="deleteRuleButton" min="-2" max="-2" attributes="0"/> <Component id="deleteRuleButton" min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace min="24" pref="47" max="32767" attributes="0"/> <EmptySpace min="24" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</Group> </Group>

View File

@ -960,7 +960,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
.addComponent(editRuleButton) .addComponent(editRuleButton)
.addGap(18, 18, 18) .addGap(18, 18, 18)
.addComponent(deleteRuleButton))) .addComponent(deleteRuleButton)))
.addGap(24, 47, Short.MAX_VALUE)))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
); );
jPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {copySetButton, deleteSetButton, editSetButton, exportSetButton, importSetButton, newSetButton}); jPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {copySetButton, deleteSetButton, editSetButton, exportSetButton, importSetButton, newSetButton});
@ -1060,7 +1060,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 800, Short.MAX_VALUE)
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)

View File

@ -47,22 +47,23 @@
</Group> </Group>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="ruleNameTextField" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Component id="daysIncludedTextField" min="-2" pref="69" max="-2" attributes="0"/> <Component id="daysIncludedTextField" min="-2" pref="69" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="daysIncludedLabel" min="-2" max="-2" attributes="0"/> <Component id="daysIncludedLabel" min="-2" max="-2" attributes="0"/>
</Group> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="ruleNameTextField" pref="249" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="fullNameRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="extensionRadioButton" min="-2" pref="98" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="nameRegexCheckbox" min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/> <EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0">
<Component id="fullNameRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="extensionRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="nameRegexCheckbox" min="-2" max="-2" attributes="0"/>
</Group>
</Group> </Group>
</Group> </Group>
<Component id="jLabel5" min="-2" max="-2" attributes="0"/> <Component id="jLabel5" min="-2" max="-2" attributes="0"/>
@ -205,6 +206,9 @@
</Property> </Property>
<Property name="enabled" type="boolean" value="false"/> <Property name="enabled" type="boolean" value="false"/>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="fullNameRadioButtonActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="extensionRadioButton"> <Component class="javax.swing.JRadioButton" name="extensionRadioButton">
<Properties> <Properties>
@ -216,6 +220,9 @@
</Property> </Property>
<Property name="enabled" type="boolean" value="false"/> <Property name="enabled" type="boolean" value="false"/>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="extensionRadioButtonActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="nameRegexCheckbox"> <Component class="javax.swing.JCheckBox" name="nameRegexCheckbox">
<Properties> <Properties>

View File

@ -18,7 +18,9 @@
*/ */
package org.sleuthkit.autopsy.modules.interestingitems; package org.sleuthkit.autopsy.modules.interestingitems;
import java.awt.Color;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.logging.Level; import java.util.logging.Level;
@ -31,6 +33,7 @@ import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor; import org.openide.NotifyDescriptor;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.corecomponents.TextPrompt;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
import org.sleuthkit.autopsy.modules.interestingitems.FilesSetDefsPanel.PANEL_TYPE; import org.sleuthkit.autopsy.modules.interestingitems.FilesSetDefsPanel.PANEL_TYPE;
@ -46,6 +49,8 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
"FilesSetRulePanel.kiloBytes=Kilobytes", "FilesSetRulePanel.kiloBytes=Kilobytes",
"FilesSetRulePanel.megaBytes=Megabytes", "FilesSetRulePanel.megaBytes=Megabytes",
"FilesSetRulePanel.gigaBytes=Gigabytes", "FilesSetRulePanel.gigaBytes=Gigabytes",
"FilesSetRulePanel.nameTextField.fullNameExample=Example: \"file.exe\"",
"FilesSetRulePanel.nameTextField.extensionExample=Examples: \"jpg\" or \"jpg,jpeg,gif\"",
"FilesSetRulePanel.NoConditionError=Must have at least one condition to make a rule.", "FilesSetRulePanel.NoConditionError=Must have at least one condition to make a rule.",
"FilesSetRulePanel.NoMimeTypeError=Please select a valid MIME type.", "FilesSetRulePanel.NoMimeTypeError=Please select a valid MIME type.",
"FilesSetRulePanel.NoNameError=Name cannot be empty", "FilesSetRulePanel.NoNameError=Name cannot be empty",
@ -62,6 +67,7 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
private static final List<String> ILLEGAL_FILE_PATH_CHARS = FilesSetsManager.getIllegalFilePathChars(); private static final List<String> ILLEGAL_FILE_PATH_CHARS = FilesSetsManager.getIllegalFilePathChars();
private JButton okButton; private JButton okButton;
private JButton cancelButton; private JButton cancelButton;
private TextPrompt nameTextFieldPrompt;
/** /**
* Constructs a files set rule panel in create rule mode. * Constructs a files set rule panel in create rule mode.
@ -87,6 +93,8 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
this.dateCheckActionPerformed(null); this.dateCheckActionPerformed(null);
populateComponentsWithDefaultValues(); populateComponentsWithDefaultValues();
this.setButtons(okButton, cancelButton); this.setButtons(okButton, cancelButton);
updateNameTextFieldPrompt();
} }
/** /**
@ -120,6 +128,34 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
populatePathConditionComponents(rule); populatePathConditionComponents(rule);
populateDateConditionComponents(rule); populateDateConditionComponents(rule);
this.setButtons(okButton, cancelButton); this.setButtons(okButton, cancelButton);
updateNameTextFieldPrompt();
}
/**
* Update the text prompt of the name text field based on the input type
* selection.
*/
private void updateNameTextFieldPrompt() {
/**
* Add text prompt to the text field.
*/
String text;
if (fullNameRadioButton.isSelected()) {
text = Bundle.FilesSetRulePanel_nameTextField_fullNameExample();
} else {
text = Bundle.FilesSetRulePanel_nameTextField_extensionExample();
}
nameTextFieldPrompt = new TextPrompt(text, nameTextField);
/**
* Sets the foreground color and transparency of the text prompt.
*/
nameTextFieldPrompt.setForeground(Color.LIGHT_GRAY);
nameTextFieldPrompt.changeAlpha(0.9f); // Mostly opaque
validate();
repaint();
} }
/** /**
@ -435,7 +471,7 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
if (!this.nameTextField.getText().isEmpty()) { if (!this.nameTextField.getText().isEmpty()) {
if (this.nameRegexCheckbox.isSelected()) { if (this.nameRegexCheckbox.isSelected()) {
try { try {
Pattern pattern = Pattern.compile(this.nameTextField.getText()); Pattern pattern = Pattern.compile(this.nameTextField.getText(), Pattern.CASE_INSENSITIVE);
if (this.fullNameRadioButton.isSelected()) { if (this.fullNameRadioButton.isSelected()) {
condition = new FilesSet.Rule.FullNameCondition(pattern); condition = new FilesSet.Rule.FullNameCondition(pattern);
} else { } else {
@ -449,7 +485,7 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
if (this.fullNameRadioButton.isSelected()) { if (this.fullNameRadioButton.isSelected()) {
condition = new FilesSet.Rule.FullNameCondition(this.nameTextField.getText()); condition = new FilesSet.Rule.FullNameCondition(this.nameTextField.getText());
} else { } else {
condition = new FilesSet.Rule.ExtensionCondition(this.nameTextField.getText()); condition = new FilesSet.Rule.ExtensionCondition(Arrays.asList(this.nameTextField.getText().split(",")));
} }
} else { } else {
logger.log(Level.SEVERE, "Attempt to get name condition with illegal chars"); // NON-NLS logger.log(Level.SEVERE, "Attempt to get name condition with illegal chars"); // NON-NLS
@ -520,7 +556,7 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
if (!this.pathTextField.getText().isEmpty()) { if (!this.pathTextField.getText().isEmpty()) {
if (this.pathRegexCheckBox.isSelected()) { if (this.pathRegexCheckBox.isSelected()) {
try { try {
condition = new FilesSet.Rule.ParentPathCondition(Pattern.compile(this.pathTextField.getText())); condition = new FilesSet.Rule.ParentPathCondition(Pattern.compile(this.pathTextField.getText(), Pattern.CASE_INSENSITIVE));
} catch (PatternSyntaxException ex) { } catch (PatternSyntaxException ex) {
logger.log(Level.SEVERE, "Attempt to get malformed path condition", ex); // NON-NLS logger.log(Level.SEVERE, "Attempt to get malformed path condition", ex); // NON-NLS
throw new IllegalStateException("The files set rule panel path condition is not in a valid state"); // NON-NLS throw new IllegalStateException("The files set rule panel path condition is not in a valid state"); // NON-NLS
@ -657,10 +693,20 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
nameButtonGroup.add(fullNameRadioButton); nameButtonGroup.add(fullNameRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(fullNameRadioButton, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.fullNameRadioButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(fullNameRadioButton, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.fullNameRadioButton.text")); // NOI18N
fullNameRadioButton.setEnabled(false); fullNameRadioButton.setEnabled(false);
fullNameRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
fullNameRadioButtonActionPerformed(evt);
}
});
nameButtonGroup.add(extensionRadioButton); nameButtonGroup.add(extensionRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(extensionRadioButton, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.extensionRadioButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(extensionRadioButton, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.extensionRadioButton.text")); // NOI18N
extensionRadioButton.setEnabled(false); extensionRadioButton.setEnabled(false);
extensionRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
extensionRadioButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(nameRegexCheckbox, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.nameRegexCheckbox.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(nameRegexCheckbox, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.nameRegexCheckbox.text")); // NOI18N
nameRegexCheckbox.setEnabled(false); nameRegexCheckbox.setEnabled(false);
@ -782,18 +828,19 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
.addComponent(pathSeparatorInfoLabel)) .addComponent(pathSeparatorInfoLabel))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(ruleNameTextField)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(daysIncludedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 69, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(daysIncludedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 69, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(daysIncludedLabel)) .addComponent(daysIncludedLabel)
.addComponent(ruleNameTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 249, Short.MAX_VALUE) .addGap(0, 0, Short.MAX_VALUE)))
.addGroup(layout.createSequentialGroup() .addGap(1, 1, 1))
.addComponent(fullNameRadioButton) .addGroup(layout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(fullNameRadioButton)
.addComponent(extensionRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 98, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(extensionRadioButton)
.addComponent(nameRegexCheckbox))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGap(1, 1, 1)))) .addComponent(nameRegexCheckbox))))
.addComponent(jLabel5) .addComponent(jLabel5)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -952,6 +999,14 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
this.setOkButton(); this.setOkButton();
}//GEN-LAST:event_mimeCheckActionPerformed }//GEN-LAST:event_mimeCheckActionPerformed
private void extensionRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_extensionRadioButtonActionPerformed
updateNameTextFieldPrompt();
}//GEN-LAST:event_extensionRadioButtonActionPerformed
private void fullNameRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fullNameRadioButtonActionPerformed
updateNameTextFieldPrompt();
}//GEN-LAST:event_fullNameRadioButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton allRadioButton; private javax.swing.JRadioButton allRadioButton;
private javax.swing.JCheckBox dateCheck; private javax.swing.JCheckBox dateCheck;

View File

@ -25,6 +25,7 @@ import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -280,7 +281,7 @@ class InterestingItemsFilesSetSettings implements Serializable {
if (elem.getTagName().equals(NAME_RULE_TAG)) { if (elem.getTagName().equals(NAME_RULE_TAG)) {
nameCondition = new FilesSet.Rule.FullNameCondition(content); nameCondition = new FilesSet.Rule.FullNameCondition(content);
} else if (elem.getTagName().equals(EXTENSION_RULE_TAG)) { } else if (elem.getTagName().equals(EXTENSION_RULE_TAG)) {
nameCondition = new FilesSet.Rule.ExtensionCondition(content); nameCondition = new FilesSet.Rule.ExtensionCondition(Arrays.asList(content.split(",")));
} }
} }
} }

View File

@ -271,6 +271,9 @@ class ReportHTML implements TableReportModule {
case TSK_ACCOUNT: case TSK_ACCOUNT:
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/accounts.png"); //NON-NLS in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/accounts.png"); //NON-NLS
break; break;
case TSK_WIFI_NETWORK:
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/network-wifi.png"); //NON-NLS
break;
default: default:
logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS

View File

@ -0,0 +1,30 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2018 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.texttranslation;
/**
* Exception to indicate that no Service Provider could be found during the
* Lookup action.
*/
public class NoServiceProviderException extends Exception {
public NoServiceProviderException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,77 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2018 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.texttranslation;
import java.util.Optional;
import org.openide.util.Lookup;
/**
* Performs a lookup for a TextTranslator service provider and if present,
* will use this provider to run translation on the input.
*/
public final class TextTranslationService {
private final static TextTranslationService tts = new TextTranslationService();
private final Optional<TextTranslator> translator;
private TextTranslationService(){
//Perform look up for Text Translation implementations ONLY ONCE during
//class loading.
translator = Optional.ofNullable(Lookup.getDefault()
.lookup(TextTranslator.class));
}
public static TextTranslationService getInstance() {
return tts;
}
/**
* Translates the input string using whichever TextTranslator Service Provider
* was found during lookup.
*
* @param input Input string to be translated
*
* @return Translation string
*
* @throws NoServiceProviderException Failed to find a Translation service
* provider
* @throws TranslationException System exception for classes to use
* when specific translation
* implementations fail
*/
public String translate(String input) throws NoServiceProviderException, TranslationException {
if (translator.isPresent()) {
return translator.get().translate(input);
}
throw new NoServiceProviderException(
"Could not find a TextTranslator service provider");
}
/**
* Returns if a TextTranslator lookup successfully found an implementing
* class.
*
* @return
*/
public boolean hasProvider() {
return translator.isPresent();
}
}

View File

@ -0,0 +1,29 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2018 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.texttranslation;
/**
* Interface for creating text translators. Implementing classes will be picked
* up and run by the Text Translation Service.
*/
public interface TextTranslator {
String translate(String input) throws TranslationException;
}

View File

@ -0,0 +1,51 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2018 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.texttranslation;
/**
* Provides a system exception for the Text Translation errors
*/
public class TranslationException extends Exception {
/**
* Constructs a new exception with null as its message.
*/
public TranslationException() {
super();
}
/**
* Constructs a new exception with the specified message.
*
* @param message The message.
*/
public TranslationException(String message) {
super(message);
}
/**
* Constructs a new exception with the specified message and cause.
*
* @param message The message.
* @param cause The cause.
*/
public TranslationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -76,7 +76,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.autopsy.timeline.events.EventAddedEvent; import org.sleuthkit.autopsy.timeline.events.TimelineEventAddedEvent;
import org.sleuthkit.autopsy.timeline.events.ViewInTimelineRequestedEvent; import org.sleuthkit.autopsy.timeline.events.ViewInTimelineRequestedEvent;
import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.DetailViewEvent; import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.DetailViewEvent;
import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState;
@ -780,7 +780,7 @@ public class TimeLineController {
break; break;
case TIMELINE_EVENT_ADDED: case TIMELINE_EVENT_ADDED:
future = executor.submit(() -> { future = executor.submit(() -> {
filteredEvents.invalidateCaches(singleton(((EventAddedEvent) evt).getAddedEventID())); filteredEvents.invalidateCaches(singleton(((TimelineEventAddedEvent) evt).getAddedEventID()));
return null; return null;
}); });
break; break;

View File

@ -41,6 +41,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.TimeStampUtils; import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataSource;
/* /*
* A runnable that adds an archive data source as well as data sources contained * A runnable that adds an archive data source as well as data sources contained
@ -195,9 +196,18 @@ class AddArchiveTask implements Runnable {
continue; continue;
} }
// if we are here it means the data source was addedd successfully // if we are here it means the data source was added successfully
success = true; success = true;
newDataSources.addAll(internalDataSource.getContent()); newDataSources.addAll(internalDataSource.getContent());
// Update the names for all new data sources to be the root archive plus the name of the data source
for (Content c:internalDataSource.getContent()) {
if (c instanceof DataSource) {
DataSource ds = (DataSource) c;
String newName = Paths.get(archivePath).getFileName() + "/" + ds.getName();
ds.setDisplayName(newName);
}
}
// skip all other DSPs for this data source // skip all other DSPs for this data source
break; break;

View File

@ -45,6 +45,7 @@ import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.NetworkUtils; import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.coreutils.StopWatch;
import org.sleuthkit.autopsy.events.AutopsyEventException; import org.sleuthkit.autopsy.events.AutopsyEventException;
import org.sleuthkit.autopsy.events.AutopsyEventPublisher; import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.ProcessingStatus; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.ProcessingStatus;
@ -666,43 +667,73 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
* @return A result code indicating success, partial success, or failure. * @return A result code indicating success, partial success, or failure.
*/ */
CaseDeletionResult deleteCase(AutoIngestJob job) { CaseDeletionResult deleteCase(AutoIngestJob job) {
String caseName = job.getManifest().getCaseName();
Path caseDirectoryPath = job.getCaseDirectoryPath();
Path metadataFilePath = caseDirectoryPath.resolve(caseName + CaseMetadata.getFileExtension());
StopWatch stopWatch = new StopWatch();
stopWatch.start();
synchronized (jobsLock) { synchronized (jobsLock) {
String caseName = job.getManifest().getCaseName(); stopWatch.stop();
Path metadataFilePath = job.getCaseDirectoryPath().resolve(caseName + CaseMetadata.getFileExtension()); LOGGER.log(Level.INFO, String.format("Used %d s to acquire jobsLock (Java monitor in AutoIngestMonitor class) for case %s at %s", stopWatch.getElapsedTimeSecs(), caseName, caseDirectoryPath));
stopWatch.reset();
stopWatch.start();
try { try {
CaseMetadata metadata = new CaseMetadata(metadataFilePath); CaseMetadata metadata = new CaseMetadata(metadataFilePath);
stopWatch.stop();
LOGGER.log(Level.INFO, String.format("Used %d s to read case metadata for case %s at %s", stopWatch.getElapsedTimeSecs(), caseName, caseDirectoryPath));
stopWatch.reset();
stopWatch.start();
Case.deleteCase(metadata); Case.deleteCase(metadata);
} catch (CaseMetadata.CaseMetadataException ex) { } catch (CaseMetadata.CaseMetadataException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to get case metadata file %s for case %s at %s", metadataFilePath.toString(), caseName, job.getCaseDirectoryPath().toString()), ex); LOGGER.log(Level.SEVERE, String.format("Failed to read case metadata file %s for case %s at %s", metadataFilePath, caseName, caseDirectoryPath), ex);
stopWatch.stop();
LOGGER.log(Level.INFO, String.format("Used %d s to fail to read case metadata file %s for case %s at %s", stopWatch.getElapsedTimeSecs(), metadataFilePath, caseName, caseDirectoryPath));
return CaseDeletionResult.FAILED; return CaseDeletionResult.FAILED;
} catch (CaseActionException ex) { } catch (CaseActionException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to physically delete case %s at %s", caseName, job.getCaseDirectoryPath().toString()), ex); LOGGER.log(Level.SEVERE, String.format("Failed to delete case %s at %s", caseName, caseDirectoryPath), ex);
return CaseDeletionResult.FAILED; return CaseDeletionResult.FAILED;
} }
// Update the state of completed jobs associated with this case to indicate // Update the state of completed jobs associated with this case to indicate
// that the case has been deleted // that the case has been deleted
for (AutoIngestJob completedJob : getCompletedJobs()) { stopWatch.reset();
stopWatch.start();
List<AutoIngestJob> completedJobs = getCompletedJobs();
stopWatch.stop();
LOGGER.log(Level.INFO, String.format("Used %d s to get completed jobs listing for case %s at %s", stopWatch.getElapsedTimeSecs(), caseName, caseDirectoryPath));
stopWatch.reset();
stopWatch.start();
for (AutoIngestJob completedJob : completedJobs) {
if (caseName.equals(completedJob.getManifest().getCaseName())) { if (caseName.equals(completedJob.getManifest().getCaseName())) {
try { try {
completedJob.setProcessingStatus(DELETED); completedJob.setProcessingStatus(DELETED);
AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(completedJob); AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(completedJob);
coordinationService.setNodeData(CoordinationService.CategoryNode.MANIFESTS, completedJob.getManifest().getFilePath().toString(), nodeData.toArray()); coordinationService.setNodeData(CoordinationService.CategoryNode.MANIFESTS, completedJob.getManifest().getFilePath().toString(), nodeData.toArray());
} catch (CoordinationServiceException | InterruptedException ex) { } catch (CoordinationServiceException | InterruptedException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to update completed job node data for %s when deleting case %s", completedJob.getManifest().getFilePath().toString(), caseName), ex); LOGGER.log(Level.SEVERE, String.format("Failed to update completed job node data for %s when deleting case %s at %s", completedJob.getManifest().getFilePath(), caseName, caseDirectoryPath), ex);
stopWatch.stop();
LOGGER.log(Level.INFO, String.format("Used %d s to fail to update job node data for completed jobs for case %s at %s", stopWatch.getElapsedTimeSecs(), caseName, caseDirectoryPath));
return CaseDeletionResult.PARTIALLY_DELETED; return CaseDeletionResult.PARTIALLY_DELETED;
} }
} }
} }
stopWatch.stop();
LOGGER.log(Level.INFO, String.format("Used %d s to update job node data for completed jobs for case %s at %s", stopWatch.getElapsedTimeSecs(), caseName, caseDirectoryPath));
// Remove jobs associated with this case from the completed jobs collection. // Remove jobs associated with this case from the completed jobs collection.
jobsSnapshot.completedJobs.removeIf((AutoIngestJob completedJob) stopWatch.reset();
stopWatch.start();
completedJobs.removeIf((AutoIngestJob completedJob)
-> completedJob.getManifest().getCaseName().equals(caseName)); -> completedJob.getManifest().getCaseName().equals(caseName));
stopWatch.stop();
LOGGER.log(Level.INFO, String.format("Used %d s to remove completed jobs for case %s at %s from current jobs snapshot", stopWatch.getElapsedTimeSecs(), caseName, caseDirectoryPath));
// Publish a message to update auto ingest nodes. // Publish a message to update auto ingest nodes.
stopWatch.reset();
stopWatch.start();
eventPublisher.publishRemotely(new AutoIngestCaseDeletedEvent(caseName, LOCAL_HOST_NAME, AutoIngestManager.getSystemUserNameProperty())); eventPublisher.publishRemotely(new AutoIngestCaseDeletedEvent(caseName, LOCAL_HOST_NAME, AutoIngestManager.getSystemUserNameProperty()));
stopWatch.stop();
LOGGER.log(Level.INFO, String.format("Used %d s to publish job deletion event for case %s at %s", stopWatch.getElapsedTimeSecs(), caseName,caseDirectoryPath));
} }
return CaseDeletionResult.FULLY_DELETED; return CaseDeletionResult.FULLY_DELETED;

View File

@ -25,9 +25,7 @@ import java.util.Calendar;
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.SimpleTimeZone;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TimeZone;
import java.util.TreeSet; import java.util.TreeSet;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JPanel; import javax.swing.JPanel;
@ -43,6 +41,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PathValidator; import org.sleuthkit.autopsy.coreutils.PathValidator;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class MemoryDSInputPanel extends JPanel implements DocumentListener { final class MemoryDSInputPanel extends JPanel implements DocumentListener {
@ -132,26 +131,13 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
* machine time zone to be selected. * machine time zone to be selected.
*/ */
private void createTimeZoneList() { private void createTimeZoneList() {
// load and add all timezone List<String> timeZoneList = TimeZoneUtils.createTimeZoneList();
String[] ids = SimpleTimeZone.getAvailableIDs(); for (String timeZone : timeZoneList) {
for (String id : ids) { timeZoneComboBox.addItem(timeZone);
TimeZone zone = TimeZone.getTimeZone(id);
int offset = zone.getRawOffset() / 1000;
int hour = offset / 3600;
int minutes = (offset % 3600) / 60;
String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id);
timeZoneComboBox.addItem(item);
} }
// get the current timezone
TimeZone thisTimeZone = Calendar.getInstance().getTimeZone();
int thisOffset = thisTimeZone.getRawOffset() / 1000;
int thisHour = thisOffset / 3600;
int thisMinutes = (thisOffset % 3600) / 60;
String formatted = String.format("(GMT%+d:%02d) %s", thisHour, thisMinutes, thisTimeZone.getID());
// set the selected timezone // set the selected timezone
timeZoneComboBox.setSelectedItem(formatted); timeZoneComboBox.setSelectedItem(TimeZoneUtils.createTimeZoneString(Calendar.getInstance().getTimeZone()));
} }

View File

@ -1,6 +1,6 @@
Manifest-Version: 1.0 Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.imagegallery/2 OpenIDE-Module: org.sleuthkit.autopsy.imagegallery/2
OpenIDE-Module-Implementation-Version: 4 OpenIDE-Module-Implementation-Version: 5
OpenIDE-Module-Layer: org/sleuthkit/autopsy/imagegallery/layer.xml OpenIDE-Module-Layer: org/sleuthkit/autopsy/imagegallery/layer.xml
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/imagegallery/Bundle.properties OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/imagegallery/Bundle.properties

View File

@ -11,4 +11,4 @@ ImageGalleryOptionsPanel.descriptionLabel.text=<html>To minimize its startup tim
ImageGalleryOptionsPanel.furtherDescriptionArea.text=If Image Gallery is disabled, only the fact that an update is needed is recorded. If Image Gallery is enabled after ingest, it will do one bulk update based on the results from ingest. If Image Gallery is disabled, you will be prompted to enable it when attempting to open its window. ImageGalleryOptionsPanel.furtherDescriptionArea.text=If Image Gallery is disabled, only the fact that an update is needed is recorded. If Image Gallery is enabled after ingest, it will do one bulk update based on the results from ingest. If Image Gallery is disabled, you will be prompted to enable it when attempting to open its window.
ImageGalleryOptionsPanel.unavailableDuringInjestLabel.text=This setting is unavailable during ingest. ImageGalleryOptionsPanel.unavailableDuringInjestLabel.text=This setting is unavailable during ingest.
ImageGalleryOptionsPanel.groupCategorizationWarningBox.text=Don't show a warning when overwriting categories, by acting on an entire group. ImageGalleryOptionsPanel.groupCategorizationWarningBox.text=Don't show a warning when overwriting categories, by acting on an entire group.
CTL_OpenAction=Open Image/Video CTL_OpenAction=Open Image/Video

View File

@ -219,8 +219,13 @@ public enum FileTypeUtils {
* type. False if a non image/video mimetype. empty Optional if a * type. False if a non image/video mimetype. empty Optional if a
* mimetype could not be detected. * mimetype could not be detected.
*/ */
static boolean hasDrawableMIMEType(AbstractFile file) throws FileTypeDetector.FileTypeDetectorInitException { static boolean hasDrawableMIMEType(AbstractFile file) {
String mimeType = getFileTypeDetector().getMIMEType(file).toLowerCase(); String mimeType = file.getMIMEType();
if (mimeType == null) {
return false;
}
mimeType = mimeType.toLowerCase();
return isDrawableMimeType(mimeType) || (mimeType.equals("audio/x-aiff") && "tiff".equalsIgnoreCase(file.getNameExtension())); return isDrawableMimeType(mimeType) || (mimeType.equals("audio/x-aiff") && "tiff".equalsIgnoreCase(file.getNameExtension()));
} }
@ -234,13 +239,13 @@ public enum FileTypeUtils {
* available, a video extension. * available, a video extension.
*/ */
public static boolean hasVideoMIMEType(AbstractFile file) { public static boolean hasVideoMIMEType(AbstractFile file) {
try { String mimeType = file.getMIMEType();
String mimeType = getFileTypeDetector().getMIMEType(file).toLowerCase(); if (mimeType == null) {
return mimeType.startsWith("video/") || videoMimeTypes.contains(mimeType);
} catch (FileTypeDetector.FileTypeDetectorInitException ex) {
LOGGER.log(Level.SEVERE, "Error determining MIME type of " + getContentPathSafe(file), ex);
return false; return false;
} }
mimeType = mimeType.toLowerCase();
return mimeType.startsWith("video/") || videoMimeTypes.contains(mimeType);
} }
/** /**

View File

@ -22,6 +22,8 @@ import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -67,8 +69,8 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.HashSetManager;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupManager; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupManager;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState; import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
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.DataSource; import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
@ -205,8 +207,8 @@ public final class ImageGalleryController {
// 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 we just turned on listening and a single-user case is open and that case is not up to date, then rebuild it
// For multiuser cases, we defer DB rebuild till the user actually opens Image Gallery // For multiuser cases, we defer DB rebuild till the user actually opens Image Gallery
if (isEnabled && !wasPreviouslyEnabled if (isEnabled && !wasPreviouslyEnabled
&& isDataSourcesTableStale() && isDataSourcesTableStale()
&& (Case.getCurrentCaseThrows().getCaseType() == CaseType.SINGLE_USER_CASE)) { && (Case.getCurrentCaseThrows().getCaseType() == CaseType.SINGLE_USER_CASE)) {
//populate the db //populate the db
this.rebuildDB(); this.rebuildDB();
} }
@ -232,19 +234,19 @@ public final class ImageGalleryController {
dbTaskQueueSize.addListener(obs -> this.updateRegroupDisabled()); dbTaskQueueSize.addListener(obs -> this.updateRegroupDisabled());
} }
/** /**
* @return Currently displayed group or null if nothing is being displayed * @return Currently displayed group or null if nothing is being displayed
*/ */
public GroupViewState getViewState() { public GroupViewState getViewState() {
return historyManager.getCurrentState(); return historyManager.getCurrentState();
} }
/** /**
* Get observable property of the current group. The UI currently changes * Get observable property of the current group. The UI currently changes
* based on this property changing, which happens when other actions and * based on this property changing, which happens when other actions and
* threads call advance(). * threads call advance().
* *
* @return Currently displayed group (as a property that can be observed) * @return Currently displayed group (as a property that can be observed)
*/ */
public ReadOnlyObjectProperty<GroupViewState> viewStateProperty() { public ReadOnlyObjectProperty<GroupViewState> viewStateProperty() {
@ -253,7 +255,8 @@ public final class ImageGalleryController {
/** /**
* Should the "forward" button on the history be enabled? * Should the "forward" button on the history be enabled?
* @return *
* @return
*/ */
public ReadOnlyBooleanProperty getCanAdvance() { public ReadOnlyBooleanProperty getCanAdvance() {
return historyManager.getCanAdvance(); return historyManager.getCanAdvance();
@ -261,19 +264,19 @@ public final class ImageGalleryController {
/** /**
* Should the "Back" button on the history be enabled? * Should the "Back" button on the history be enabled?
* @return *
* @return
*/ */
public ReadOnlyBooleanProperty getCanRetreat() { public ReadOnlyBooleanProperty getCanRetreat() {
return historyManager.getCanRetreat(); return historyManager.getCanRetreat();
} }
/** /**
* Display the passed in group. Causes this group to * Display the passed in group. Causes this group to get recorded in the
* get recorded in the history queue and observers of the * history queue and observers of the current state will be notified and
* current state will be notified and update their panels/widgets * update their panels/widgets appropriately.
* appropriately. *
* * @param newState
* @param newState
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.ANY) @ThreadConfined(type = ThreadConfined.ThreadType.ANY)
public void advance(GroupViewState newState) { public void advance(GroupViewState newState) {
@ -282,7 +285,8 @@ public final class ImageGalleryController {
/** /**
* Display the next group in the "forward" history stack * Display the next group in the "forward" history stack
* @return *
* @return
*/ */
public GroupViewState advance() { public GroupViewState advance() {
return historyManager.advance(); return historyManager.advance();
@ -290,7 +294,8 @@ public final class ImageGalleryController {
/** /**
* Display the previous group in the "back" history stack * Display the previous group in the "back" history stack
* @return *
* @return
*/ */
public GroupViewState retreat() { public GroupViewState retreat() {
return historyManager.retreat(); return historyManager.retreat();
@ -322,6 +327,7 @@ public final class ImageGalleryController {
groupManager.reset(); groupManager.reset();
shutDownDBExecutor(); shutDownDBExecutor();
drawableDB.close();
dbExecutor = getNewDBExecutor(); dbExecutor = getNewDBExecutor();
} }
@ -382,6 +388,51 @@ public final class ImageGalleryController {
} }
/**
* Returns a map of all data source object ids, along with their DB build
* status.
*
* This includes any data sources already in the table, and any data sources
* that might have been added to the case, but are not in the datasources
* table.
*
* @return map of data source object ids and their Db build status.
*/
public Map<Long, DrawableDbBuildStatusEnum> getAllDataSourcesDrawableDBStatus() {
Map<Long, DrawableDbBuildStatusEnum> dataSourceStatusMap = new HashMap<>();
// no current case open to check
if ((null == getDatabase()) || (null == getSleuthKitCase())) {
return dataSourceStatusMap;
}
try {
Map<Long, DrawableDbBuildStatusEnum> knownDataSourceIds = getDatabase().getDataSourceDbBuildStatus();
List<DataSource> dataSources = getSleuthKitCase().getDataSources();
Set<Long> caseDataSourceIds = new HashSet<>();
dataSources.stream().map(DataSource::getId).forEach(caseDataSourceIds::add);
// collect all data sources already in the table
knownDataSourceIds.entrySet().stream().forEach((Map.Entry<Long, DrawableDbBuildStatusEnum> t) -> {
dataSourceStatusMap.put(t.getKey(), t.getValue());
});
// collect any new data sources in the case.
caseDataSourceIds.forEach((Long id) -> {
if (!knownDataSourceIds.containsKey(id)) {
dataSourceStatusMap.put(id, DrawableDbBuildStatusEnum.UNKNOWN);
}
});
return dataSourceStatusMap;
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Image Gallery failed to get data source DB status.", ex);
return dataSourceStatusMap;
}
}
public boolean hasTooManyFiles(DataSource datasource) throws TskCoreException { public boolean hasTooManyFiles(DataSource datasource) throws TskCoreException {
String whereClause = (datasource == null) String whereClause = (datasource == null)
? "1 = 1" ? "1 = 1"
@ -391,6 +442,30 @@ public final class ImageGalleryController {
} }
/**
* Checks if the given data source has any files with no mimetype
*
* @param datasource
*
* @return true if the datasource has any files with no mime type
*
* @throws TskCoreException
*/
public boolean hasFilesWithNoMimetype(Content datasource) throws TskCoreException {
// There are some special files/attributes in the root folder, like $BadClus:$Bad and $Security:$SDS
// The IngestTasksScheduler does not push them down to the ingest modules,
// and hence they do not have any assigned mimetype
String whereClause = "data_source_obj_id = " + datasource.getId()
+ " AND ( meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() + ")"
+ " AND ( mime_type IS NULL )"
+ " AND ( meta_addr >= 32 ) "
+ " AND ( parent_path <> '/' )"
+ " AND ( name NOT like '$%:%' )";
return sleuthKitCase.countFilesWhere(whereClause) > 0;
}
synchronized private void shutDownDBExecutor() { synchronized private void shutDownDBExecutor() {
if (dbExecutor != null) { if (dbExecutor != null) {
dbExecutor.shutdownNow(); dbExecutor.shutdownNow();
@ -433,10 +508,6 @@ public final class ImageGalleryController {
return drawableDB.getFileFromID(fileID); return drawableDB.getFileFromID(fileID);
} }
public ReadOnlyDoubleProperty regroupProgress() {
return groupManager.regroupProgress();
}
public HashSetManager getHashSetManager() { public HashSetManager getHashSetManager() {
return hashSetManager; return hashSetManager;
} }
@ -528,10 +599,11 @@ public final class ImageGalleryController {
} }
} }
/** /**
* Abstract base class for tasks associated with a file in the database * task that updates one file in database with results from ingest
*/ */
static abstract class FileTask extends BackgroundTask { static class UpdateFileTask extends BackgroundTask {
private final AbstractFile file; private final AbstractFile file;
private final DrawableDB taskDB; private final DrawableDB taskDB;
@ -543,22 +615,12 @@ public final class ImageGalleryController {
public AbstractFile getFile() { public AbstractFile getFile() {
return file; return file;
} }
FileTask(AbstractFile f, DrawableDB taskDB) { UpdateFileTask(AbstractFile f, DrawableDB taskDB) {
super(); super();
this.file = f; this.file = f;
this.taskDB = taskDB; this.taskDB = taskDB;
} }
}
/**
* task that updates one file in database with results from ingest
*/
static class UpdateFileTask extends FileTask {
UpdateFileTask(AbstractFile f, DrawableDB taskDB) {
super(f, taskDB);
}
/** /**
* Update a file in the database * Update a file in the database
@ -568,41 +630,12 @@ public final class ImageGalleryController {
try { try {
DrawableFile drawableFile = DrawableFile.create(getFile(), true, false); DrawableFile drawableFile = DrawableFile.create(getFile(), true, false);
getTaskDB().updateFile(drawableFile); getTaskDB().updateFile(drawableFile);
} catch (NullPointerException ex) { } catch (TskCoreException | SQLException ex) {
// This is one of the places where we get many errors if the case is closed during processing. Logger.getLogger(UpdateFileTask.class.getName()).log(Level.SEVERE, "Error in update file task", ex); //NON-NLS
// We don't want to print out a ton of exceptions if this is the case.
if (Case.isCaseOpen()) {
Logger.getLogger(UpdateFileTask.class.getName()).log(Level.SEVERE, "Error in UpdateFile task"); //NON-NLS
}
} }
} }
} }
/**
* task that updates one file in database with results from ingest
*/
static class RemoveFileTask extends FileTask {
RemoveFileTask(AbstractFile f, DrawableDB taskDB) {
super(f, taskDB);
}
/**
* Update a file in the database
*/
@Override
public void run() {
try {
getTaskDB().removeFile(getFile().getId());
} catch (NullPointerException ex) {
// This is one of the places where we get many errors if the case is closed during processing.
// We don't want to print out a ton of exceptions if this is the case.
if (Case.isCaseOpen()) {
Logger.getLogger(RemoveFileTask.class.getName()).log(Level.SEVERE, "Case was closed out from underneath RemoveFile task"); //NON-NLS
}
}
}
}
/** /**
* Base abstract class for various methods of copying image files data, for * Base abstract class for various methods of copying image files data, for
@ -615,13 +648,13 @@ public final class ImageGalleryController {
static private final String FILE_EXTENSION_CLAUSE static private final String FILE_EXTENSION_CLAUSE
= "(extension LIKE '" //NON-NLS = "(extension LIKE '" //NON-NLS
+ String.join("' OR extension 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
+ String.join("' OR mime_type LIKE '", FileTypeUtils.getAllSupportedMimeTypes()) //NON-NLS + String.join("' OR mime_type LIKE '", FileTypeUtils.getAllSupportedMimeTypes()) //NON-NLS
+ "') "; + "') ";
private final String DRAWABLE_QUERY; private final String DRAWABLE_QUERY;
private final String DATASOURCE_CLAUSE; private final String DATASOURCE_CLAUSE;
@ -644,14 +677,14 @@ public final class ImageGalleryController {
DRAWABLE_QUERY DRAWABLE_QUERY
= DATASOURCE_CLAUSE = DATASOURCE_CLAUSE
+ " AND ( meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() + ")" + " AND ( meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() + ")"
+ " AND ( " + " AND ( "
+ //grab files with supported extension + //grab files with supported extension
FILE_EXTENSION_CLAUSE FILE_EXTENSION_CLAUSE
//grab files with supported mime-types //grab files with supported mime-types
+ " OR " + MIMETYPE_CLAUSE //NON-NLS + " OR " + MIMETYPE_CLAUSE //NON-NLS
//grab files with image or video mime-types even if we don't officially support them //grab files with image or video mime-types even if we don't officially support them
+ " OR mime_type LIKE 'video/%' OR mime_type LIKE 'image/%' )"; //NON-NLS + " OR mime_type LIKE 'video/%' OR mime_type LIKE 'image/%' )"; //NON-NLS
} }
/** /**
@ -696,8 +729,18 @@ public final class ImageGalleryController {
// Cycle through all of the files returned and call processFile on each // Cycle through all of the files returned and call processFile on each
//do in transaction //do in transaction
drawableDbTransaction = taskDB.beginTransaction(); drawableDbTransaction = taskDB.beginTransaction();
caseDbTransaction = tskCase.beginTransaction();
/*
* We are going to periodically commit the CaseDB transaction
* and sleep so that the user can have Autopsy do other stuff
* while these bulk tasks are ongoing.
*/
int caseDbCounter = 0;
for (final AbstractFile f : files) { for (final AbstractFile f : files) {
if (caseDbTransaction == null) {
caseDbTransaction = tskCase.beginTransaction();
}
if (isCancelled() || Thread.interrupted()) { if (isCancelled() || Thread.interrupted()) {
logger.log(Level.WARNING, "Task cancelled or interrupted: not all contents may be transfered to drawable database."); //NON-NLS logger.log(Level.WARNING, "Task cancelled or interrupted: not all contents may be transfered to drawable database."); //NON-NLS
taskCompletionStatus = false; taskCompletionStatus = false;
@ -712,6 +755,14 @@ public final class ImageGalleryController {
progressHandle.progress(f.getName(), workDone); progressHandle.progress(f.getName(), workDone);
updateProgress(workDone - 1 / (double) files.size()); updateProgress(workDone - 1 / (double) files.size());
updateMessage(f.getName()); updateMessage(f.getName());
// Periodically, commit the transaction (which frees the lock) and sleep
// to allow other threads to get some work done in CaseDB
if ((++caseDbCounter % 200) == 0) {
caseDbTransaction.commit();
caseDbTransaction = null;
Thread.sleep(500); // 1/2 second
}
} }
progressHandle.finish(); progressHandle.finish();
@ -720,33 +771,43 @@ public final class ImageGalleryController {
updateProgress(1.0); updateProgress(1.0);
progressHandle.start(); progressHandle.start();
caseDbTransaction.commit(); if (caseDbTransaction != null) {
caseDbTransaction = null; caseDbTransaction.commit();
caseDbTransaction = null;
}
// pass true so that groupmanager is notified of the changes // pass true so that groupmanager is notified of the changes
taskDB.commitTransaction(drawableDbTransaction, true); taskDB.commitTransaction(drawableDbTransaction, true);
drawableDbTransaction = null; drawableDbTransaction = null;
} catch (TskCoreException ex) { } catch (TskCoreException | SQLException | InterruptedException ex) {
progressHandle.progress(Bundle.BulkTask_stopCopy_status());
logger.log(Level.WARNING, "Stopping copy to drawable db task. Failed to transfer all database contents", ex); //NON-NLS
MessageNotifyUtil.Notify.warn(Bundle.BulkTask_errPopulating_errMsg(), ex.getMessage());
cleanup(false);
return;
} finally {
if (null != drawableDbTransaction) {
taskDB.rollbackTransaction(drawableDbTransaction);
}
if (null != caseDbTransaction) { if (null != caseDbTransaction) {
try { try {
caseDbTransaction.rollback(); caseDbTransaction.rollback();
} catch (TskCoreException ex2) { } catch (TskCoreException ex2) {
logger.log(Level.SEVERE, "Error in trying to rollback transaction", ex2); //NON-NLS logger.log(Level.SEVERE, String.format("Failed to roll back case db transaction after error: %s", ex.getMessage()), ex2); //NON-NLS
} }
} }
progressHandle.finish(); if (null != drawableDbTransaction) {
if (taskCompletionStatus) { try {
taskDB.insertOrUpdateDataSource(dataSourceObjId, DrawableDB.DrawableDbBuildStatusEnum.COMPLETE); taskDB.rollbackTransaction(drawableDbTransaction);
} catch (SQLException ex2) {
logger.log(Level.SEVERE, String.format("Failed to roll back drawables db transaction after error: %s", ex.getMessage()), ex2); //NON-NLS
}
} }
progressHandle.progress(Bundle.BulkTask_stopCopy_status());
logger.log(Level.WARNING, "Stopping copy to drawable db task. Failed to transfer all database contents", ex); //NON-NLS
MessageNotifyUtil.Notify.warn(Bundle.BulkTask_errPopulating_errMsg(), ex.getMessage());
cleanup(false);
} finally {
progressHandle.finish();
DrawableDB.DrawableDbBuildStatusEnum datasourceDrawableDBStatus
= (taskCompletionStatus)
? DrawableDB.DrawableDbBuildStatusEnum.COMPLETE
: DrawableDB.DrawableDbBuildStatusEnum.DEFAULT;
taskDB.insertOrUpdateDataSource(dataSourceObjId, datasourceDrawableDBStatus);
updateMessage(""); updateMessage("");
updateProgress(-1.0); updateProgress(-1.0);
} }
@ -792,20 +853,16 @@ public final class ImageGalleryController {
if (known) { if (known) {
taskDB.removeFile(f.getId(), tr); //remove known files taskDB.removeFile(f.getId(), tr); //remove known files
} else { } else {
try { // if mimetype of the file hasn't been ascertained, ingest might not have completed yet.
// if mimetype of the file hasn't been ascertained, ingest might not have completed yet. if (null == f.getMIMEType()) {
if (null == f.getMIMEType()) { // set to false to force the DB to be marked as stale
// set to false to force the DB to be marked as stale this.setTaskCompletionStatus(false);
this.setTaskCompletionStatus(false); } //supported mimetype => analyzed
} //supported mimetype => analyzed else if (FileTypeUtils.hasDrawableMIMEType(f)) {
else if (FileTypeUtils.hasDrawableMIMEType(f)) { taskDB.updateFile(DrawableFile.create(f, true, false), tr, caseDbTransaction);
taskDB.updateFile(DrawableFile.create(f, true, false), tr, caseDbTransaction); } //unsupported mimtype => analyzed but shouldn't include
} //unsupported mimtype => analyzed but shouldn't include else {
else { taskDB.removeFile(f.getId(), tr);
taskDB.removeFile(f.getId(), tr);
}
} catch (FileTypeDetector.FileTypeDetectorInitException ex) {
throw new TskCoreException("Failed to initialize FileTypeDetector.", ex);
} }
} }
} }
@ -817,36 +874,4 @@ public final class ImageGalleryController {
} }
} }
/**
* Copy files from a newly added data source into the DB. Get all "drawable"
* files, based on extension and mime-type. After ingest we use file type id
* module and if necessary jpeg/png signature matching to add/remove files
*/
@NbBundle.Messages({"PrePopulateDataSourceFiles.committingDb.status=committing image/video database"})
static class PrePopulateDataSourceFiles extends BulkTransferTask {
/**
* @param dataSourceObjId The object ID of the DataSource that is being
* pre-populated into the DrawableDB.
* @param controller The controller for this task.
*/
PrePopulateDataSourceFiles(long dataSourceObjId, ImageGalleryController controller) {
super(dataSourceObjId, controller);
}
@Override
protected void cleanup(boolean success) {
}
@Override
void processFile(final AbstractFile f, DrawableDB.DrawableTransaction tr, CaseDbTransaction caseDBTransaction) {
taskDB.insertFile(DrawableFile.create(f, false, false), tr, caseDBTransaction);
}
@Override
@NbBundle.Messages({"PrePopulateDataSourceFiles.prepopulatingDb.status=prepopulating image/video database",})
ProgressHandle getInitialProgressHandle() {
return ProgressHandle.createHandle(Bundle.PrePopulateDataSourceFiles_prepopulatingDb_status(), this);
}
}
} }

View File

@ -43,6 +43,8 @@ import org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent;
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED; import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED;
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.FILE_DONE; import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.FILE_DONE;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent;
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisStartedEvent;
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.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
@ -154,14 +156,14 @@ public class ImageGalleryModule {
IngestManager.getInstance().removeIngestModuleEventListener(this); IngestManager.getInstance().removeIngestModuleEventListener(this);
return; return;
} }
/* only process individual files in realtime on the node that is /* only process individual files in realtime on the node that is
* running the ingest. on a remote node, image files are processed * running the ingest. on a remote node, image files are processed
* enblock when ingest is complete */ * enblock when ingest is complete */
if (((AutopsyEvent) evt).getSourceType() != AutopsyEvent.SourceType.LOCAL) { if (((AutopsyEvent) evt).getSourceType() != AutopsyEvent.SourceType.LOCAL) {
return; return;
} }
// Bail out if the case is closed // Bail out if the case is closed
try { try {
if (controller == null || Case.getCurrentCaseThrows() == null) { if (controller == null || Case.getCurrentCaseThrows() == null) {
@ -187,14 +189,7 @@ public class ImageGalleryModule {
if (isDrawableAndNotKnown(file)) { if (isDrawableAndNotKnown(file)) {
con.queueDBTask(new ImageGalleryController.UpdateFileTask(file, controller.getDatabase())); con.queueDBTask(new ImageGalleryController.UpdateFileTask(file, controller.getDatabase()));
} }
// Remove it from the DB if it is no longer relevant, but had the correct extension
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 */
con.queueDBTask(new ImageGalleryController.RemoveFileTask(file, controller.getDatabase()));
}
} catch (FileTypeDetector.FileTypeDetectorInitException ex) { } catch (FileTypeDetector.FileTypeDetectorInitException ex) {
logger.log(Level.SEVERE, "Unable to determine if file is drawable and not known. Not making any changes to DB", ex); //NON-NLS 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", MessageNotifyUtil.Notify.error("Image Gallery Error",
@ -208,8 +203,8 @@ public class ImageGalleryModule {
} }
} }
else if (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName()) == DATA_ADDED) { else if (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName()) == DATA_ADDED) {
ModuleDataEvent mde = (ModuleDataEvent)evt.getOldValue(); ModuleDataEvent mde = (ModuleDataEvent) evt.getOldValue();
if (mde.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) { if (mde.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
DrawableDB drawableDB = controller.getDatabase(); DrawableDB drawableDB = controller.getDatabase();
if (mde.getArtifacts() != null) { if (mde.getArtifacts() != null) {
@ -281,20 +276,20 @@ public class ImageGalleryModule {
//For a data source added on the local node, prepopulate all file data to drawable database //For a data source added on the local node, prepopulate all file data to drawable database
if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.LOCAL) { if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.LOCAL) {
Content newDataSource = (Content) evt.getNewValue(); Content newDataSource = (Content) evt.getNewValue();
if (con.isListeningEnabled()) { if (con.isListeningEnabled()) {
con.queueDBTask(new ImageGalleryController.PrePopulateDataSourceFiles(newDataSource.getId(), controller)); controller.getDatabase().insertOrUpdateDataSource(newDataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.DEFAULT);
} }
} }
break; break;
case CONTENT_TAG_ADDED: case CONTENT_TAG_ADDED:
final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) evt; final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) evt;
long objId = tagAddedEvent.getAddedTag().getContent().getId(); long objId = tagAddedEvent.getAddedTag().getContent().getId();
// update the cache // update the cache
DrawableDB drawableDB = controller.getDatabase(); DrawableDB drawableDB = controller.getDatabase();
drawableDB.addTagCache(objId); drawableDB.addTagCache(objId);
if (con.getDatabase().isInDB(objId)) { if (con.getDatabase().isInDB(objId)) {
con.getTagsManager().fireTagAddedEvent(tagAddedEvent); con.getTagsManager().fireTagAddedEvent(tagAddedEvent);
} }
@ -326,38 +321,68 @@ public class ImageGalleryModule {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
IngestJobEvent eventType = IngestJobEvent.valueOf(evt.getPropertyName()); IngestJobEvent eventType = IngestJobEvent.valueOf(evt.getPropertyName());
if (eventType != IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED
|| ((AutopsyEvent) evt).getSourceType() != AutopsyEvent.SourceType.REMOTE) {
return;
}
// 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
try { try {
ImageGalleryController con = getController(); ImageGalleryController controller = getController();
con.setStale(true);
if (con.isListeningEnabled() && ImageGalleryTopComponent.isImageGalleryOpen()) { if (eventType == IngestJobEvent.DATA_SOURCE_ANALYSIS_STARTED) {
SwingUtilities.invokeLater(() -> {
int showAnswer = JOptionPane.showConfirmDialog(ImageGalleryTopComponent.getTopComponent(), if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.LOCAL) {
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_msg(), if (controller.isListeningEnabled()) {
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_title(), DataSourceAnalysisStartedEvent dataSourceAnalysisStartedEvent = (DataSourceAnalysisStartedEvent) evt;
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); Content dataSource = dataSourceAnalysisStartedEvent.getDataSource();
switch (showAnswer) { controller.getDatabase().insertOrUpdateDataSource(dataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS);
case JOptionPane.YES_OPTION:
con.rebuildDB();
break;
case JOptionPane.NO_OPTION:
case JOptionPane.CANCEL_OPTION:
default:
break; //do nothing
} }
}); }
} else if (eventType == IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED) {
if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.LOCAL) {
if (controller.isListeningEnabled()) {
DataSourceAnalysisCompletedEvent dataSourceAnalysisCompletedEvent = (DataSourceAnalysisCompletedEvent) evt;
Content dataSource = dataSourceAnalysisCompletedEvent.getDataSource();
DrawableDB.DrawableDbBuildStatusEnum datasourceDrawableDBStatus =
controller.hasFilesWithNoMimetype(dataSource) ?
DrawableDB.DrawableDbBuildStatusEnum.DEFAULT :
DrawableDB.DrawableDbBuildStatusEnum.COMPLETE;
controller.getDatabase().insertOrUpdateDataSource(dataSource.getId(), datasourceDrawableDBStatus);
}
return;
}
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
controller.setStale(true);
if (controller.isListeningEnabled()) {
SwingUtilities.invokeLater(() -> {
if (ImageGalleryTopComponent.isImageGalleryOpen()) {
int showAnswer = JOptionPane.showConfirmDialog(ImageGalleryTopComponent.getTopComponent(),
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_msg(),
Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_title(),
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
switch (showAnswer) {
case JOptionPane.YES_OPTION:
controller.rebuildDB();
break;
case JOptionPane.NO_OPTION:
case JOptionPane.CANCEL_OPTION:
default:
break; //do nothing
}
}
});
}
}
} }
} catch (NoCurrentCaseException ex) { }
logger.log(Level.SEVERE, "Attempted to access ImageGallery with no case open.", ex); //NON-NLS catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Attempted to access ImageGallery with no case open.", ex); //NON-NLS
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting ImageGalleryController.", ex); //NON-NLS logger.log(Level.SEVERE, "Error getting ImageGalleryController.", ex); //NON-NLS
} }
} }
} }

View File

@ -18,13 +18,10 @@
*/ */
package org.sleuthkit.autopsy.imagegallery; package org.sleuthkit.autopsy.imagegallery;
import java.util.ArrayList;
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.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javafx.application.Platform; import javafx.application.Platform;
@ -55,10 +52,8 @@ import javafx.stage.Modality;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import static org.apache.commons.lang3.ObjectUtils.notEqual; import static org.apache.commons.lang3.ObjectUtils.notEqual;
import org.apache.commons.lang3.StringUtils;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils; import org.openide.explorer.ExplorerUtils;
import org.openide.util.Exceptions;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
@ -232,8 +227,8 @@ public final class ImageGalleryTopComponent extends TopComponent implements Expl
@SuppressWarnings(value = "unchecked") @SuppressWarnings(value = "unchecked")
ComboBox<Optional<DataSource>> comboBox = (ComboBox<Optional<DataSource>>) datasourceDialog.getDialogPane().lookup(".combo-box"); ComboBox<Optional<DataSource>> comboBox = (ComboBox<Optional<DataSource>>) datasourceDialog.getDialogPane().lookup(".combo-box");
//set custom cell renderer //set custom cell renderer
comboBox.setCellFactory((ListView<Optional<DataSource>> param) -> new DataSourceCell(dataSourcesTooManyFiles)); comboBox.setCellFactory((ListView<Optional<DataSource>> param) -> new DataSourceCell(dataSourcesTooManyFiles, controller.getAllDataSourcesDrawableDBStatus()));
comboBox.setButtonCell(new DataSourceCell(dataSourcesTooManyFiles)); comboBox.setButtonCell(new DataSourceCell(dataSourcesTooManyFiles, controller.getAllDataSourcesDrawableDBStatus()));
DataSource dataSource = datasourceDialog.showAndWait().orElse(Optional.empty()).orElse(null); DataSource dataSource = datasourceDialog.showAndWait().orElse(Optional.empty()).orElse(null);
try { try {

View File

@ -19,11 +19,10 @@
package org.sleuthkit.autopsy.imagegallery.actions; package org.sleuthkit.autopsy.imagegallery.actions;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
import java.awt.Component; import java.awt.Component;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import static java.util.concurrent.Executors.newSingleThreadExecutor; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
@ -32,7 +31,6 @@ import javafx.scene.control.CheckBox;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.stage.Modality; import javafx.stage.Modality;
import javax.annotation.concurrent.ThreadSafe;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
@ -50,11 +48,12 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.Installer; import org.sleuthkit.autopsy.core.Installer;
import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.RuntimeProperties;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryModule; import org.sleuthkit.autopsy.imagegallery.ImageGalleryModule;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryPreferences; import org.sleuthkit.autopsy.imagegallery.ImageGalleryPreferences;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryTopComponent; import org.sleuthkit.autopsy.imagegallery.ImageGalleryTopComponent;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB.DrawableDbBuildStatusEnum;
import org.sleuthkit.autopsy.imagegallery.gui.GuiUtils; import org.sleuthkit.autopsy.imagegallery.gui.GuiUtils;
import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils; import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils;
import static org.sleuthkit.autopsy.imagegallery.utils.TaskUtils.addFXCallback; import static org.sleuthkit.autopsy.imagegallery.utils.TaskUtils.addFXCallback;
@ -70,6 +69,8 @@ import org.sleuthkit.datamodel.TskCoreException;
"OpenAction.stale.confDlg.msg=The image / video database may be out of date. " "OpenAction.stale.confDlg.msg=The image / video database may be out of date. "
+ "Do you want to update and listen for further ingest results?\n" + "Do you want to update and listen for further ingest results?\n"
+ "Choosing 'yes' will update the database and enable listening to future ingests.", + "Choosing 'yes' will update the database and enable listening to future ingests.",
"OpenAction.notAnalyzedDlg.msg=No image/video files available to display yet.\n"
+ "Please run FileType and EXIF ingest modules.",
"OpenAction.stale.confDlg.title=Image Gallery"}) "OpenAction.stale.confDlg.title=Image Gallery"})
public final class OpenAction extends CallableSystemAction { public final class OpenAction extends CallableSystemAction {
@ -184,17 +185,43 @@ public final class OpenAction extends CallableSystemAction {
} }
private void checkDBStale(ImageGalleryController controller) { private void checkDBStale(ImageGalleryController controller) {
//check if db is stale on throw away bg thread and then react back on jfx thread.
ListenableFuture<Boolean> staleFuture = TaskUtils.getExecutorForClass(OpenAction.class) ListenableFuture<Map<Long, DrawableDB.DrawableDbBuildStatusEnum>> dataSourceStatusMapFuture = TaskUtils.getExecutorForClass(OpenAction.class)
.submit(controller::isDataSourcesTableStale); .submit(controller::getAllDataSourcesDrawableDBStatus);
addFXCallback(staleFuture,
dbIsStale -> { addFXCallback(dataSourceStatusMapFuture,
dataSourceStatusMap -> {
boolean dbIsStale = false;
for (Map.Entry<Long, DrawableDbBuildStatusEnum> entry : dataSourceStatusMap.entrySet()) {
DrawableDbBuildStatusEnum status = entry.getValue();
if (DrawableDbBuildStatusEnum.COMPLETE != status) {
dbIsStale = true;
}
}
//back on fx thread. //back on fx thread.
if (false == dbIsStale) { if (false == dbIsStale) {
//drawable db is not stale, just open it //drawable db is not stale, just open it
openTopComponent(); openTopComponent();
} else { } else {
//drawable db is stale, ask what to do
// If there is only one datasource and it's in DEFAULT State -
// ingest modules need to be run on the data source
if (dataSourceStatusMap.size()== 1) {
Map.Entry<Long, DrawableDB.DrawableDbBuildStatusEnum> entry = dataSourceStatusMap.entrySet().iterator().next();
if (entry.getValue() == DrawableDbBuildStatusEnum.DEFAULT ) {
Alert alert = new Alert(Alert.AlertType.WARNING, Bundle.OpenAction_notAnalyzedDlg_msg(), ButtonType.OK);
alert.setTitle(Bundle.OpenAction_stale_confDlg_title());
alert.initModality(Modality.APPLICATION_MODAL);
alert.showAndWait();
return;
}
}
//drawable db is stale,
//ask what to do
Alert alert = new Alert(Alert.AlertType.WARNING, Alert alert = new Alert(Alert.AlertType.WARNING,
Bundle.OpenAction_stale_confDlg_msg(), Bundle.OpenAction_stale_confDlg_msg(),
ButtonType.YES, ButtonType.NO, ButtonType.CANCEL); ButtonType.YES, ButtonType.NO, ButtonType.CANCEL);

View File

@ -33,7 +33,9 @@ import org.sleuthkit.datamodel.TskCoreException;
*/ */
public class HashSetManager { public class HashSetManager {
/** The db that initial values are loaded from. */ /**
* The db that initial values are loaded from.
*/
private final DrawableDB drawableDB; private final DrawableDB drawableDB;
public HashSetManager(DrawableDB drawableDB) { public HashSetManager(DrawableDB drawableDB) {
@ -54,14 +56,9 @@ public class HashSetManager {
*/ */
private Set<String> getHashSetsForFileHelper(long fileID) { private Set<String> getHashSetsForFileHelper(long fileID) {
try { try {
if (drawableDB.isClosed()) { return drawableDB.getHashSetsForFile(fileID);
Logger.getLogger(HashSetManager.class.getName()).log(Level.WARNING, "Failed to get Hash Sets for file. The Db connection was already closed."); //NON-NLS } catch (TskCoreException ex) {
return Collections.emptySet(); Logger.getLogger(HashSetManager.class.getName()).log(Level.SEVERE, String.format("Failed to get hash sets for file (id=%d)", fileID), ex); //NON-NLS
} else {
return drawableDB.getHashSetsForFile(fileID);
}
} catch (TskCoreException | SQLException ex) {
Logger.getLogger(HashSetManager.class.getName()).log(Level.SEVERE, "Failed to get Hash Sets for file."); //NON-NLS
return Collections.emptySet(); return Collections.emptySet();
} }
} }

View File

@ -68,13 +68,6 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
DrawableGroup(GroupKey<?> groupKey, Set<Long> filesInGroup, boolean seen) { DrawableGroup(GroupKey<?> groupKey, Set<Long> filesInGroup, boolean seen) {
this.groupKey = groupKey; this.groupKey = groupKey;
this.fileIDs.setAll(filesInGroup); this.fileIDs.setAll(filesInGroup);
fileIDs.addListener((ListChangeListener.Change<? extends Long> listchange) -> {
boolean seenChanged = false;
while (false == seenChanged && listchange.next()) {
seenChanged |= listchange.wasAdded();
}
invalidateProperties(seenChanged);
});
this.seen.set(seen); this.seen.set(seen);
} }
@ -183,15 +176,21 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
if (fileIDs.contains(f) == false) { if (fileIDs.contains(f) == false) {
fileIDs.add(f); fileIDs.add(f);
} }
// invalidate no matter what because the file could have new hash hits, etc.
invalidateProperties(true);
} }
synchronized void setFiles(Set<? extends Long> newFileIds) { synchronized void setFiles(Set<? extends Long> newFileIds) {
fileIDs.removeIf(fileID -> newFileIds.contains(fileID) == false); fileIDs.removeIf(fileID -> newFileIds.contains(fileID) == false);
invalidateProperties(false);
newFileIds.stream().forEach(this::addFile); newFileIds.stream().forEach(this::addFile);
} }
synchronized void removeFile(Long f) { synchronized void removeFile(Long f) {
fileIDs.removeAll(f); if (fileIDs.contains(f)) {
fileIDs.removeAll(f);
invalidateProperties(false);
}
} }
private void invalidateProperties(boolean seenChanged) { private void invalidateProperties(boolean seenChanged) {

View File

@ -50,6 +50,7 @@ import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyDoubleProperty; import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.concurrent.Service; import javafx.concurrent.Service;
@ -98,18 +99,24 @@ public class GroupManager {
private static final Logger logger = Logger.getLogger(GroupManager.class.getName()); private static final Logger logger = Logger.getLogger(GroupManager.class.getName());
/** An executor to submit async UI related background tasks to. */ /**
* An executor to submit async UI related background tasks to.
*/
private final ListeningExecutorService exec = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor( private final ListeningExecutorService exec = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(
new BasicThreadFactory.Builder().namingPattern("GroupManager BG Thread-%d").build())); //NON-NLS new BasicThreadFactory.Builder().namingPattern("GroupManager BG Thread-%d").build())); //NON-NLS
private final ImageGalleryController controller; private final ImageGalleryController controller;
/** list of all analyzed groups */ /**
* list of all analyzed groups
*/
@GuardedBy("this") //NOPMD @GuardedBy("this") //NOPMD
private final ObservableList<DrawableGroup> analyzedGroups = FXCollections.observableArrayList(); private final ObservableList<DrawableGroup> analyzedGroups = FXCollections.observableArrayList();
private final ObservableList<DrawableGroup> unmodifiableAnalyzedGroups = FXCollections.unmodifiableObservableList(analyzedGroups); private final ObservableList<DrawableGroup> unmodifiableAnalyzedGroups = FXCollections.unmodifiableObservableList(analyzedGroups);
/** list of unseen groups */ /**
* list of unseen groups
*/
@GuardedBy("this") //NOPMD @GuardedBy("this") //NOPMD
private final ObservableList<DrawableGroup> unSeenGroups = FXCollections.observableArrayList(); private final ObservableList<DrawableGroup> unSeenGroups = FXCollections.observableArrayList();
private final ObservableList<DrawableGroup> unmodifiableUnSeenGroups = FXCollections.unmodifiableObservableList(unSeenGroups); private final ObservableList<DrawableGroup> unmodifiableUnSeenGroups = FXCollections.unmodifiableObservableList(unSeenGroups);
@ -266,14 +273,23 @@ public class GroupManager {
try { try {
Examiner examiner = controller.getSleuthKitCase().getCurrentExaminer(); Examiner examiner = controller.getSleuthKitCase().getCurrentExaminer();
getDrawableDB().markGroupSeen(group.getGroupKey(), seen, examiner.getId()); getDrawableDB().markGroupSeen(group.getGroupKey(), seen, examiner.getId());
group.setSeen(seen); // only update and reshuffle if its new results
updateUnSeenGroups(group); if (group.isSeen() != seen) {
group.setSeen(seen);
updateUnSeenGroups(group);
}
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error marking group as seen", ex); //NON-NLS logger.log(Level.SEVERE, String.format("Error setting seen status for group: %s", group.getGroupKey().getValue().toString()), ex); //NON-NLS
} }
}); });
} }
/**
* Update unseenGroups list accordingly based on the current status of
* 'group'. Removes it if it is seen or adds it if it is unseen.
*
* @param group
*/
synchronized private void updateUnSeenGroups(DrawableGroup group) { synchronized private void updateUnSeenGroups(DrawableGroup group) {
if (group.isSeen()) { if (group.isSeen()) {
unSeenGroups.removeAll(group); unSeenGroups.removeAll(group);
@ -477,8 +493,8 @@ public class GroupManager {
setSortOrder(sortOrder); setSortOrder(sortOrder);
//only re-query the db if the data source or group by attribute changed or it is forced //only re-query the db if the data source or group by attribute changed or it is forced
if (dataSource != getDataSource() if (dataSource != getDataSource()
|| groupBy != getGroupBy() || groupBy != getGroupBy()
|| force) { || force) {
setDataSource(dataSource); setDataSource(dataSource);
setGroupBy(groupBy); setGroupBy(groupBy);
@ -496,6 +512,10 @@ public class GroupManager {
return regrouper.progressProperty(); return regrouper.progressProperty();
} }
public ReadOnlyStringProperty regroupMessage() {
return regrouper.messageProperty();
}
@Subscribe @Subscribe
synchronized public void handleTagAdded(ContentTagAddedEvent evt) { synchronized public void handleTagAdded(ContentTagAddedEvent evt) {
GroupKey<?> newGroupKey = null; GroupKey<?> newGroupKey = null;
@ -530,15 +550,18 @@ public class GroupManager {
// NOTE: We assume that it has already been determined that GroupKey can be displayed based on Data Source filters // NOTE: We assume that it has already been determined that GroupKey can be displayed based on Data Source filters
if (group == null) { if (group == null) {
//if there wasn't already a group check if there should be one now //if there wasn't already a DrawableGroup, then check if this group is now
// path group, for example, only gets created when all files are analyzed // in an appropriate state to get one made.
// Path group, for example, only gets a DrawableGroup created when all files are analyzed
group = popuplateIfAnalyzed(groupKey, null); group = popuplateIfAnalyzed(groupKey, null);
} else { } else {
//if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it. //if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it.
group.addFile(fileID); group.addFile(fileID);
} }
// reset the seen status for the group // reset the seen status for the group
markGroupSeen(group, false); if (group != null) {
markGroupSeen(group, false);
}
} }
@Subscribe @Subscribe
@ -607,6 +630,8 @@ public class GroupManager {
* If the group is analyzed (or other criteria based on grouping) and should * If the group is analyzed (or other criteria based on grouping) and should
* be shown to the user, then add it to the appropriate data structures so * be shown to the user, then add it to the appropriate data structures so
* that it can be viewed. * that it can be viewed.
*
* @returns null if Group is not ready to be viewed
*/ */
synchronized private DrawableGroup popuplateIfAnalyzed(GroupKey<?> groupKey, ReGroupTask<?> task) { synchronized private DrawableGroup popuplateIfAnalyzed(GroupKey<?> groupKey, ReGroupTask<?> task) {
/* /*
@ -626,9 +651,9 @@ public class GroupManager {
* analyzed because we don't know all the files that will be a part of * analyzed because we don't know all the files that will be a part of
* that group. just show them no matter what. * that group. just show them no matter what.
*/ */
if (groupKey.getAttribute() != DrawableAttribute.PATH try {
|| getDrawableDB().isGroupAnalyzed(groupKey)) { if (groupKey.getAttribute() != DrawableAttribute.PATH
try { || getDrawableDB().isGroupAnalyzed(groupKey)) {
Set<Long> fileIDs = getFileIDsInGroup(groupKey); Set<Long> fileIDs = getFileIDsInGroup(groupKey);
if (Objects.nonNull(fileIDs)) { if (Objects.nonNull(fileIDs)) {
@ -654,9 +679,9 @@ public class GroupManager {
return group; return group;
} }
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "failed to get files for group: " + groupKey.getAttribute().attrName.toString() + " = " + groupKey.getValue(), ex); //NON-NLS
} }
} catch (SQLException | TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to get files for group: " + groupKey.getAttribute().attrName.toString() + " = " + groupKey.getValue(), ex); //NON-NLS
} }
return null; return null;
@ -716,12 +741,7 @@ public class GroupManager {
*/ */
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
@NbBundle.Messages({"# {0} - groupBy attribute Name", @NbBundle.Messages({"# {0} - groupBy attribute Name",
"# {1} - sortBy name", "ReGroupTask.displayTitle=regrouping by {0}: "})
"# {2} - sort Order",
"ReGroupTask.displayTitle=regrouping files by {0} sorted by {1} in {2} order",
"# {0} - groupBy attribute Name",
"# {1} - atribute value",
"ReGroupTask.progressUpdate=regrouping files by {0} : {1}"})
class ReGroupTask<AttrValType extends Comparable<AttrValType>> extends LoggedTask<Void> { class ReGroupTask<AttrValType extends Comparable<AttrValType>> extends LoggedTask<Void> {
private final DataSource dataSource; private final DataSource dataSource;
@ -729,16 +749,14 @@ public class GroupManager {
private final GroupSortBy sortBy; private final GroupSortBy sortBy;
private final SortOrder sortOrder; private final SortOrder sortOrder;
private final ProgressHandle groupProgress;
ReGroupTask(DataSource dataSource, DrawableAttribute<AttrValType> groupBy, GroupSortBy sortBy, SortOrder sortOrder) { ReGroupTask(DataSource dataSource, DrawableAttribute<AttrValType> groupBy, GroupSortBy sortBy, SortOrder sortOrder) {
super(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString(), sortBy.getDisplayName(), sortOrder.toString()), true); super(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString()), true);
this.dataSource = dataSource; this.dataSource = dataSource;
this.groupBy = groupBy; this.groupBy = groupBy;
this.sortBy = sortBy; this.sortBy = sortBy;
this.sortOrder = sortOrder; this.sortOrder = sortOrder;
groupProgress = ProgressHandle.createHandle(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString(), sortBy.getDisplayName(), sortOrder.toString()), this); updateTitle(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString()));
} }
@Override @Override
@ -747,7 +765,8 @@ public class GroupManager {
if (isCancelled()) { if (isCancelled()) {
return null; return null;
} }
groupProgress.start();
updateProgress(-1, 1);
analyzedGroups.clear(); analyzedGroups.clear();
unSeenGroups.clear(); unSeenGroups.clear();
@ -755,7 +774,7 @@ public class GroupManager {
// Get the list of group keys // Get the list of group keys
Multimap<DataSource, AttrValType> valsByDataSource = findValuesForAttribute(); Multimap<DataSource, AttrValType> valsByDataSource = findValuesForAttribute();
groupProgress.switchToDeterminate(valsByDataSource.entries().size()); updateProgress(0, valsByDataSource.entries().size());
int p = 0; int p = 0;
// For each key value, partially create the group and add it to the list. // For each key value, partially create the group and add it to the list.
for (final Map.Entry<DataSource, AttrValType> valForDataSource : valsByDataSource.entries()) { for (final Map.Entry<DataSource, AttrValType> valForDataSource : valsByDataSource.entries()) {
@ -763,9 +782,8 @@ public class GroupManager {
return null; return null;
} }
p++; p++;
updateMessage(Bundle.ReGroupTask_progressUpdate(groupBy.attrName.toString(), valForDataSource.getValue())); updateMessage(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString()) + valForDataSource.getValue());
updateProgress(p, valsByDataSource.size()); updateProgress(p, valsByDataSource.size());
groupProgress.progress(Bundle.ReGroupTask_progressUpdate(groupBy.attrName.toString(), valForDataSource), p);
popuplateIfAnalyzed(new GroupKey<>(groupBy, valForDataSource.getValue(), valForDataSource.getKey()), this); popuplateIfAnalyzed(new GroupKey<>(groupBy, valForDataSource.getValue(), valForDataSource.getKey()), this);
} }
@ -779,8 +797,8 @@ public class GroupManager {
= viewedKey.map(GroupKey::getAttribute).orElse(null); = viewedKey.map(GroupKey::getAttribute).orElse(null);
if (viewedGroup.isPresent() == false //if no group was being viewed, if (viewedGroup.isPresent() == false //if no group was being viewed,
|| (dataSource != null && notEqual(dataSourceOfCurrentGroup, dataSource)) //or the datasource of the viewed group is wrong, || (dataSource != null && notEqual(dataSourceOfCurrentGroup, dataSource)) //or the datasource of the viewed group is wrong,
|| groupBy != attributeOfCurrentGroup) { // or the groupBy attribute is wrong... || groupBy != attributeOfCurrentGroup) { // or the groupBy attribute is wrong...
//the current group should not be visible so ... //the current group should not be visible so ...
if (isNotEmpty(unSeenGroups)) { if (isNotEmpty(unSeenGroups)) {
@ -794,8 +812,8 @@ public class GroupManager {
} }
} }
} finally { } finally {
groupProgress.finish();
updateProgress(1, 1); updateProgress(1, 1);
updateMessage("");
} }
return null; return null;
} }
@ -813,12 +831,9 @@ public class GroupManager {
} }
/** /**
* find the distinct values for the given column (DrawableAttribute) * Find the distinct values for the given column (DrawableAttribute).
*
* These values represent the groups of files. * These values represent the groups of files.
* *
* @param groupBy
*
* @return map of data source (or null if group by attribute ignores * @return map of data source (or null if group by attribute ignores
* data sources) to list of unique group values * data sources) to list of unique group values
*/ */

View File

@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.imagegallery.gui;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB.DrawableDbBuildStatusEnum;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
/** /**
@ -29,9 +31,23 @@ import org.sleuthkit.datamodel.DataSource;
public class DataSourceCell extends ListCell<Optional<DataSource>> { public class DataSourceCell extends ListCell<Optional<DataSource>> {
private final Map<DataSource, Boolean> dataSourcesTooManyFiles; private final Map<DataSource, Boolean> dataSourcesTooManyFiles;
private final Map<Long, DrawableDB.DrawableDbBuildStatusEnum> dataSourcesDrawableDBStatus;
public DataSourceCell(Map<DataSource, Boolean> dataSourcesViewable) { /**
this.dataSourcesTooManyFiles = dataSourcesViewable; *
* @param dataSourcesTooManyFiles: a map of too many files indicator for
* each data source.
* Data sources with too many files may substantially slow down
* the system and hence are disabled for selection.
* @param dataSourcesDrawableDBStatus a map of drawable DB status for
* each data sources.
* Data sources in DEFAULT state are not fully analyzed yet and are
* disabled for selection.
*/
public DataSourceCell(Map<DataSource, Boolean> dataSourcesTooManyFiles, Map<Long, DrawableDB.DrawableDbBuildStatusEnum> dataSourcesDrawableDBStatus) {
this.dataSourcesTooManyFiles = dataSourcesTooManyFiles;
this.dataSourcesDrawableDBStatus = dataSourcesDrawableDBStatus;
} }
@Override @Override
@ -43,14 +59,28 @@ public class DataSourceCell extends ListCell<Optional<DataSource>> {
DataSource dataSource = item.orElse(null); DataSource dataSource = item.orElse(null);
String text = (dataSource == null) ? "All" : dataSource.getName() + " (Id: " + dataSource.getId() + ")"; String text = (dataSource == null) ? "All" : dataSource.getName() + " (Id: " + dataSource.getId() + ")";
Boolean tooManyFilesInDataSource = dataSourcesTooManyFiles.getOrDefault(dataSource, false); Boolean tooManyFilesInDataSource = dataSourcesTooManyFiles.getOrDefault(dataSource, false);
DrawableDbBuildStatusEnum dataSourceDBStatus = (dataSource != null) ?
dataSourcesDrawableDBStatus.get(dataSource.getId()) : DrawableDbBuildStatusEnum.UNKNOWN;
Boolean dataSourceNotAnalyzed = (dataSourceDBStatus == DrawableDbBuildStatusEnum.DEFAULT);
if (tooManyFilesInDataSource) { if (tooManyFilesInDataSource) {
text += " - Too many files"; text += " - Too many files";
}
if (dataSourceNotAnalyzed) {
text += " - Not Analyzed";
}
// check if item should be disabled
if (tooManyFilesInDataSource || dataSourceNotAnalyzed) {
setDisable(true);
setStyle("-fx-opacity : .5"); setStyle("-fx-opacity : .5");
} else { }
else {
setGraphic(null); setGraphic(null);
setStyle("-fx-opacity : 1"); setStyle("-fx-opacity : 1");
} }
setDisable(tooManyFilesInDataSource);
setText(text); setText(text);
} }
} }

View File

@ -10,12 +10,21 @@
<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?> <?import javafx.scene.layout.StackPane?>
<fx:root id="AnchorPane" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" type="javafx.scene.layout.AnchorPane" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1"> <fx:root id="AnchorPane" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" type="javafx.scene.layout.AnchorPane" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1">
<children> <children>
<BorderPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <BorderPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<right> <right>
<HBox alignment="CENTER_RIGHT" prefHeight="-1.0" prefWidth="-1.0" spacing="5.0" BorderPane.alignment="CENTER_RIGHT"> <HBox alignment="CENTER_RIGHT" prefHeight="-1.0" prefWidth="-1.0" spacing="5.0" BorderPane.alignment="CENTER_RIGHT">
<children> <children>
<Label fx:id="staleLabel" text="Some data may be out of date. Enable listening to ingest to update.">
<graphic>
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../images/information.png" />
</image>
</ImageView>
</graphic>
</Label>
<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" HBox.hgrow="NEVER"> <StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" HBox.hgrow="NEVER">
<children> <children>
<ProgressBar id="progBar" fx:id="fileTaskProgresBar" focusTraversable="false" maxHeight="-1.0" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-1.0" prefHeight="24.0" prefWidth="-1.0" progress="0.0" visible="true" /> <ProgressBar id="progBar" fx:id="fileTaskProgresBar" focusTraversable="false" maxHeight="-1.0" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-1.0" prefHeight="24.0" prefWidth="-1.0" progress="0.0" visible="true" />
@ -31,37 +40,27 @@
<Insets /> <Insets />
</HBox.margin> </HBox.margin>
</StackPane> </StackPane>
<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" HBox.hgrow="NEVER">
<children>
<ProgressBar fx:id="bgTaskProgressBar" maxHeight="-1.0" maxWidth="-1.0" minHeight="-Infinity" minWidth="-1.0" prefHeight="24.0" prefWidth="-1.0" progress="0.0" StackPane.alignment="CENTER" />
<Label fx:id="bgTaskLabel" alignment="CENTER" cache="false" contentDisplay="CENTER" disable="false" focusTraversable="false" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" text="" StackPane.alignment="CENTER">
<StackPane.margin>
<Insets left="3.0" right="3.0" />
</StackPane.margin>
<padding>
<Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
</padding></Label>
</children>
<HBox.margin>
<Insets right="5.0" />
</HBox.margin>
</StackPane>
</children> </children>
<BorderPane.margin> <BorderPane.margin>
<Insets left="10.0" /> <Insets left="10.0" />
</BorderPane.margin> </BorderPane.margin>
</HBox> </HBox>
</right> </right>
<left><Label fx:id="staleLabel" text="Some data may be out of date. Enable listening to ingest to update." BorderPane.alignment="CENTER"> <left>
<graphic><ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" BorderPane.alignment="CENTER">
<image> <children>
<Image url="@../images/information.png" /> <ProgressBar fx:id="regroupProgressBar" maxHeight="-1.0" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-1.0" prefHeight="24.0" prefWidth="500.0" progress="0.0" StackPane.alignment="CENTER_LEFT" />
</image></ImageView> <Label fx:id="regroupLabel" cache="false" contentDisplay="CENTER" disable="false" focusTraversable="false" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="500.0" text="" textOverrun="CENTER_ELLIPSIS" StackPane.alignment="CENTER_LEFT">
</graphic> <StackPane.margin>
<BorderPane.margin> <Insets left="3.0" right="3.0" />
<Insets bottom="5.0" left="5.0" right="10.0" top="5.0" /> </StackPane.margin>
</BorderPane.margin></Label> <padding>
</left> <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
</padding>
</Label>
</children>
</StackPane>
</left>
</BorderPane> </BorderPane>
</children> </children>
</fx:root> </fx:root>

View File

@ -28,28 +28,26 @@ import javafx.scene.control.Tooltip;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupManager;
/** /**
* *
*/ */
public class StatusBar extends AnchorPane { public class StatusBar extends AnchorPane {
private final ImageGalleryController controller;
@FXML @FXML
private ProgressBar fileTaskProgresBar; private ProgressBar fileTaskProgresBar;
@FXML @FXML
private Label fileUpdateTaskLabel; private Label fileUpdateTaskLabel;
@FXML @FXML
private Label bgTaskLabel; private Label regroupLabel;
@FXML @FXML
private Label staleLabel; private Label staleLabel;
@FXML @FXML
private ProgressBar bgTaskProgressBar; private ProgressBar regroupProgressBar;
private final ImageGalleryController controller;
private final GroupManager groupManager;
@FXML @FXML
@NbBundle.Messages({"StatusBar.fileUpdateTaskLabel.text= File Update Tasks", @NbBundle.Messages({"StatusBar.fileUpdateTaskLabel.text= File Update Tasks",
@ -58,23 +56,25 @@ public class StatusBar extends AnchorPane {
void initialize() { void initialize() {
assert fileTaskProgresBar != null : "fx:id=\"fileTaskProgresBar\" was not injected: check your FXML file 'StatusBar.fxml'."; assert fileTaskProgresBar != null : "fx:id=\"fileTaskProgresBar\" was not injected: check your FXML file 'StatusBar.fxml'.";
assert fileUpdateTaskLabel != null : "fx:id=\"fileUpdateTaskLabel\" was not injected: check your FXML file 'StatusBar.fxml'."; assert fileUpdateTaskLabel != null : "fx:id=\"fileUpdateTaskLabel\" was not injected: check your FXML file 'StatusBar.fxml'.";
assert bgTaskLabel != null : "fx:id=\"bgTaskLabel\" was not injected: check your FXML file 'StatusBar.fxml'."; assert regroupLabel != null : "fx:id=\"regroupLabel\" was not injected: check your FXML file 'StatusBar.fxml'.";
assert bgTaskProgressBar != null : "fx:id=\"bgTaskProgressBar\" was not injected: check your FXML file 'StatusBar.fxml'."; assert regroupProgressBar != null : "fx:id=\"regroupProgressBar\" was not injected: check your FXML file 'StatusBar.fxml'.";
fileUpdateTaskLabel.textProperty().bind(controller.getDBTasksQueueSizeProperty().asString().concat(Bundle.StatusBar_fileUpdateTaskLabel_text())); fileUpdateTaskLabel.textProperty().bind(controller.getDBTasksQueueSizeProperty().asString().concat(Bundle.StatusBar_fileUpdateTaskLabel_text()));
fileTaskProgresBar.progressProperty().bind(controller.getDBTasksQueueSizeProperty().negate()); fileTaskProgresBar.progressProperty().bind(controller.getDBTasksQueueSizeProperty().negate());
controller.regroupProgress().addListener((ov, oldSize, newSize) -> { groupManager.regroupProgress().addListener((ov, oldSize, newSize) -> {
Platform.runLater(() -> { Platform.runLater(() -> {
if (controller.regroupProgress().lessThan(1.0).get()) { if (groupManager.regroupProgress().lessThan(1.0).get()) {
// Regrouping in progress // Regrouping in progress
bgTaskProgressBar.progressProperty().setValue(-1.0); regroupProgressBar.progressProperty().setValue(groupManager.regroupProgress().doubleValue());
bgTaskLabel.setText(Bundle.StatusBar_bgTaskLabel_text()); regroupLabel.setText(groupManager.regroupMessage().get());
} else { } else {
// Clear the progress bar // Clear the progress bar
bgTaskProgressBar.progressProperty().setValue(0.0); regroupProgressBar.progressProperty().setValue(0.0);
bgTaskLabel.setText(""); regroupLabel.setText("");
} }
regroupLabel.setTooltip(new Tooltip(regroupLabel.getText()));
}); });
}); });
@ -84,6 +84,7 @@ public class StatusBar extends AnchorPane {
public StatusBar(ImageGalleryController controller) { public StatusBar(ImageGalleryController controller) {
this.controller = controller; this.controller = controller;
this.groupManager = controller.getGroupManager();
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("StatusBar.fxml")); //NON-NLS FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("StatusBar.fxml")); //NON-NLS
fxmlLoader.setRoot(this); fxmlLoader.setRoot(this);
fxmlLoader.setController(this); fxmlLoader.setController(this);
@ -93,6 +94,5 @@ public class StatusBar extends AnchorPane {
} catch (IOException exception) { } catch (IOException exception) {
throw new RuntimeException(exception); throw new RuntimeException(exception);
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More