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

@ -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
} }
} else {
// If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled. // If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled.
if (this.file != null) { } else if (this.file != null && this.file.getSize() > 0) {
String md5 = this.file.getMd5Hash(); String md5 = this.file.getMd5Hash();
if (md5 != null && !md5.isEmpty()) { if (md5 != null && !md5.isEmpty()) {
try { try {
final CorrelationAttributeInstance.Type fileAttributeType final CorrelationAttributeInstance.Type fileAttributeType
= CorrelationAttributeInstance.getDefaultCorrelationTypes() = CorrelationAttributeInstance.getDefaultCorrelationTypes()
.stream() .stream()
.filter(attrType -> attrType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) .filter(attrType -> attrType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID)
.findAny() .findAny()
.get(); .get();
//The Central Repository is not enabled
ret.add(new CorrelationAttributeInstance(fileAttributeType, md5)); ret.add(new CorrelationAttributeInstance(fileAttributeType, md5, null, null, "", "", TskData.FileKnown.UNKNOWN, this.file.getId()));
} catch (EamDbException ex) { } catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS
} catch (CorrelationAttributeNormalizationException ex) { } catch (CorrelationAttributeNormalizationException ex) {
LOGGER.log(Level.INFO, String.format("Unable to create CorrelationAttributeInstance for value %s", md5), ex); // NON-NLS 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,10 +31,9 @@ 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,6 +182,7 @@ 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.
* *
@ -208,13 +208,12 @@ 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
*/ */
@ -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;
@ -347,6 +346,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.
* @param correlationDataSource The data source tied to the instance. * @param correlationDataSource The data source tied to the instance.
@ -360,6 +362,22 @@ public interface EamDb {
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
@ -385,10 +403,13 @@ public interface EamDb {
* "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".
* *
@ -700,8 +721,9 @@ 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;
@ -709,9 +731,10 @@ 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
* @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 {
@ -50,7 +50,8 @@ final class PostgresEamDb extends AbstractSqlEamDb {
* *
* @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) {
@ -62,8 +63,9 @@ 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()
} }
@ -191,29 +193,32 @@ final class PostgresEamDb extends AbstractSqlEamDb {
} }
/** /**
* 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

@ -36,7 +36,8 @@ 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 {
@ -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 (");
@ -454,11 +442,12 @@ public final class PostgresEamDbSettings {
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) {
@ -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
@ -391,9 +393,9 @@ final class SqliteEamDb extends AbstractSqlEamDb {
/** /**
* 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
@ -740,8 +749,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
@ -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

@ -36,7 +36,8 @@ 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 {
@ -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());
} }
/** /**
@ -151,6 +152,7 @@ public final class SqliteEamDbSettings {
/** /**
* Delete the database * Delete the database
*
* @return * @return
*/ */
public boolean deleteDatabase() { public boolean deleteDatabase() {
@ -333,26 +335,13 @@ 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 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 (");
@ -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) {
@ -427,6 +417,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();
if (null == conn) { if (null == conn) {
@ -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(
md5,
filesType, filesType,
md5,
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"),
List<ContentTag> tags = getContentTagsFromDatabase(); 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());
}
}
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

@ -208,13 +208,14 @@ 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;
} }
} }
@ -223,8 +224,8 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
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;
@ -367,6 +368,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
* 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) {

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,15 +264,15 @@ 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());
@ -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());
}
} catch (SQLException ex) { currentTableHeader = new ArrayList<>();
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 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) {
logger.log(Level.WARNING, String.format("Failed to load table %s " //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,6 +74,7 @@ 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() {
@ -182,6 +185,14 @@ public final class UserPreferences {
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);
} }
@ -245,6 +256,14 @@ public final class UserPreferences {
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,10 +630,14 @@ 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;

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);
@ -60,6 +66,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

@ -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;
@ -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,7 +546,6 @@ 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;
@ -793,138 +798,55 @@ class SevenZipExtractor {
} }
/** /**
* 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 abstract long getSize(); public void setNewOutputStream(String localAbsPath) throws IOException {
this.output.close();
OutputStream getOutput() { this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1);
return output; this.localAbsPath = localAbsPath;
this.bytesWritten = 0;
} }
String getLocalAbsPath() { public int getSize() {
return localAbsPath; return bytesWritten;
}
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;
@ -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

@ -649,6 +649,15 @@ public final class FilesSet implements Serializable {
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,10 +196,19 @@ 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

@ -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();
} }
@ -253,6 +255,7 @@ 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() {
@ -261,6 +264,7 @@ 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() {
@ -268,10 +272,9 @@ public final class ImageGalleryController {
} }
/** /**
* 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
*/ */
@ -282,6 +285,7 @@ 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() {
@ -290,6 +294,7 @@ 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() {
@ -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;
@ -544,21 +616,11 @@ public final class ImageGalleryController {
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;
@ -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,7 +203,7 @@ 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();
@ -282,7 +277,7 @@ public class ImageGalleryModule {
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;
@ -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()) {
SwingUtilities.invokeLater(() -> {
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) { if (eventType == IngestJobEvent.DATA_SOURCE_ANALYSIS_STARTED) {
case JOptionPane.YES_OPTION:
con.rebuildDB(); if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.LOCAL) {
break; if (controller.isListeningEnabled()) {
case JOptionPane.NO_OPTION: DataSourceAnalysisStartedEvent dataSourceAnalysisStartedEvent = (DataSourceAnalysisStartedEvent) evt;
case JOptionPane.CANCEL_OPTION: Content dataSource = dataSourceAnalysisStartedEvent.getDataSource();
default:
break; //do nothing controller.getDatabase().insertOrUpdateDataSource(dataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS);
} }
}); }
} 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;
/**
*
* @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;
public DataSourceCell(Map<DataSource, Boolean> dataSourcesViewable) {
this.dataSourcesTooManyFiles = dataSourcesViewable;
} }
@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);
} }
} }
} }

View File

@ -240,8 +240,8 @@ public class Toolbar extends ToolBar {
} }
private void initDataSourceComboBox() { private void initDataSourceComboBox() {
dataSourceComboBox.setCellFactory(param -> new DataSourceCell(dataSourcesViewable)); dataSourceComboBox.setCellFactory(param -> new DataSourceCell(dataSourcesViewable, controller.getAllDataSourcesDrawableDBStatus()));
dataSourceComboBox.setButtonCell(new DataSourceCell(dataSourcesViewable)); dataSourceComboBox.setButtonCell(new DataSourceCell(dataSourcesViewable, controller.getAllDataSourcesDrawableDBStatus()));
dataSourceComboBox.setConverter(new StringConverter<Optional<DataSource>>() { dataSourceComboBox.setConverter(new StringConverter<Optional<DataSource>>() {
@Override @Override
public String toString(Optional<DataSource> object) { public String toString(Optional<DataSource> object) {

View File

@ -21,21 +21,15 @@ package org.sleuthkit.autopsy.keywordsearch;
import com.google.common.io.CharSource; import com.google.common.io.CharSource;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
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.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.Objects;
import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
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.autopsy.coreutils.SQLiteTableReader;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Dedicated SqliteTextExtractor to solve the problems associated with Tika's * Dedicated SqliteTextExtractor to solve the problems associated with Tika's
@ -49,7 +43,6 @@ class SqliteTextExtractor extends ContentTextExtractor {
private static final String SQLITE_MIMETYPE = "application/x-sqlite3"; private static final String SQLITE_MIMETYPE = "application/x-sqlite3";
private static final Logger logger = Logger.getLogger(SqliteTextExtractor.class.getName()); private static final Logger logger = Logger.getLogger(SqliteTextExtractor.class.getName());
private static final CharSequence EMPTY_CHARACTER_SEQUENCE = "";
@Override @Override
boolean isContentTypeSpecific() { boolean isContentTypeSpecific() {
@ -80,7 +73,7 @@ class SqliteTextExtractor extends ContentTextExtractor {
} }
/** /**
* Returns an input stream that will read from a sqlite database. * Returns a stream that will read from a sqlite database.
* *
* @param source Content file * @param source Content file
* *
@ -91,267 +84,250 @@ class SqliteTextExtractor extends ContentTextExtractor {
*/ */
@Override @Override
public Reader getReader(Content source) throws TextExtractorException { public Reader getReader(Content source) throws TextExtractorException {
try { //Firewall for any content that is not an AbstractFile
//Firewall for any content that is not an AbstractFile if (!AbstractFile.class.isInstance(source)) {
if (!AbstractFile.class.isInstance(source)) { try {
return CharSource.wrap(EMPTY_CHARACTER_SEQUENCE).openStream(); return CharSource.wrap("").openStream();
} catch (IOException ex) {
throw new TextExtractorException("", ex);
} }
return new SQLiteTableReader((AbstractFile) source);
} catch (NoCurrentCaseException | IOException | TskCoreException
| ClassNotFoundException | SQLException ex) {
throw new TextExtractorException(
String.format("Encountered an issue while trying to initialize " //NON-NLS
+ "a sqlite table steamer for abstract file with id: [%s], name: " //NON-NLS
+ "[%s].", source.getId(), source.getName()), ex); //NON-NLS
} }
return new SQLiteStreamReader((AbstractFile) source);
} }
/** /**
* Lazily loads tables from the database during reading to conserve memory. * Produces a continuous stream of characters from a database file. To
* achieve this, all table names are queues up and a SQLiteTableReader is
* used to do the actual queries and table iteration.
*/ */
private class SQLiteTableReader extends Reader { public class SQLiteStreamReader extends Reader {
private final Iterator<String> tableIterator; private final SQLiteTableReader reader;
private final Connection connection; private final AbstractFile file;
private Reader currentTableReader;
private final AbstractFile source; private Iterator<String> tableNames;
private String currentTableName;
private char[] buf;
private ExcessBytes leftOvers;
private int totalColumns;
private int bufIndex;
/** /**
* Creates a reader that streams each table into memory and wraps a * Creates a new reader for the sqlite file. This table reader class
* reader around it. Designed to save memory for large databases. * will iterate through a table row by row and pass the values to
* different functions based on data type. Here we define what to do on
* the column names and we define what to do for all data types.
* *
* @param file Sqlite database file * @param file Sqlite file
*
* @throws NoCurrentCaseException Current case has closed
* @throws IOException Exception copying abstract file over
* to local temp directory
* @throws TskCoreException Exception using file manager to find
* meta files
* @throws ClassNotFoundException Could not find sqlite JDBC class
* @throws SQLException Could not establish jdbc connection
*/ */
public SQLiteTableReader(AbstractFile file) throws NoCurrentCaseException, public SQLiteStreamReader(AbstractFile file) {
IOException, TskCoreException, ClassNotFoundException, SQLException { this.file = file;
source = file; reader = new SQLiteTableReader.Builder(file)
.onColumnNames(getColumnNameStrategy())
String localDiskPath = SqliteUtil.writeAbstractFileToLocalDisk(file); .forAll(getForAllTableValuesStrategy()).build();
SqliteUtil.findAndCopySQLiteMetaFile(file);
Class.forName("org.sqlite.JDBC"); //NON-NLS
connection = DriverManager.getConnection("jdbc:sqlite:" + localDiskPath); //NON-NLS
tableIterator = getTables().iterator();
} }
/** /**
* Gets the table names from the SQLite database file. * On every item in the database we want to do the following series of
* steps: 1) Get it's string representation (ignore blobs with empty
* string). 2) Format it based on its positioning in the row. 3) Write
* it to buffer
* *
* @return Collection of table names from the database schema * rowIndex is purely for keeping track of where the object is in the
*/ * table, hence the bounds checking with the mod function.
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;
}
/**
* Reads from a database table and loads the contents into a table
* builder so that its properly formatted during indexing.
* *
* @param tableName Database table to be read * @return Our consumer class defined to do the steps above.
*/ */
private String getTableAsString(String tableName) { private Consumer<Object> getForAllTableValuesStrategy() {
TableBuilder table = new TableBuilder(); return new Consumer<Object>() {
table.addTableName(tableName); private int columnIndex = 0;
String quotedTableName = "\"" + tableName + "\"";
try (Statement statement = connection.createStatement(); @Override
ResultSet resultSet = statement.executeQuery( public void accept(Object value) {
"SELECT * FROM " + quotedTableName)) { //NON-NLS columnIndex++;
ResultSetMetaData metaData = resultSet.getMetaData(); //Ignore blobs
int columnCount = resultSet.getMetaData().getColumnCount(); String objectStr = (value instanceof byte[]) ? "" : Objects.toString(value, "");
Collection<String> row = new LinkedList<>();
//Add column names once from metadata if (columnIndex > 1 && columnIndex < totalColumns) {
for (int i = 1; i <= columnCount; i++) { objectStr += " ";
row.add(metaData.getColumnName(i));
}
table.addHeader(row);
while (resultSet.next()) {
row = new LinkedList<>();
for (int i = 1; i <= columnCount; i++) {
Object result = resultSet.getObject(i);
String type = metaData.getColumnTypeName(i);
if (isValuableResult(result, type)) {
row.add(resultSet.getObject(i).toString());
}
} }
table.addRow(row); if (columnIndex == 1) {
objectStr = "\t" + objectStr + " ";
}
if (columnIndex == totalColumns) {
objectStr += "\n";
}
fillBuffer(objectStr);
columnIndex = columnIndex % totalColumns;
} }
table.addCell("\n"); };
} catch (SQLException ex) {
logger.log(Level.WARNING, String.format(
"Error attempting to read file table: [%s]" //NON-NLS
+ " for file: [%s] (id=%d).", tableName, //NON-NLS
source.getName(), source.getId()), ex);
}
return table.toString();
} }
/** /**
* Determines if the result from the result set is worth adding to the * On every column name in the header do the following series of steps:
* row. Ignores nulls and blobs for the time being. * 1) Write the tableName before the header. 2) Format the column name
* based on row positioning 3) Reset the count if we are at the end,
* that way if we want to read multiple tables we can do so without
* having to build new consumers.
* *
* @param result Object result retrieved from resultSet * columnIndex is purely for keeping track of where the column name is
* @param type Type of objet retrieved from resultSet * in the table, hence the bounds checking with the mod function.
* *
* @return boolean where true means valuable, false implies it can be * @return Our consumer class defined to do the steps above.
* skipped.
*/ */
private boolean isValuableResult(Object result, String type) { private Consumer<String> getColumnNameStrategy() {
//Ignore nulls and blobs return new Consumer<String>() {
return result != null && type.compareToIgnoreCase("blob") != 0; private int columnIndex = 0;
@Override
public void accept(String columnName) {
if (columnIndex == 0) {
fillBuffer("\n" + currentTableName + "\n\n\t");
}
columnIndex++;
fillBuffer(columnName + ((columnIndex == totalColumns) ? "\n" : " "));
//Reset the columnCount to 0 for next table read
columnIndex = columnIndex % totalColumns;
}
};
} }
/** /**
* Loads a database file into the character buffer. The underlying * This functions writes the string representation of a database value
* implementation here only loads one table at a time to conserve * into the read buffer. If the buffer becomes full, we save the extra
* memory. * characters and hold on to them until the next call to read().
* *
* @param cbuf Buffer to copy database content characters into * @param val Formatted database value string
* @param off offset to begin loading in buffer */
* @param len length of the buffer private void fillBuffer(String val) {
for (int i = 0; i < val.length(); i++) {
if (bufIndex != buf.length) {
buf[bufIndex++] = val.charAt(i);
} else {
leftOvers = new ExcessBytes(val, i);
break;
}
}
}
/**
* Reads database values into the buffer. This function is responsible for
* getting the next table in the queue, initiating calls to the SQLiteTableReader,
* and filling in any excess bytes that are lingering from the previous call.
* *
* @return The number of characters read from the reader * @throws IOException
*
* @throws IOException If there is an error with the CharSource wrapping
*/ */
@Override @Override
public int read(char[] cbuf, int off, int len) throws IOException { public int read(char[] cbuf, int off, int len) throws IOException {
if (currentTableReader == null) { buf = cbuf;
String tableResults = getNextTable();
if (tableResults == null) { bufIndex = off;
//Lazily wait to get table names until first call to read.
if (Objects.isNull(tableNames)) {
try {
tableNames = reader.getTableNames().iterator();
} catch (SQLiteTableReaderException ex) {
//Can't get table names so can't read the file!
return -1; return -1;
} }
currentTableReader = CharSource.wrap(tableResults).openStream();
} }
int charactersRead = currentTableReader.read(cbuf, off, len); //If there are excess bytes from last read, then copy thoses in.
while (charactersRead == -1) { if (Objects.nonNull(leftOvers) && !leftOvers.isFinished()) {
String tableResults = getNextTable(); bufIndex += leftOvers.read(cbuf, off, len);
if (tableResults == null) { }
return -1;
//Keep grabbing table names from the queue and reading them until
//our buffer is full.
while (bufIndex != len) {
if (Objects.isNull(currentTableName) || reader.isFinished()) {
if (tableNames.hasNext()) {
currentTableName = tableNames.next();
try {
totalColumns = reader.getColumnCount(currentTableName);
reader.read(currentTableName, () -> bufIndex == len);
} catch (SQLiteTableReaderException ex) {
logger.log(Level.WARNING, String.format(
"Error attempting to read file table: [%s]" //NON-NLS
+ " for file: [%s] (id=%d).", currentTableName, //NON-NLS
file.getName(), file.getId()), ex.getMessage());
}
} else {
if (bufIndex == off) {
return -1;
}
return bufIndex;
}
} else {
try {
reader.read(currentTableName, () -> bufIndex == len);
} catch (SQLiteTableReaderException ex) {
logger.log(Level.WARNING, String.format(
"Error attempting to read file table: [%s]" //NON-NLS
+ " for file: [%s] (id=%d).", currentTableName, //NON-NLS
file.getName(), file.getId()), ex.getMessage());
}
} }
currentTableReader = CharSource.wrap(tableResults).openStream();
charactersRead = currentTableReader.read(cbuf, off, len);
} }
return charactersRead; return bufIndex;
} }
/**
* Grab the next table name from the collection of all table names, once
* we no longer have a table to process, return null which will be
* understood to mean the end of parsing.
*
* @return Current table contents or null meaning there are not more
* tables to process
*/
private String getNextTable() {
if (tableIterator.hasNext()) {
return getTableAsString(tableIterator.next());
} else {
return null;
}
}
/**
* Close the underlying connection to the database.
*
* @throws IOException Not applicable, we can just catch the
* SQLException
*/
@Override @Override
public void close() throws IOException { public void close() throws IOException {
try { try {
connection.close(); reader.close();
} catch (SQLException ex) { } catch (SQLiteTableReaderException ex) {
//Non-essential exception, user has no need for the connection logger.log(Level.WARNING, "Could not close SQliteTableReader.", ex.getMessage());
//object at this stage so closing details are not important
logger.log(Level.WARNING, "Could not close JDBC connection", ex);
} }
} }
}
/**
* Formats input so that it reads as a table in the console or in a text
* viewer
*/
private class TableBuilder {
private final Integer DEFAULT_CAPACITY = 32000;
private final StringBuilder table = new StringBuilder(DEFAULT_CAPACITY);
private static final String TAB = "\t";
private static final String NEW_LINE = "\n";
private static final String SPACE = " ";
/** /**
* Add the section to the top left corner of the table. This is where * Wrapper that holds the excess bytes that were left over from the previous
* the name of the table should go * call to read().
*
* @param tableName Table name
*/ */
public void addTableName(String tableName) { private class ExcessBytes {
table.append(tableName)
.append(NEW_LINE)
.append(NEW_LINE);
}
/** private final String entity;
* Adds a formatted header row to the underlying StringBuilder private Integer pointer;
*
* @param vals
*/
public void addHeader(Collection<String> vals) {
addRow(vals);
}
/** public ExcessBytes(String entity, Integer pointer) {
* Adds a formatted row to the underlying StringBuilder this.entity = entity;
* this.pointer = pointer;
* @param vals }
*/
public void addRow(Collection<String> vals) {
table.append(TAB);
vals.forEach((val) -> {
table.append(val);
table.append(SPACE);
});
table.append(NEW_LINE);
}
public void addCell(String cell) { public boolean isFinished() {
table.append(cell); return entity.length() == pointer;
} }
/** /**
* Returns a string version of the table, with all of the escape * Copies the excess bytes this instance is holding onto into the
* sequences necessary to print nicely in the console output. * buffer.
* *
* @return Formated table contents * @param buf buffer to write into
*/ * @param off index in buffer to start the write
@Override * @param len length of the write
public String toString() { *
return table.toString(); * @return number of characters read into the buffer
*/
public int read(char[] buf, int off, int len) {
for (int i = off; i < len; i++) {
if (isFinished()) {
return i - off;
}
buf[i] = entity.charAt(pointer++);
}
return len - off;
}
} }
} }
} }

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