Merge branch 'develop' into 1467-add-rank-to-tag-names-table

This commit is contained in:
Kelly Kelly 2020-05-20 14:01:20 -04:00
commit 1ed1e81d29
50 changed files with 3979 additions and 2157 deletions

View File

@ -90,6 +90,7 @@ public interface AutopsyService {
private final Case theCase; private final Case theCase;
private final ProgressIndicator progressIndicator; private final ProgressIndicator progressIndicator;
private volatile boolean cancelRequested; private volatile boolean cancelRequested;
private final boolean isNewCase;
/** /**
* Constructs the context for the creation/opening/upgrading of * Constructs the context for the creation/opening/upgrading of
@ -100,9 +101,23 @@ public interface AutopsyService {
* case-level resources * case-level resources
*/ */
public CaseContext(Case theCase, ProgressIndicator progressIndicator) { public CaseContext(Case theCase, ProgressIndicator progressIndicator) {
this(theCase, progressIndicator, false);
}
/**
* Constructs the context for the creation/opening/upgrading of
* case-level resources by a service.
*
* @param theCase The case.
* @param progressIndicator A progress indicator for the opening of the
* case-level resources.
* @param isNewCase True if theCase is a new case.
*/
public CaseContext(Case theCase, ProgressIndicator progressIndicator, boolean isNewCase) {
this.theCase = theCase; this.theCase = theCase;
this.progressIndicator = progressIndicator; this.progressIndicator = progressIndicator;
this.cancelRequested = false; this.cancelRequested = false;
this.isNewCase = isNewCase;
} }
/** /**
@ -145,6 +160,16 @@ public interface AutopsyService {
public boolean cancelRequested() { public boolean cancelRequested() {
return this.cancelRequested; return this.cancelRequested;
} }
/**
* Indicates whether or the case is a new case in the process of being
* created.
*
* @return True if it is a new case.
*/
public boolean isNewCase() {
return this.isNewCase;
}
} }
/** /**

View File

@ -132,7 +132,6 @@ class AddImageTask implements Runnable {
List<String> errorMessages = new ArrayList<>(); List<String> errorMessages = new ArrayList<>();
List<Content> newDataSources = new ArrayList<>(); List<Content> newDataSources = new ArrayList<>();
try { try {
currentCase.getSleuthkitCase().acquireSingleUserCaseWriteLock();
synchronized (tskAddImageProcessLock) { synchronized (tskAddImageProcessLock) {
if (!tskAddImageProcessStopped) { if (!tskAddImageProcessStopped) {
tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles, imageWriterPath); tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles, imageWriterPath);
@ -147,7 +146,6 @@ class AddImageTask implements Runnable {
commitOrRevertAddImageProcess(currentCase, errorMessages, newDataSources); commitOrRevertAddImageProcess(currentCase, errorMessages, newDataSources);
progressMonitor.setProgress(100); progressMonitor.setProgress(100);
} finally { } finally {
currentCase.getSleuthkitCase().releaseSingleUserCaseWriteLock();
DataSourceProcessorCallback.DataSourceProcessorResult result; DataSourceProcessorCallback.DataSourceProcessorResult result;
if (criticalErrorOccurred) { if (criticalErrorOccurred) {
result = DataSourceProcessorResult.CRITICAL_ERRORS; result = DataSourceProcessorResult.CRITICAL_ERRORS;

View File

@ -1958,7 +1958,7 @@ public class Case {
checkForCancellation(); checkForCancellation();
openCaseLevelServices(progressIndicator); openCaseLevelServices(progressIndicator);
checkForCancellation(); checkForCancellation();
openAppServiceCaseResources(progressIndicator); openAppServiceCaseResources(progressIndicator, true);
checkForCancellation(); checkForCancellation();
openCommunicationChannels(progressIndicator); openCommunicationChannels(progressIndicator);
return null; return null;
@ -2007,7 +2007,7 @@ public class Case {
checkForCancellation(); checkForCancellation();
openCaseLevelServices(progressIndicator); openCaseLevelServices(progressIndicator);
checkForCancellation(); checkForCancellation();
openAppServiceCaseResources(progressIndicator); openAppServiceCaseResources(progressIndicator, false);
checkForCancellation(); checkForCancellation();
openCommunicationChannels(progressIndicator); openCommunicationChannels(progressIndicator);
checkForCancellation(); checkForCancellation();
@ -2060,6 +2060,7 @@ public class Case {
private final SleuthkitCase tskCase; private final SleuthkitCase tskCase;
private final String caseName; private final String caseName;
private final long MAX_IMAGE_THRESHOLD = 100;
private final ProgressIndicator progressIndicator; private final ProgressIndicator progressIndicator;
/** /**
@ -2117,28 +2118,18 @@ public class Case {
* event that the operation is * event that the operation is
* cancelled prior to completion. * cancelled prior to completion.
*/ */
private void openFileSystems(List<Image> images) throws InterruptedException { private void openFileSystems(List<Image> images) throws TskCoreException, InterruptedException {
byte[] tempBuff = new byte[512]; byte[] tempBuff = new byte[512];
for (Image image : images) { for (Image image : images) {
String imageStr = image.getName(); String imageStr = image.getName();
progressIndicator.progress(Bundle.Case_openFileSystems_openingImage(imageStr)); progressIndicator.progress(Bundle.Case_openFileSystems_openingImage(imageStr));
Collection<FileSystem> fileSystems = this.tskCase.getFileSystems(image); Collection<FileSystem> fileSystems = this.tskCase.getImageFileSystems(image);
checkIfCancelled(); checkIfCancelled();
for (FileSystem fileSystem : fileSystems) { for (FileSystem fileSystem : fileSystems) {
try { fileSystem.read(tempBuff, 0, 512);
fileSystem.read(tempBuff, 0, 512);
} catch (TskCoreException ex) {
String fileSysStr = fileSystem.getName();
logger.log(
Level.WARNING,
String.format("Could not open filesystem: %s in image: %s for case: %s.", fileSysStr, imageStr, caseName),
ex);
}
checkIfCancelled(); checkIfCancelled();
} }
@ -2153,6 +2144,14 @@ public class Case {
if (images == null) { if (images == null) {
return; return;
} }
if (images.size() > MAX_IMAGE_THRESHOLD) {
// If we have a large number of images, don't try to preload anything
logger.log(
Level.INFO,
String.format("Skipping background load of file systems due to large number of images in case (%d)", images.size()));
return;
}
checkIfCancelled(); checkIfCancelled();
openFileSystems(images); openFileSystems(images);
@ -2160,6 +2159,9 @@ public class Case {
logger.log( logger.log(
Level.INFO, Level.INFO,
String.format("Background operation opening all file systems in %s has been cancelled.", caseName)); String.format("Background operation opening all file systems in %s has been cancelled.", caseName));
} catch (Exception ex) {
// Exception firewall
logger.log(Level.WARNING, "Error while opening file systems in background", ex);
} }
} }
@ -2516,7 +2518,7 @@ public class Case {
"# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...", "# {0} - service name", "Case.serviceOpenCaseResourcesProgressIndicator.cancellingMessage=Cancelling opening case resources by {0}...",
"# {0} - service name", "Case.servicesException.notificationTitle={0} Error" "# {0} - service name", "Case.servicesException.notificationTitle={0} Error"
}) })
private void openAppServiceCaseResources(ProgressIndicator progressIndicator) throws CaseActionException { private void openAppServiceCaseResources(ProgressIndicator progressIndicator, boolean isNewCase) throws CaseActionException {
/* /*
* Each service gets its own independently cancellable/interruptible * Each service gets its own independently cancellable/interruptible
* task, running in a named thread managed by an executor service, with * task, running in a named thread managed by an executor service, with
@ -2548,7 +2550,7 @@ public class Case {
appServiceProgressIndicator = new LoggingProgressIndicator(); appServiceProgressIndicator = new LoggingProgressIndicator();
} }
appServiceProgressIndicator.start(Bundle.Case_progressMessage_preparing()); appServiceProgressIndicator.start(Bundle.Case_progressMessage_preparing());
AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, appServiceProgressIndicator); AutopsyService.CaseContext context = new AutopsyService.CaseContext(this, appServiceProgressIndicator, isNewCase);
String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS String threadNameSuffix = service.getServiceName().replaceAll("[ ]", "-"); //NON-NLS
threadNameSuffix = threadNameSuffix.toLowerCase(); threadNameSuffix = threadNameSuffix.toLowerCase();
TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix)); TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));

View File

@ -55,11 +55,11 @@ final class TagNameDefinition implements Comparable<TagNameDefinition> {
private static final String TAG_SETTING_VERSION_KEY = "CustomTagNameVersion"; private static final String TAG_SETTING_VERSION_KEY = "CustomTagNameVersion";
private static final int TAG_SETTINGS_VERSION = 1; private static final int TAG_SETTINGS_VERSION = 1;
private static final String CATEGORY_ONE_NAME = "CAT-1: Child Exploitation (Illegal)"; private static final String CATEGORY_ONE_NAME = "Child Exploitation (Illegal)";
private static final String CATEGORY_TWO_NAME = "CAT-2: Child Exploitation (Non-Illegal/Age Difficult)"; private static final String CATEGORY_TWO_NAME = "Child Exploitation (Non-Illegal/Age Difficult)";
private static final String CATEGORY_THREE_NAME = "CAT-3: CGI/Animation (Child Exploitive)"; private static final String CATEGORY_THREE_NAME = "CGI/Animation (Child Exploitive)";
private static final String CATEGORY_FOUR_NAME = "CAT-4: Exemplar/Comparison (Internal Use Only)"; private static final String CATEGORY_FOUR_NAME = "Exemplar/Comparison (Internal Use Only)";
private static final String CATEGORY_FIVE_NAME = "CAT-5: Non-pertinent"; private static final String CATEGORY_FIVE_NAME = "Non-pertinent";
private final String displayName; private final String displayName;
private final String description; private final String description;
@ -119,6 +119,33 @@ final class TagNameDefinition implements Comparable<TagNameDefinition> {
return strList; return strList;
} }
/**
* Returns the bookmark tag display string.
*
* @return
*/
static String getBookmarkDisplayString() {
return Bundle.TagNameDefinition_predefTagNames_bookmark_text();
}
/**
* Returns the Follow Up tag display string.
*
* @return
*/
static String getFollowUpDisplayString() {
return Bundle.TagNameDefinition_predefTagNames_followUp_text();
}
/**
* Returns the Notable tag display string.
*
* @return
*/
static String getNotableDisplayString() {
return Bundle.TagNameDefinition_predefTagNames_notableItem_text();
}
/** /**
* Gets the display name for the tag name. * Gets the display name for the tag name.
* *

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2020 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");
@ -55,7 +55,7 @@ public class TagsManager implements Closeable {
private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName()); private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName());
private final SleuthkitCase caseDb; private final SleuthkitCase caseDb;
static String DEFAULT_TAG_SET_NAME = "Project VIC (United States)"; private static String DEFAULT_TAG_SET_NAME = "Project VIC";
static { static {
@ -146,6 +146,12 @@ public class TagsManager implements Closeable {
return tagDisplayNames; return tagDisplayNames;
} }
/**
* Gets the set of display names of notable (TskData.FileKnown.BAD) tag types.
* If a case is not open the list will only include only the user defined
* custom tags. Otherwise the list will include all notable tags.
* @return
*/
public static List<String> getNotableTagDisplayNames() { public static List<String> getNotableTagDisplayNames() {
List<String> tagDisplayNames = new ArrayList<>(); List<String> tagDisplayNames = new ArrayList<>();
for (TagNameDefinition tagDef : TagNameDefinition.getTagNameDefinitions()) { for (TagNameDefinition tagDef : TagNameDefinition.getTagNameDefinitions()) {
@ -153,6 +159,22 @@ public class TagsManager implements Closeable {
tagDisplayNames.add(tagDef.getDisplayName()); tagDisplayNames.add(tagDef.getDisplayName());
} }
} }
try {
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
for (TagName tagName : tagsManager.getAllTagNames()) {
if(tagName.getKnownStatus() == TskData.FileKnown.BAD &&
!tagDisplayNames.contains(tagName.getDisplayName())) {
tagDisplayNames.add(tagName.getDisplayName());
}
}
} catch (NoCurrentCaseException ignored) {
/*
* No current case, nothing more to add to the set.
*/
} catch(TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Failed to get list of TagNames from TagsManager.", ex);
}
return tagDisplayNames; return tagDisplayNames;
} }
@ -162,7 +184,62 @@ public class TagsManager implements Closeable {
* @return list of predefined tag names * @return list of predefined tag names
*/ */
public static List<String> getStandardTagNames() { public static List<String> getStandardTagNames() {
return TagNameDefinition.getStandardTagNames(); List<String> tagList = new ArrayList<>();
for (TagNameDefinition tagNameDef : TagNameDefinition.getStandardTagNameDefinitions()) {
tagList.add(tagNameDef.getDisplayName());
}
try {
List<TagSet> tagSetList = Case.getCurrentCaseThrows().getSleuthkitCase().getTaggingManager().getTagSets();
for (TagSet tagSet : tagSetList) {
if (tagSet.getName().equals(DEFAULT_TAG_SET_NAME)) {
for (TagName tagName : tagSet.getTagNames()) {
tagList.add(tagName.getDisplayName());
}
}
}
} catch (NoCurrentCaseException | TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Failed to get Project VIC tags from the database.", ex);
}
return tagList;
}
/**
* Returns the name of the Category TagSet.
*
* @return Name of category TagSet.
*/
public static String getCategoryTagSetName() {
return DEFAULT_TAG_SET_NAME;
}
/**
* Returns the bookmark tag display string.
*
* @return
*/
public static String getBookmarkDisplayString() {
return TagNameDefinition.getBookmarkDisplayString();
}
/**
* Returns the Follow Up tag display string.
*
* @return
*/
public static String getFollowUpDisplayString() {
return TagNameDefinition.getFollowUpDisplayString();
}
/**
* Returns the Notable tag display string.
*
* @return
*/
public static String getNotableDisplayString() {
return TagNameDefinition.getNotableDisplayString();
} }
/** /**

View File

@ -26,6 +26,7 @@ import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.datamodel.HashHitInfo;
/** /**
* Main interface for interacting with the database * Main interface for interacting with the database
@ -553,6 +554,22 @@ public interface CentralRepository {
*/ */
public boolean isFileHashInReferenceSet(String hash, int referenceSetID) throws CentralRepoException, CorrelationAttributeNormalizationException; public boolean isFileHashInReferenceSet(String hash, int referenceSetID) throws CentralRepoException, CorrelationAttributeNormalizationException;
/**
* Retrieves the given file HashHitInfo if the given file hash is in this
* reference set. Only searches the reference_files table.
*
* @param hash The hash to find in a search.
* @param referenceSetID The referenceSetID within which the file should exist.
*
* @return The HashHitInfo if found or null if not found.
*
* @throws CentralRepoException
* @throws CorrelationAttributeNormalizationException
*/
HashHitInfo lookupHash(String hash, int referenceSetID) throws CentralRepoException, CorrelationAttributeNormalizationException;
/** /**
* Check if the given value is in a specific reference set * Check if the given value is in a specific reference set
* *

View File

@ -41,6 +41,7 @@ import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
@ -51,6 +52,7 @@ import org.sleuthkit.autopsy.healthmonitor.HealthMonitor;
import org.sleuthkit.autopsy.healthmonitor.TimingMetric; import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber; import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
import org.sleuthkit.datamodel.HashHitInfo;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -80,7 +82,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
private static final Cache<Pair<CentralRepoAccountType, String>, CentralRepoAccount> accountsCache = CacheBuilder.newBuilder() private static final Cache<Pair<CentralRepoAccountType, String>, CentralRepoAccount> accountsCache = CacheBuilder.newBuilder()
.expireAfterWrite(ACCOUNTS_CACHE_TIMEOUT, TimeUnit.MINUTES). .expireAfterWrite(ACCOUNTS_CACHE_TIMEOUT, TimeUnit.MINUTES).
build(); build();
private boolean isCRTypeCacheInitialized; private boolean isCRTypeCacheInitialized;
private static final Cache<Integer, CorrelationAttributeInstance.Type> typeCache = CacheBuilder.newBuilder().build(); private static final Cache<Integer, CorrelationAttributeInstance.Type> typeCache = CacheBuilder.newBuilder().build();
private static final Cache<String, CorrelationCase> caseCacheByUUID = CacheBuilder.newBuilder() private static final Cache<String, CorrelationCase> caseCacheByUUID = CacheBuilder.newBuilder()
@ -103,8 +105,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
static final int DEFAULT_BULK_THRESHHOLD = 1000; static final int DEFAULT_BULK_THRESHHOLD = 1000;
private static final int QUERY_STR_MAX_LEN = 1000; private static final int QUERY_STR_MAX_LEN = 1000;
/** /**
* Connect to the DB and initialize it. * Connect to the DB and initialize it.
* *
@ -135,6 +136,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
* Get an ephemeral connection. * Get an ephemeral connection.
*/ */
protected abstract Connection getEphemeralConnection(); protected abstract Connection getEphemeralConnection();
/** /**
* Add a new name/value pair in the db_info table. * Add a new name/value pair in the db_info table.
* *
@ -221,7 +223,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
* Reset the contents of the caches associated with EamDb results. * Reset the contents of the caches associated with EamDb results.
*/ */
public final void clearCaches() { public final void clearCaches() {
synchronized(typeCache) { synchronized (typeCache) {
typeCache.invalidateAll(); typeCache.invalidateAll();
isCRTypeCacheInitialized = false; isCRTypeCacheInitialized = false;
} }
@ -1015,27 +1017,26 @@ abstract class RdbmsCentralRepo implements CentralRepository {
// @@@ We should cache the case and data source IDs in memory // @@@ We should cache the case and data source IDs in memory
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()); String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType());
boolean artifactHasAnAccount = CentralRepoDbUtil.correlationAttribHasAnAccount(eamArtifact.getCorrelationType()); boolean artifactHasAnAccount = CentralRepoDbUtil.correlationAttribHasAnAccount(eamArtifact.getCorrelationType());
String sql; String sql;
// _instance table for accounts have an additional account_id column // _instance table for accounts have an additional account_id column
if (artifactHasAnAccount) { if (artifactHasAnAccount) {
sql = "INSERT INTO " sql = "INSERT INTO "
+ tableName + tableName
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) " + "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
+ getConflictClause(); + getConflictClause();
} } else {
else { sql = "INSERT INTO "
sql = "INSERT INTO " + tableName
+ tableName + "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) " + "VALUES (?, ?, ?, ?, ?, ?, ?) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?) " + getConflictClause();
+ getConflictClause();
} }
try (Connection conn = connect(); try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);) { PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
if (!eamArtifact.getCorrelationValue().isEmpty()) { if (!eamArtifact.getCorrelationValue().isEmpty()) {
preparedStatement.setInt(1, eamArtifact.getCorrelationCase().getID()); preparedStatement.setInt(1, eamArtifact.getCorrelationCase().getID());
preparedStatement.setInt(2, eamArtifact.getCorrelationDataSource().getID()); preparedStatement.setInt(2, eamArtifact.getCorrelationDataSource().getID());
@ -1049,7 +1050,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
preparedStatement.setString(6, eamArtifact.getComment()); preparedStatement.setString(6, eamArtifact.getComment());
} }
preparedStatement.setLong(7, eamArtifact.getFileObjectId()); preparedStatement.setLong(7, eamArtifact.getFileObjectId());
// set in the accountId only for artifacts that represent accounts // set in the accountId only for artifacts that represent accounts
if (artifactHasAnAccount) { if (artifactHasAnAccount) {
if (eamArtifact.getAccountId() >= 0) { if (eamArtifact.getAccountId() >= 0) {
@ -1064,21 +1065,21 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} catch (SQLException ex) { } catch (SQLException ex) {
throw new CentralRepoException("Error inserting new artifact into artifacts table.", ex); // NON-NLS throw new CentralRepoException("Error inserting new artifact into artifacts table.", ex); // NON-NLS
} }
} }
/** /**
* Gets the Central Repo account for the given account type and account ID. * Gets the Central Repo account for the given account type and account ID.
* Create a new account first, if one doesn't exist * Create a new account first, if one doesn't exist
* *
* @param accountType account type * @param accountType account type
* @param accountUniqueID unique account identifier * @param accountUniqueID unique account identifier
* *
* @return A matching account, either existing or newly created. * @return A matching account, either existing or newly created.
* *
* @throws TskCoreException exception thrown if a critical error occurs * @throws TskCoreException exception thrown if a critical error occurs
* within TSK core * within TSK core
*/ */
@Override @Override
public CentralRepoAccount getOrCreateAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException { public CentralRepoAccount getOrCreateAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException {
// Get the account fom the accounts table // Get the account fom the accounts table
@ -1104,56 +1105,53 @@ abstract class RdbmsCentralRepo implements CentralRepository {
return account; return account;
} }
@Override @Override
public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException { public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException {
try { try {
return accountTypesCache.get(accountTypeName, () -> getCRAccountTypeFromDb(accountTypeName)); return accountTypesCache.get(accountTypeName, () -> getCRAccountTypeFromDb(accountTypeName));
} catch (CacheLoader.InvalidCacheLoadException | ExecutionException ex) { } catch (CacheLoader.InvalidCacheLoadException | ExecutionException ex) {
throw new CentralRepoException("Error looking up CR account type in cache.", ex); throw new CentralRepoException("Error looking up CR account type in cache.", ex);
} }
} }
@Override @Override
public Collection<CentralRepoAccountType> getAllAccountTypes() throws CentralRepoException { public Collection<CentralRepoAccountType> getAllAccountTypes() throws CentralRepoException {
Collection<CentralRepoAccountType> accountTypes = new ArrayList<>();
String sql = "SELECT * FROM account_types";
try ( Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
Collection<CentralRepoAccountType> accountTypes = new ArrayList<>();
String sql = "SELECT * FROM account_types";
try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
try (ResultSet resultSet = preparedStatement.executeQuery();) { try (ResultSet resultSet = preparedStatement.executeQuery();) {
while (resultSet.next()) { while (resultSet.next()) {
Account.Type acctType = new Account.Type(resultSet.getString("type_name"), resultSet.getString("display_name")); Account.Type acctType = new Account.Type(resultSet.getString("type_name"), resultSet.getString("display_name"));
CentralRepoAccountType crAccountType = new CentralRepoAccountType(resultSet.getInt("id"), acctType, resultSet.getInt("correlation_type_id")); CentralRepoAccountType crAccountType = new CentralRepoAccountType(resultSet.getInt("id"), acctType, resultSet.getInt("correlation_type_id"));
accountTypes.add(crAccountType); accountTypes.add(crAccountType);
} }
} }
} catch (SQLException ex) { } catch (SQLException ex) {
throw new CentralRepoException("Error getting account types from central repository.", ex); // NON-NLS throw new CentralRepoException("Error getting account types from central repository.", ex); // NON-NLS
} }
return accountTypes; return accountTypes;
} }
/** /**
* Gets the CR account type for the specified type name. * Gets the CR account type for the specified type name.
* *
* @param accountTypeName account type name to look for * @param accountTypeName account type name to look for
*
* @return CR account type * @return CR account type
* *
* @throws CentralRepoException * @throws CentralRepoException
*/ */
private CentralRepoAccountType getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException { private CentralRepoAccountType getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
String sql = "SELECT * FROM account_types WHERE type_name = ?"; String sql = "SELECT * FROM account_types WHERE type_name = ?";
try ( Connection conn = connect(); try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);) { PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
preparedStatement.setString(1, accountTypeName); preparedStatement.setString(1, accountTypeName);
try (ResultSet resultSet = preparedStatement.executeQuery();) { try (ResultSet resultSet = preparedStatement.executeQuery();) {
@ -1168,24 +1166,27 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} }
} catch (SQLException ex) { } catch (SQLException ex) {
throw new CentralRepoException("Error getting correlation type by id.", ex); // NON-NLS throw new CentralRepoException("Error getting correlation type by id.", ex); // NON-NLS
} }
} }
/** /**
* Get the CR account with the given account type and the unique account identifier. * Get the CR account with the given account type and the unique account
* Looks in the cache first. * identifier. Looks in the cache first. If not found in cache, reads from
* If not found in cache, reads from the database and saves in cache. * the database and saves in cache.
* *
* Returns null if the account is not found in the cache and not in the database. * Returns null if the account is not found in the cache and not in the
* * database.
* @param crAccountType account type to look for *
* @param crAccountType account type to look for
* @param accountUniqueID unique account id * @param accountUniqueID unique account id
* @return CentralRepoAccount for the give type/id. May return null if not found. *
* * @return CentralRepoAccount for the give type/id. May return null if not
* @throws CentralRepoException * found.
*
* @throws CentralRepoException
*/ */
private CentralRepoAccount getAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException { private CentralRepoAccount getAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException {
CentralRepoAccount crAccount = accountsCache.getIfPresent(Pair.of(crAccountType, accountUniqueID)); CentralRepoAccount crAccount = accountsCache.getIfPresent(Pair.of(crAccountType, accountUniqueID));
if (crAccount == null) { if (crAccount == null) {
crAccount = getCRAccountFromDb(crAccountType, accountUniqueID); crAccount = getCRAccountFromDb(crAccountType, accountUniqueID);
@ -1193,30 +1194,29 @@ abstract class RdbmsCentralRepo implements CentralRepository {
accountsCache.put(Pair.of(crAccountType, accountUniqueID), crAccount); accountsCache.put(Pair.of(crAccountType, accountUniqueID), crAccount);
} }
} }
return crAccount; return crAccount;
} }
/** /**
* Get the Account with the given account type and account identifier, * Get the Account with the given account type and account identifier, from
* from the database. * the database.
* *
* @param accountType account type * @param accountType account type
* @param accountUniqueID unique account identifier * @param accountUniqueID unique account identifier
* *
* @return Account, returns NULL is no matching account found * @return Account, returns NULL is no matching account found
* *
* @throws TskCoreException exception thrown if a critical error occurs * @throws TskCoreException exception thrown if a critical error occurs
* within TSK core * within TSK core
*/ */
private CentralRepoAccount getCRAccountFromDb(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException { private CentralRepoAccount getCRAccountFromDb(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException {
CentralRepoAccount account = null; CentralRepoAccount account = null;
String sql = "SELECT * FROM accounts WHERE account_type_id = ? AND account_unique_identifier = ?"; String sql = "SELECT * FROM accounts WHERE account_type_id = ? AND account_unique_identifier = ?";
try ( Connection connection = connect(); try (Connection connection = connect();
PreparedStatement preparedStatement = connection.prepareStatement(sql);) { PreparedStatement preparedStatement = connection.prepareStatement(sql);) {
preparedStatement.setInt(1, crAccountType.getAccountTypeId()); preparedStatement.setInt(1, crAccountType.getAccountTypeId());
preparedStatement.setString(2, accountUniqueID); preparedStatement.setString(2, accountUniqueID);
@ -1232,8 +1232,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
return account; return account;
} }
private void checkAddArtifactInstanceNulls(CorrelationAttributeInstance eamArtifact) throws CentralRepoException { private void checkAddArtifactInstanceNulls(CorrelationAttributeInstance eamArtifact) throws CentralRepoException {
if (eamArtifact == null) { if (eamArtifact == null) {
throw new CentralRepoException("CorrelationAttribute is null"); throw new CentralRepoException("CorrelationAttribute is null");
@ -1574,7 +1573,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
synchronized (bulkArtifacts) { synchronized (bulkArtifacts) {
if (bulkArtifacts.get(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType())) == null) { if (bulkArtifacts.get(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType())) == null) {
bulkArtifacts.put(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()), new ArrayList<>()); bulkArtifacts.put(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()), new ArrayList<>());
} }
bulkArtifacts.get(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType())).add(eamArtifact); bulkArtifacts.get(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType())).add(eamArtifact);
bulkArtifactsCount++; bulkArtifactsCount++;
@ -2001,8 +2000,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
String sqlUpdate String sqlUpdate
= "UPDATE " = "UPDATE "
+ tableName + tableName
+ " SET known_status=?, comment=? " + " SET known_status=? WHERE id=?";
+ "WHERE id=?";
try { try {
preparedQuery = conn.prepareStatement(sqlQuery); preparedQuery = conn.prepareStatement(sqlQuery);
@ -2016,15 +2014,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
preparedUpdate = conn.prepareStatement(sqlUpdate); preparedUpdate = conn.prepareStatement(sqlUpdate);
preparedUpdate.setByte(1, knownStatus.getFileKnownValue()); preparedUpdate.setByte(1, knownStatus.getFileKnownValue());
// NOTE: if the user tags the same instance as BAD multiple times, preparedUpdate.setInt(2, instance_id);
// the comment from the most recent tagging is the one that will
// prevail in the DB.
if ("".equals(eamArtifact.getComment())) {
preparedUpdate.setNull(2, Types.INTEGER);
} else {
preparedUpdate.setString(2, eamArtifact.getComment());
}
preparedUpdate.setInt(3, instance_id);
preparedUpdate.executeUpdate(); preparedUpdate.executeUpdate();
} else { } else {
@ -2307,6 +2297,42 @@ abstract class RdbmsCentralRepo implements CentralRepository {
return isValueInReferenceSet(hash, referenceSetID, CorrelationAttributeInstance.FILES_TYPE_ID); return isValueInReferenceSet(hash, referenceSetID, CorrelationAttributeInstance.FILES_TYPE_ID);
} }
@Override
public HashHitInfo lookupHash(String hash, int referenceSetID) throws CentralRepoException, CorrelationAttributeNormalizationException {
int correlationTypeID = CorrelationAttributeInstance.FILES_TYPE_ID;
String normalizeValued = CorrelationAttributeNormalizer.normalize(this.getCorrelationTypeById(correlationTypeID), hash);
Connection conn = connect();
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String sql = "SELECT value,comment FROM %s WHERE value=? AND reference_set_id=?";
String fileTableName = CentralRepoDbUtil.correlationTypeToReferenceTableName(getCorrelationTypeById(correlationTypeID));
try {
preparedStatement = conn.prepareStatement(String.format(sql, fileTableName));
preparedStatement.setString(1, normalizeValued);
preparedStatement.setInt(2, referenceSetID);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
String comment = resultSet.getString("comment");
String hashFound = resultSet.getString("value");
HashHitInfo found = new HashHitInfo(hashFound, "", "");
found.addComment(comment);
return found;
} else {
return null;
}
} catch (SQLException ex) {
throw new CentralRepoException("Error determining if value (" + normalizeValued + ") is in reference set " + referenceSetID, ex); // NON-NLS
} finally {
CentralRepoDbUtil.closeStatement(preparedStatement);
CentralRepoDbUtil.closeResultSet(resultSet);
CentralRepoDbUtil.closeConnection(conn);
}
}
/** /**
* Check if the given value is in a specific reference set * Check if the given value is in a specific reference set
* *
@ -2476,7 +2502,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
CentralRepoDbUtil.closeConnection(conn); CentralRepoDbUtil.closeConnection(conn);
} }
} }
/** /**
* Process a SELECT query * Process a SELECT query
* *
@ -2514,7 +2540,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
CentralRepoDbUtil.closeResultSet(resultSet); CentralRepoDbUtil.closeResultSet(resultSet);
CentralRepoDbUtil.closeConnection(conn); CentralRepoDbUtil.closeConnection(conn);
} }
} }
@Override @Override
public void executeInsertSQL(String insertClause) throws CentralRepoException { public void executeInsertSQL(String insertClause) throws CentralRepoException {
@ -2557,7 +2583,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
throw new CentralRepoException(String.format("Error running SQL %s, exception = %s", selectSQL, ex.getMessage()), ex); throw new CentralRepoException(String.format("Error running SQL %s, exception = %s", selectSQL, ex.getMessage()), ex);
} }
} }
@Override @Override
public CentralRepoOrganization newOrganization(CentralRepoOrganization eamOrg) throws CentralRepoException { public CentralRepoOrganization newOrganization(CentralRepoOrganization eamOrg) throws CentralRepoException {
if (eamOrg == null) { if (eamOrg == null) {
@ -2697,15 +2723,16 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} }
/** /**
* Queries the examiner table for the given user name. * Queries the examiner table for the given user name. Adds a row if the
* Adds a row if the user is not found in the examiner table. * user is not found in the examiner table.
* *
* @param examinerLoginName user name to look for. * @param examinerLoginName user name to look for.
*
* @return CentralRepoExaminer for the given user name. * @return CentralRepoExaminer for the given user name.
*
* @throws CentralRepoException If there is an error in looking up or * @throws CentralRepoException If there is an error in looking up or
* inserting the user in the examiners table. * inserting the user in the examiners table.
*/ */
@Override @Override
public CentralRepoExaminer getOrInsertExaminer(String examinerLoginName) throws CentralRepoException { public CentralRepoExaminer getOrInsertExaminer(String examinerLoginName) throws CentralRepoException {
@ -2730,7 +2757,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
default: default:
throw new CentralRepoException(String.format("Cannot add examiner to currently selected CR database platform %s", CentralRepoDbManager.getSavedDbChoice().getDbPlatform())); //NON-NLS throw new CentralRepoException(String.format("Cannot add examiner to currently selected CR database platform %s", CentralRepoDbManager.getSavedDbChoice().getDbPlatform())); //NON-NLS
} }
statement.execute(insertSQL); statement.execute(insertSQL);
// Query the table again to get the row for the user // Query the table again to get the row for the user
try (ResultSet resultSet2 = statement.executeQuery(querySQL)) { try (ResultSet resultSet2 = statement.executeQuery(querySQL)) {
@ -2750,7 +2777,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
throw new CentralRepoException("Error getting examiner for name = " + examinerLoginName, ex); throw new CentralRepoException("Error getting examiner for name = " + examinerLoginName, ex);
} }
} }
/** /**
* Update an existing organization. * Update an existing organization.
* *
@ -3145,7 +3172,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
typeId = newCorrelationTypeKnownId(newType); typeId = newCorrelationTypeKnownId(newType);
} }
synchronized(typeCache) { synchronized (typeCache) {
typeCache.put(newType.getId(), newType); typeCache.put(newType.getId(), newType);
} }
return typeId; return typeId;
@ -3362,7 +3389,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
preparedStatement.setInt(4, aType.isEnabled() ? 1 : 0); preparedStatement.setInt(4, aType.isEnabled() ? 1 : 0);
preparedStatement.setInt(5, aType.getId()); preparedStatement.setInt(5, aType.getId());
preparedStatement.executeUpdate(); preparedStatement.executeUpdate();
synchronized(typeCache) { synchronized (typeCache) {
typeCache.put(aType.getId(), aType); typeCache.put(aType.getId(), aType);
} }
} catch (SQLException ex) { } catch (SQLException ex) {
@ -3386,7 +3413,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
@Override @Override
public CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws CentralRepoException { public CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws CentralRepoException {
try { try {
synchronized(typeCache) { synchronized (typeCache) {
return typeCache.get(typeId, () -> getCorrelationTypeByIdFromCr(typeId)); return typeCache.get(typeId, () -> getCorrelationTypeByIdFromCr(typeId));
} }
} catch (CacheLoader.InvalidCacheLoadException ignored) { } catch (CacheLoader.InvalidCacheLoadException ignored) {
@ -3397,7 +3424,6 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} }
} }
/** /**
* Get the EamArtifact.Type that has the given Type.Id from the central repo * Get the EamArtifact.Type that has the given Type.Id from the central repo
* *
@ -3436,24 +3462,25 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} }
/** /**
* Reads the correlation types from the database and loads them up in the cache. * Reads the correlation types from the database and loads them up in the
* * cache.
*
* @throws CentralRepoException If there is an error. * @throws CentralRepoException If there is an error.
*/ */
private void getCorrelationTypesFromCr() throws CentralRepoException { private void getCorrelationTypesFromCr() throws CentralRepoException {
// clear out the cache // clear out the cache
synchronized(typeCache) { synchronized (typeCache) {
typeCache.invalidateAll(); typeCache.invalidateAll();
isCRTypeCacheInitialized = false; isCRTypeCacheInitialized = false;
} }
String sql = "SELECT * FROM correlation_types";
try ( Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();) {
synchronized(typeCache) { String sql = "SELECT * FROM correlation_types";
try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();) {
synchronized (typeCache) {
while (resultSet.next()) { while (resultSet.next()) {
CorrelationAttributeInstance.Type aType = getCorrelationTypeFromResultSet(resultSet); CorrelationAttributeInstance.Type aType = getCorrelationTypeFromResultSet(resultSet);
typeCache.put(aType.getId(), aType); typeCache.put(aType.getId(), aType);
@ -3462,9 +3489,9 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} }
} catch (SQLException ex) { } catch (SQLException ex) {
throw new CentralRepoException("Error getting correlation types.", ex); // NON-NLS throw new CentralRepoException("Error getting correlation types.", ex); // NON-NLS
} }
} }
/** /**
* Convert a ResultSet to a EamCase object * Convert a ResultSet to a EamCase object
* *
@ -3622,13 +3649,13 @@ abstract class RdbmsCentralRepo implements CentralRepository {
case POSTGRESQL: case POSTGRESQL:
return "INSERT " + sql + " ON CONFLICT DO NOTHING"; //NON-NLS return "INSERT " + sql + " ON CONFLICT DO NOTHING"; //NON-NLS
case SQLITE: case SQLITE:
return "INSERT OR IGNORE " + sql; return "INSERT OR IGNORE " + sql;
default: default:
throw new CentralRepoException("Unknown Central Repo DB platform" + CentralRepoDbManager.getSavedDbChoice().getDbPlatform()); throw new CentralRepoException("Unknown Central Repo DB platform" + CentralRepoDbManager.getSavedDbChoice().getDbPlatform());
} }
} }
/** /**
* Determine if a specific column already exists in a specific table * Determine if a specific column already exists in a specific table
* *
@ -3741,7 +3768,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
*/ */
if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 2)) < 0) { if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 2)) < 0) {
final String addIntegerColumnTemplate = "ALTER TABLE %s ADD COLUMN %s INTEGER;"; //NON-NLS final String addIntegerColumnTemplate = "ALTER TABLE %s ADD COLUMN %s INTEGER;"; //NON-NLS
final String addSsidTableTemplate = RdbmsCentralRepoFactory.getCreateArtifactInstancesTableTemplate(selectedPlatform); final String addSsidTableTemplate = RdbmsCentralRepoFactory.getCreateArtifactInstancesTableTemplate(selectedPlatform);
final String addCaseIdIndexTemplate = RdbmsCentralRepoFactory.getAddCaseIdIndexTemplate(); final String addCaseIdIndexTemplate = RdbmsCentralRepoFactory.getAddCaseIdIndexTemplate();
final String addDataSourceIdIndexTemplate = RdbmsCentralRepoFactory.getAddDataSourceIdIndexTemplate(); final String addDataSourceIdIndexTemplate = RdbmsCentralRepoFactory.getAddDataSourceIdIndexTemplate();
@ -3761,7 +3788,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
default: default:
throw new CentralRepoException("Currently selected database platform \"" + selectedPlatform.name() + "\" can not be upgraded.", Bundle.AbstractSqlEamDb_cannotUpgrage_message(selectedPlatform.name())); throw new CentralRepoException("Currently selected database platform \"" + selectedPlatform.name() + "\" can not be upgraded.", Bundle.AbstractSqlEamDb_cannotUpgrage_message(selectedPlatform.name()));
} }
final String dataSourcesTableName = "data_sources"; final String dataSourcesTableName = "data_sources";
final String dataSourceObjectIdColumnName = "datasource_obj_id"; final String dataSourceObjectIdColumnName = "datasource_obj_id";
if (!doesColumnExist(conn, dataSourcesTableName, dataSourceObjectIdColumnName)) { if (!doesColumnExist(conn, dataSourcesTableName, dataSourceObjectIdColumnName)) {
@ -3926,7 +3953,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
// Upgrade to 1.4 // Upgrade to 1.4
(new CentralRepoDbUpgrader13To14()).upgradeSchema(dbSchemaVersion, conn); (new CentralRepoDbUpgrader13To14()).upgradeSchema(dbSchemaVersion, conn);
// Upgrade to 1.5 // Upgrade to 1.5
(new CentralRepoDbUpgrader14To15()).upgradeSchema(dbSchemaVersion, conn); (new CentralRepoDbUpgrader14To15()).upgradeSchema(dbSchemaVersion, conn);

View File

@ -27,7 +27,6 @@ import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
@ -55,6 +54,8 @@ import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.autopsy.events.AutopsyEvent;
/** /**
* Listen for case events and update entries in the Central Repository database * Listen for case events and update entries in the Central Repository database
@ -66,12 +67,12 @@ final class CaseEventListener implements PropertyChangeListener {
private static final Logger LOGGER = Logger.getLogger(CaseEventListener.class.getName()); private static final Logger LOGGER = Logger.getLogger(CaseEventListener.class.getName());
private final ExecutorService jobProcessingExecutor; private final ExecutorService jobProcessingExecutor;
private static final String CASE_EVENT_THREAD_NAME = "Case-Event-Listener-%d"; private static final String CASE_EVENT_THREAD_NAME = "Case-Event-Listener-%d";
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of( private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED,
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED, Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED, Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED, Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED,
Case.Events.DATA_SOURCE_ADDED, Case.Events.DATA_SOURCE_ADDED,
Case.Events.TAG_DEFINITION_CHANGED, Case.Events.TAG_DEFINITION_CHANGED,
Case.Events.CURRENT_CASE, Case.Events.CURRENT_CASE,
Case.Events.DATA_SOURCE_NAME_CHANGED); Case.Events.DATA_SOURCE_NAME_CHANGED);
@ -86,6 +87,10 @@ final class CaseEventListener implements PropertyChangeListener {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if (!(evt instanceof AutopsyEvent) || (((AutopsyEvent) evt).getSourceType() != AutopsyEvent.SourceType.LOCAL)) {
return;
}
CentralRepository dbManager; CentralRepository dbManager;
try { try {
dbManager = CentralRepository.getInstance(); dbManager = CentralRepository.getInstance();
@ -93,7 +98,7 @@ final class CaseEventListener implements PropertyChangeListener {
LOGGER.log(Level.SEVERE, "Failed to get instance of db manager.", ex); LOGGER.log(Level.SEVERE, "Failed to get instance of db manager.", ex);
return; return;
} }
// If any changes are made to which event types are handled the change // If any changes are made to which event types are handled the change
// must also be made to CASE_EVENTS_OF_INTEREST. // must also be made to CASE_EVENTS_OF_INTEREST.
switch (Case.Events.valueOf(evt.getPropertyName())) { switch (Case.Events.valueOf(evt.getPropertyName())) {
@ -127,7 +132,7 @@ final class CaseEventListener implements PropertyChangeListener {
break; break;
} }
} }
/* /*
* Add all of our Case Event Listeners to the case. * Add all of our Case Event Listeners to the case.
*/ */
@ -142,6 +147,46 @@ final class CaseEventListener implements PropertyChangeListener {
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, this); Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, this);
} }
/**
* Returns true if the tag has a notable status.
*
* @param t The tag to use in determination.
*
* @return Whether or not it is a notable tag.
*/
private static boolean isNotableTag(Tag t) {
return (t != null && isNotableTagName(t.getName()));
}
/**
* Returns true if the tag name has a notable status.
*
* @param t The tag name to use in determination.
*
* @return Whether or not it is a notable tag name.
*/
private static boolean isNotableTagName(TagName t) {
return (t != null && TagsManager.getNotableTagDisplayNames().contains(t.getDisplayName()));
}
/**
* Searches a list of tags for a tag with a notable status.
*
* @param tags The tags to search.
*
* @return Whether or not the list contains a notable tag.
*/
private static boolean hasNotableTag(List<? extends Tag> tags) {
if (tags == null) {
return false;
}
return tags.stream()
.filter(CaseEventListener::isNotableTag)
.findFirst()
.isPresent();
}
private final class ContentTagTask implements Runnable { private final class ContentTagTask implements Runnable {
private final CentralRepository dbManager; private final CentralRepository dbManager;
@ -158,72 +203,98 @@ final class CaseEventListener implements PropertyChangeListener {
return; return;
} }
AbstractFile af; Case.Events curEventType = Case.Events.valueOf(event.getPropertyName());
TskData.FileKnown knownStatus; if (curEventType == Case.Events.CONTENT_TAG_ADDED && event instanceof ContentTagAddedEvent) {
String comment; handleTagAdded((ContentTagAddedEvent) event);
if (Case.Events.valueOf(event.getPropertyName()) == Case.Events.CONTENT_TAG_ADDED) { } else if (curEventType == Case.Events.CONTENT_TAG_DELETED && event instanceof ContentTagDeletedEvent) {
// For added tags, we want to change the known status to BAD if the handleTagDeleted((ContentTagDeletedEvent) event);
// tag that was just added is in the list of central repo tags. } else {
final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) event; LOGGER.log(Level.SEVERE,
final ContentTag tagAdded = tagAddedEvent.getAddedTag(); String.format("Received an event %s of type %s and was expecting either CONTENT_TAG_ADDED or CONTENT_TAG_DELETED.",
event, curEventType));
}
}
if (TagsManager.getNotableTagDisplayNames().contains(tagAdded.getName().getDisplayName())) { private void handleTagDeleted(ContentTagDeletedEvent evt) {
if (tagAdded.getContent() instanceof AbstractFile) { // ensure tag deleted event has a valid content id
af = (AbstractFile) tagAdded.getContent(); if (evt.getDeletedTagInfo() == null) {
knownStatus = TskData.FileKnown.BAD; LOGGER.log(Level.SEVERE, "ContentTagDeletedEvent did not have valid content to provide a content id.");
comment = tagAdded.getComment(); return;
} else {
LOGGER.log(Level.WARNING, "Error updating non-file object");
return;
}
} else {
// The added tag isn't flagged as bad in central repo, so do nothing
return;
}
} else { // CONTENT_TAG_DELETED
// For deleted tags, we want to set the file status to UNKNOWN if:
// - The tag that was just removed is notable in central repo
// - There are no remaining tags that are notable
final ContentTagDeletedEvent tagDeletedEvent = (ContentTagDeletedEvent) event;
long contentID = tagDeletedEvent.getDeletedTagInfo().getContentID();
String tagName = tagDeletedEvent.getDeletedTagInfo().getName().getDisplayName();
if (!TagsManager.getNotableTagDisplayNames().contains(tagName)) {
// If the tag that got removed isn't on the list of central repo tags, do nothing
return;
}
try {
// Get the remaining tags on the content object
Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(contentID);
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
List<ContentTag> tags = tagsManager.getContentTagsByContent(content);
if (tags.stream()
.map(tag -> tag.getName().getDisplayName())
.filter(TagsManager.getNotableTagDisplayNames()::contains)
.collect(Collectors.toList())
.isEmpty()) {
// There are no more bad tags on the object
if (content instanceof AbstractFile) {
af = (AbstractFile) content;
knownStatus = TskData.FileKnown.UNKNOWN;
comment = "";
} else {
LOGGER.log(Level.WARNING, "Error updating non-file object");
return;
}
} else {
// There's still at least one bad tag, so leave the known status as is
return;
}
} catch (TskCoreException | NoCurrentCaseException ex) {
LOGGER.log(Level.SEVERE, "Failed to find content", ex);
return;
}
} }
try {
// obtain content
Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(evt.getDeletedTagInfo().getContentID());
if (content == null) {
LOGGER.log(Level.WARNING,
String.format("Unable to get content for item with content id: %d.", evt.getDeletedTagInfo().getContentID()));
return;
}
// then handle the event
handleTagChange(content);
} catch (NoCurrentCaseException | TskCoreException ex) {
LOGGER.log(Level.WARNING, "Error updating non-file object: " + evt.getDeletedTagInfo().getContentID(), ex);
}
}
private void handleTagAdded(ContentTagAddedEvent evt) {
// ensure tag added event has a valid content id
if (evt.getAddedTag() == null || evt.getAddedTag().getContent() == null) {
LOGGER.log(Level.SEVERE, "ContentTagAddedEvent did not have valid content to provide a content id.");
return;
}
// then handle the event
handleTagChange(evt.getAddedTag().getContent());
}
/**
* When a tag is added or deleted, check if there are other notable tags
* for the item. If there are, set known status as notable. If not set
* status as unknown.
*
* @param content The content for the tag that was added or deleted.
*/
private void handleTagChange(Content content) {
AbstractFile af = null;
try {
af = Case.getCurrentCaseThrows().getSleuthkitCase().getAbstractFileById(content.getId());
} catch (NoCurrentCaseException | TskCoreException ex) {
Long contentID = (content != null) ? content.getId() : null;
LOGGER.log(Level.WARNING, "Error updating non-file object: " + contentID, ex);
}
if (af == null) {
return;
}
try {
// Get the tags on the content object
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
if (hasNotableTag(tagsManager.getContentTagsByContent(content))) {
// if there is a notable tag on the object, set content known status to bad
setContentKnownStatus(af, TskData.FileKnown.BAD);
} else {
// otherwise, set to unknown
setContentKnownStatus(af, TskData.FileKnown.UNKNOWN);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
LOGGER.log(Level.SEVERE, "Failed to obtain tags manager for case.", ex);
}
}
/**
* Sets the known status for the correlation attribute instance for the
* given abstract file.
*
* @param af The abstract file for which to set the correlation
* attribute instance.
* @param knownStatus The new known status for the correlation attribute
* instance.
*/
private void setContentKnownStatus(AbstractFile af, TskData.FileKnown knownStatus) {
final CorrelationAttributeInstance eamArtifact = CorrelationAttributeUtil.makeCorrAttrFromFile(af); final CorrelationAttributeInstance eamArtifact = CorrelationAttributeUtil.makeCorrAttrFromFile(af);
if (eamArtifact != null) { if (eamArtifact != null) {
@ -234,7 +305,7 @@ final class CaseEventListener implements PropertyChangeListener {
LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS
} }
} }
} // CONTENT_TAG_ADDED, CONTENT_TAG_DELETED }
} }
private final class BlackboardTagTask implements Runnable { private final class BlackboardTagTask implements Runnable {
@ -253,87 +324,125 @@ final class CaseEventListener implements PropertyChangeListener {
return; return;
} }
Content content; Case.Events curEventType = Case.Events.valueOf(event.getPropertyName());
BlackboardArtifact bbArtifact; if (curEventType == Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED && event instanceof BlackBoardArtifactTagAddedEvent) {
TskData.FileKnown knownStatus; handleTagAdded((BlackBoardArtifactTagAddedEvent) event);
String comment; } else if (curEventType == Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED && event instanceof BlackBoardArtifactTagDeletedEvent) {
if (Case.Events.valueOf(event.getPropertyName()) == Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED) { handleTagDeleted((BlackBoardArtifactTagDeletedEvent) event);
// For added tags, we want to change the known status to BAD if the } else {
// tag that was just added is in the list of central repo tags. LOGGER.log(Level.WARNING,
final BlackBoardArtifactTagAddedEvent tagAddedEvent = (BlackBoardArtifactTagAddedEvent) event; String.format("Received an event %s of type %s and was expecting either CONTENT_TAG_ADDED or CONTENT_TAG_DELETED.",
final BlackboardArtifactTag tagAdded = tagAddedEvent.getAddedTag(); event, curEventType));
if (TagsManager.getNotableTagDisplayNames().contains(tagAdded.getName().getDisplayName())) {
content = tagAdded.getContent();
bbArtifact = tagAdded.getArtifact();
knownStatus = TskData.FileKnown.BAD;
comment = tagAdded.getComment();
} else {
// The added tag isn't flagged as bad in central repo, so do nothing
return;
}
} else { //BLACKBOARD_ARTIFACT_TAG_DELETED
Case openCase;
try {
openCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex);
return;
}
// For deleted tags, we want to set the file status to UNKNOWN if:
// - The tag that was just removed is notable in central repo
// - There are no remaining tags that are notable
final BlackBoardArtifactTagDeletedEvent tagDeletedEvent = (BlackBoardArtifactTagDeletedEvent) event;
long contentID = tagDeletedEvent.getDeletedTagInfo().getContentID();
long artifactID = tagDeletedEvent.getDeletedTagInfo().getArtifactID();
String tagName = tagDeletedEvent.getDeletedTagInfo().getName().getDisplayName();
if (!TagsManager.getNotableTagDisplayNames().contains(tagName)) {
// If the tag that got removed isn't on the list of central repo tags, do nothing
return;
}
try {
// Get the remaining tags on the artifact
content = openCase.getSleuthkitCase().getContentById(contentID);
bbArtifact = openCase.getSleuthkitCase().getBlackboardArtifact(artifactID);
TagsManager tagsManager = openCase.getServices().getTagsManager();
List<BlackboardArtifactTag> tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbArtifact);
if (tags.stream()
.map(tag -> tag.getName().getDisplayName())
.filter(TagsManager.getNotableTagDisplayNames()::contains)
.collect(Collectors.toList())
.isEmpty()) {
// There are no more bad tags on the object
knownStatus = TskData.FileKnown.UNKNOWN;
comment = "";
} else {
// There's still at least one bad tag, so leave the known status as is
return;
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Failed to find content", ex);
return;
}
} }
}
if ((content instanceof AbstractFile) && (((AbstractFile) content).getKnown() == TskData.FileKnown.KNOWN)) { private void handleTagDeleted(BlackBoardArtifactTagDeletedEvent evt) {
// ensure tag deleted event has a valid content id
if (evt.getDeletedTagInfo() == null) {
LOGGER.log(Level.SEVERE, "BlackBoardArtifactTagDeletedEvent did not have valid content to provide a content id.");
return; return;
} }
try {
Case openCase = Case.getCurrentCaseThrows();
// obtain content
Content content = openCase.getSleuthkitCase().getContentById(evt.getDeletedTagInfo().getContentID());
if (content == null) {
LOGGER.log(Level.WARNING,
String.format("Unable to get content for item with content id: %d.", evt.getDeletedTagInfo().getContentID()));
return;
}
// obtain blackboard artifact
BlackboardArtifact bbArtifact = openCase.getSleuthkitCase().getBlackboardArtifact(evt.getDeletedTagInfo().getArtifactID());
if (bbArtifact == null) {
LOGGER.log(Level.WARNING,
String.format("Unable to get blackboard artifact for item with artifact id: %d.", evt.getDeletedTagInfo().getArtifactID()));
return;
}
// then handle the event
handleTagChange(content, bbArtifact);
} catch (NoCurrentCaseException | TskCoreException ex) {
LOGGER.log(Level.WARNING, "Error updating non-file object.", ex);
}
}
private void handleTagAdded(BlackBoardArtifactTagAddedEvent evt) {
// ensure tag added event has a valid content id
if (evt.getAddedTag() == null || evt.getAddedTag().getContent() == null || evt.getAddedTag().getArtifact() == null) {
LOGGER.log(Level.SEVERE, "BlackBoardArtifactTagAddedEvent did not have valid content to provide a content id.");
return;
}
// then handle the event
handleTagChange(evt.getAddedTag().getContent(), evt.getAddedTag().getArtifact());
}
/**
* When a tag is added or deleted, check if there are other notable tags
* for the item. If there are, set known status as notable. If not set
* status as unknown.
*
* @param content The content for the tag that was added or deleted.
* @param bbArtifact The artifact for the tag that was added or deleted.
*/
private void handleTagChange(Content content, BlackboardArtifact bbArtifact) {
Case openCase;
try {
openCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex);
return;
}
try {
if (isKnownFile(content)) {
return;
}
TagsManager tagsManager = openCase.getServices().getTagsManager();
List<BlackboardArtifactTag> tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbArtifact);
if (hasNotableTag(tags)) {
setArtifactKnownStatus(bbArtifact, TskData.FileKnown.BAD);
} else {
setArtifactKnownStatus(bbArtifact, TskData.FileKnown.UNKNOWN);
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Failed to obtain tags manager for case.", ex);
return;
}
}
/**
* Determines if the content is an abstract file and is a known file.
*
* @param content The content to assess.
*
* @return True if an abstract file and a known file.
*/
private boolean isKnownFile(Content content) {
return ((content instanceof AbstractFile) && (((AbstractFile) content).getKnown() == TskData.FileKnown.KNOWN));
}
/**
* Sets the known status of a blackboard artifact in the central
* repository.
*
* @param bbArtifact The blackboard artifact to set known status.
* @param knownStatus The new known status.
*/
private void setArtifactKnownStatus(BlackboardArtifact bbArtifact, TskData.FileKnown knownStatus) {
List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbArtifact); List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbArtifact);
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) { for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
eamArtifact.setComment(comment);
try { try {
dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus); dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus);
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS
} }
} }
} // BLACKBOARD_ARTIFACT_TAG_ADDED, BLACKBOARD_ARTIFACT_TAG_DELETED }
} }
@ -434,8 +543,8 @@ final class CaseEventListener implements PropertyChangeListener {
//if the file will have no tags with a status which would prevent the current status from being changed //if the file will have no tags with a status which would prevent the current status from being changed
if (!hasTagWithConflictingKnownStatus) { if (!hasTagWithConflictingKnownStatus) {
Content taggedContent = contentTag.getContent(); Content taggedContent = contentTag.getContent();
if (taggedContent instanceof AbstractFile) { if (taggedContent instanceof AbstractFile) {
final CorrelationAttributeInstance eamArtifact = CorrelationAttributeUtil.makeCorrAttrFromFile((AbstractFile)taggedContent); final CorrelationAttributeInstance eamArtifact = CorrelationAttributeUtil.makeCorrAttrFromFile((AbstractFile) taggedContent);
if (eamArtifact != null) { if (eamArtifact != null) {
CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus()); CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
} }

View File

@ -20,11 +20,19 @@ package org.sleuthkit.autopsy.contentviewers;
import java.awt.Component; import java.awt.Component;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.commons.lang3.StringEscapeUtils; import java.util.stream.Collectors;
import javax.swing.JLabel;
import javax.swing.text.EditorKit;
import javax.swing.text.html.HTMLEditorKit;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle; import static org.openide.util.NbBundle.Messages;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
@ -32,8 +40,6 @@ 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.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
@ -46,26 +52,190 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
/** /**
* Annotations view of file contents. * Annotations view of file contents.
*/ */
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
@ServiceProvider(service = DataContentViewer.class, position = 8) @ServiceProvider(service = DataContentViewer.class, position = 8)
@NbBundle.Messages({ @Messages({
"AnnotationsContentViewer.title=Annotations", "AnnotationsContentViewer.title=Annotations",
"AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content." "AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content.",
"AnnotationsContentViewer.centralRepositoryEntry.title=Central Repository Comments",
"AnnotationsContentViewer.centralRepositoryEntryDataLabel.case=Case:",
"AnnotationsContentViewer.centralRepositoryEntryDataLabel.type=Type:",
"AnnotationsContentViewer.centralRepositoryEntryDataLabel.comment=Comment:",
"AnnotationsContentViewer.centralRepositoryEntryDataLabel.path=Path:",
"AnnotationsContentViewer.tagEntry.title=Tags",
"AnnotationsContentViewer.tagEntryDataLabel.tag=Tag:",
"AnnotationsContentViewer.tagEntryDataLabel.tagUser=Examiner:",
"AnnotationsContentViewer.tagEntryDataLabel.comment=Comment:",
"AnnotationsContentViewer.fileHitEntry.artifactCommentTitle=Artifact Comment",
"AnnotationsContentViewer.fileHitEntry.hashSetHitTitle=Hash Set Hit Comments",
"AnnotationsContentViewer.fileHitEntry.interestingFileHitTitle=Interesting File Hit Comments",
"AnnotationsContentViewer.fileHitEntry.setName=Set Name:",
"AnnotationsContentViewer.fileHitEntry.comment=Comment:",
"AnnotationsContentViewer.sourceFile.title=Source File",
"AnnotationsContentViewer.onEmpty=No annotations were found for this particular item."
}) })
public class AnnotationsContentViewer extends javax.swing.JPanel implements DataContentViewer { public class AnnotationsContentViewer extends javax.swing.JPanel implements DataContentViewer {
/**
* Describes a key value pair for an item of type T where the key is the
* field name to display and the value is retrieved from item of type T
* using a provided Function<T, string>.
*
* @param <T> The item type.
*/
private static class ItemEntry<T> {
private final String itemName;
private final Function<T, String> valueRetriever;
ItemEntry(String itemName, Function<T, String> valueRetriever) {
this.itemName = itemName;
this.valueRetriever = valueRetriever;
}
String getItemName() {
return itemName;
}
Function<T, String> getValueRetriever() {
return valueRetriever;
}
String retrieveValue(T object) {
return valueRetriever.apply(object);
}
}
/**
* Describes a section that will be appended to the annotations view panel.
*
* @param <T> The item type for items to display.
*/
private static class SectionConfig<T> {
private final String title;
private final List<ItemEntry<T>> attributes;
SectionConfig(String title, List<ItemEntry<T>> attributes) {
this.title = title;
this.attributes = attributes;
}
/**
* @return The title for the section.
*/
String getTitle() {
return title;
}
/**
* @return Describes key-value pairs on the object to display to the
* user.
*/
List<ItemEntry<T>> getAttributes() {
return attributes;
}
}
private static final Logger logger = Logger.getLogger(AnnotationsContentViewer.class.getName()); private static final Logger logger = Logger.getLogger(AnnotationsContentViewer.class.getName());
private static final String EMPTY_HTML = "<html><head></head><body></body></html>";
private static final int DEFAULT_FONT_SIZE = new JLabel().getFont().getSize();
// how big the subheader should be
private static final int SUBHEADER_FONT_SIZE = DEFAULT_FONT_SIZE * 12 / 11;
// how big the header should be
private static final int HEADER_FONT_SIZE = DEFAULT_FONT_SIZE * 14 / 11;
// the subsection indent
private static final int DEFAULT_SUBSECTION_LEFT_PAD = DEFAULT_FONT_SIZE;
// spacing occurring after an item
private static final int DEFAULT_TABLE_SPACING = DEFAULT_FONT_SIZE;
private static final int DEFAULT_SECTION_SPACING = DEFAULT_FONT_SIZE * 2;
private static final int DEFAULT_SUBSECTION_SPACING = DEFAULT_FONT_SIZE / 2;
private static final int CELL_SPACING = DEFAULT_FONT_SIZE / 2;
// html stylesheet classnames for components
private static final String MESSAGE_CLASSNAME = "message";
private static final String SUBSECTION_CLASSNAME = "subsection";
private static final String SUBHEADER_CLASSNAME = "subheader";
private static final String SECTION_CLASSNAME = "section";
private static final String HEADER_CLASSNAME = "header";
private static final String VERTICAL_TABLE_CLASSNAME = "vertical-table";
// additional styling for components
private static final String STYLE_SHEET_RULE
= String.format(" .%s { font-size: %dpx;font-style:italic; margin: 0px; padding: 0px; } ", MESSAGE_CLASSNAME, DEFAULT_FONT_SIZE)
+ String.format(" .%s {font-size:%dpx;font-weight:bold; margin: 0px; margin-top: %dpx; padding: 0px; } ",
SUBHEADER_CLASSNAME, SUBHEADER_FONT_SIZE, DEFAULT_SUBSECTION_SPACING)
+ String.format(" .%s { font-size:%dpx;font-weight:bold; margin: 0px; padding: 0px; } ", HEADER_CLASSNAME, HEADER_FONT_SIZE)
+ String.format(" td { vertical-align: top; font-size:%dpx; text-align: left; margin: 0px; padding: 0px %dpx 0px 0px;} ", DEFAULT_FONT_SIZE, CELL_SPACING)
+ String.format(" th { vertical-align: top; text-align: left; margin: 0px; padding: 0px %dpx 0px 0px} ", DEFAULT_FONT_SIZE, CELL_SPACING)
+ String.format(" .%s { margin: %dpx 0px; padding-left: %dpx; } ", SUBSECTION_CLASSNAME, DEFAULT_SUBSECTION_SPACING, DEFAULT_SUBSECTION_LEFT_PAD)
+ String.format(" .%s { margin-bottom: %dpx; } ", SECTION_CLASSNAME, DEFAULT_SECTION_SPACING);
// describing table values for a tag
private static final List<ItemEntry<Tag>> TAG_ENTRIES = Arrays.asList(
new ItemEntry<>(Bundle.AnnotationsContentViewer_tagEntryDataLabel_tag(),
(tag) -> (tag.getName() != null) ? tag.getName().getDisplayName() : null),
new ItemEntry<>(Bundle.AnnotationsContentViewer_tagEntryDataLabel_tagUser(), (tag) -> tag.getUserName()),
new ItemEntry<>(Bundle.AnnotationsContentViewer_tagEntryDataLabel_comment(), (tag) -> tag.getComment())
);
private static final SectionConfig<Tag> TAG_CONFIG
= new SectionConfig<>(Bundle.AnnotationsContentViewer_tagEntry_title(), TAG_ENTRIES);
// file set attributes and table configurations
private static final List<ItemEntry<BlackboardArtifact>> FILESET_HIT_ENTRIES = Arrays.asList(
new ItemEntry<>(Bundle.AnnotationsContentViewer_fileHitEntry_setName(),
(bba) -> tryGetAttribute(bba, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)),
new ItemEntry<>(Bundle.AnnotationsContentViewer_fileHitEntry_comment(),
(bba) -> tryGetAttribute(bba, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT))
);
private static final SectionConfig<BlackboardArtifact> INTERESTING_FILE_CONFIG
= new SectionConfig<>(Bundle.AnnotationsContentViewer_fileHitEntry_interestingFileHitTitle(), FILESET_HIT_ENTRIES);
private static final SectionConfig<BlackboardArtifact> HASHSET_CONFIG
= new SectionConfig<>(Bundle.AnnotationsContentViewer_fileHitEntry_hashSetHitTitle(), FILESET_HIT_ENTRIES);
private static final SectionConfig<BlackboardArtifact> ARTIFACT_COMMENT_CONFIG
= new SectionConfig<>(Bundle.AnnotationsContentViewer_fileHitEntry_artifactCommentTitle(), FILESET_HIT_ENTRIES);
// central repository attributes and table configuration
private static final List<ItemEntry<CorrelationAttributeInstance>> CR_COMMENTS_ENTRIES = Arrays.asList(
new ItemEntry<>(Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_case(),
cai -> (cai.getCorrelationCase() != null) ? cai.getCorrelationCase().getDisplayName() : null),
new ItemEntry<>(Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_comment(), cai -> cai.getComment()),
new ItemEntry<>(Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_path(), cai -> cai.getFilePath())
);
private static final SectionConfig<CorrelationAttributeInstance> CR_COMMENTS_CONFIG
= new SectionConfig<>(Bundle.AnnotationsContentViewer_centralRepositoryEntry_title(), CR_COMMENTS_ENTRIES);
/** /**
* Creates an instance of AnnotationsContentViewer. * Creates an instance of AnnotationsContentViewer.
*/ */
public AnnotationsContentViewer() { public AnnotationsContentViewer() {
initComponents(); initComponents();
Utilities.configureTextPaneAsHtml(jTextPane1); Utilities.configureTextPaneAsHtml(jTextPane1);
// get html editor kit and apply additional style rules
EditorKit editorKit = jTextPane1.getEditorKit();
if (editorKit instanceof HTMLEditorKit) {
HTMLEditorKit htmlKit = (HTMLEditorKit) editorKit;
htmlKit.getStyleSheet().addRule(STYLE_SHEET_RULE);
}
} }
@Override @Override
@ -75,7 +245,8 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
return; return;
} }
StringBuilder html = new StringBuilder(); Document html = Jsoup.parse(EMPTY_HTML);
Element body = html.getElementsByTag("body").first();
BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class); BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class);
Content sourceFile = null; Content sourceFile = null;
@ -101,279 +272,439 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
artifact.getDisplayName(), artifact.getArtifactID()), ex); artifact.getDisplayName(), artifact.getArtifactID()), ex);
} }
boolean somethingWasRendered = false;
if (artifact != null) { if (artifact != null) {
populateTagData(html, artifact, sourceFile); somethingWasRendered = renderArtifact(body, artifact, sourceFile);
} else { } else {
populateTagData(html, sourceFile); somethingWasRendered = renderContent(body, sourceFile, false);
} }
if (sourceFile instanceof AbstractFile) { if (!somethingWasRendered) {
populateCentralRepositoryData(html, artifact, (AbstractFile) sourceFile); appendMessage(body, Bundle.AnnotationsContentViewer_onEmpty());
} }
setText(html.toString()); jTextPane1.setText(html.html());
jTextPane1.setCaretPosition(0); jTextPane1.setCaretPosition(0);
} }
/** /**
* Populate the "Selected Item" sections with tag data for the supplied * Renders annotations for an artifact.
* content.
* *
* @param html The HTML text to update. * @param parent The html element to render content int.
* @param content Selected content. * @param bba The blackboard artifact to render.
* @param sourceContent The content from which the blackboard artifact
* comes.
*
* @return If any content was actually rendered.
*/ */
private void populateTagData(StringBuilder html, Content content) { private static boolean renderArtifact(Element parent, BlackboardArtifact bba, Content sourceContent) {
boolean contentRendered = appendEntries(parent, TAG_CONFIG, getTags(bba), false);
if (CentralRepository.isEnabled()) {
List<CorrelationAttributeInstance> centralRepoComments = getCentralRepositoryData(bba);
boolean crRendered = appendEntries(parent, CR_COMMENTS_CONFIG, centralRepoComments, false);
contentRendered = contentRendered || crRendered;
}
// if artifact is a hashset hit or interesting file and has a non-blank comment
if ((ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() == bba.getArtifactTypeID()
|| ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() == bba.getArtifactTypeID())
&& (hasTskComment(bba))) {
boolean filesetRendered = appendEntries(parent, ARTIFACT_COMMENT_CONFIG, Arrays.asList(bba), false);
contentRendered = contentRendered || filesetRendered;
}
Element sourceFileSection = appendSection(parent, Bundle.AnnotationsContentViewer_sourceFile_title());
boolean sourceFileRendered = renderContent(sourceFileSection, sourceContent, true);
if (!sourceFileRendered) {
sourceFileSection.remove();
}
return contentRendered || sourceFileRendered;
}
/**
* Renders annotations for a content item.
*
* @param parent The parent within which to render.
* @param sourceContent The content for which annotations will be gathered.
* @param isSubheader True if this section should be rendered as a
* subheader as opposed to a top-level header.
*
* @return If any content was actually rendered.
*/
private static boolean renderContent(Element parent, Content sourceContent, boolean isSubheader) {
boolean contentRendered = appendEntries(parent, TAG_CONFIG, getTags(sourceContent), isSubheader);
if (sourceContent instanceof AbstractFile) {
AbstractFile sourceFile = (AbstractFile) sourceContent;
if (CentralRepository.isEnabled()) {
List<CorrelationAttributeInstance> centralRepoComments = getCentralRepositoryData(sourceFile);
boolean crRendered = appendEntries(parent, CR_COMMENTS_CONFIG, centralRepoComments, isSubheader);
contentRendered = contentRendered || crRendered;
}
boolean hashsetRendered = appendEntries(parent, HASHSET_CONFIG,
getFileSetHits(sourceFile, ARTIFACT_TYPE.TSK_HASHSET_HIT),
isSubheader);
boolean interestingFileRendered = appendEntries(parent, INTERESTING_FILE_CONFIG,
getFileSetHits(sourceFile, ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT),
isSubheader);
contentRendered = contentRendered || hashsetRendered || interestingFileRendered;
}
return contentRendered;
}
/**
* Retrieves tags associated with a content item.
*
* @param sourceContent The content for which to gather content.
*
* @return The Tags associated with this item.
*/
private static List<ContentTag> getTags(Content sourceContent) {
try { try {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
return tskCase.getContentTagsByContent(sourceContent);
startSection(html, "Selected Item");
List<ContentTag> fileTagsList = tskCase.getContentTagsByContent(content);
if (fileTagsList.isEmpty()) {
addMessage(html, "There are no tags for the selected content.");
} else {
for (ContentTag tag : fileTagsList) {
addTagEntry(html, tag);
}
}
endSection(html);
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Exception while getting tags from the case database.", ex); //NON-NLS logger.log(Level.SEVERE, "Exception while getting tags from the case database.", ex); //NON-NLS
} }
return new ArrayList<>();
} }
/** /**
* Populate the "Selected Item" and "Source File" sections with tag data for * Retrieves tags for blackboard artifact tags.
* a supplied artifact.
* *
* @param html The HTML text to update. * @param bba The blackboard artifact for which to retrieve tags.
* @param artifact A selected artifact. *
* @param sourceFile The source content of the selected artifact. * @return The found tags.
*/ */
private void populateTagData(StringBuilder html, BlackboardArtifact artifact, Content sourceFile) { private static List<BlackboardArtifactTag> getTags(BlackboardArtifact bba) {
try { try {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
return tskCase.getBlackboardArtifactTagsByArtifact(bba);
startSection(html, "Selected Item");
List<BlackboardArtifactTag> artifactTagsList = tskCase.getBlackboardArtifactTagsByArtifact(artifact);
if (artifactTagsList.isEmpty()) {
addMessage(html, "There are no tags for the selected artifact.");
} else {
for (BlackboardArtifactTag tag : artifactTagsList) {
addTagEntry(html, tag);
}
}
endSection(html);
if (sourceFile != null) {
startSection(html, "Source File");
List<ContentTag> fileTagsList = tskCase.getContentTagsByContent(sourceFile);
if (fileTagsList.isEmpty()) {
addMessage(html, "There are no tags for the source content.");
} else {
for (ContentTag tag : fileTagsList) {
addTagEntry(html, tag);
}
}
endSection(html);
}
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Exception while getting tags from the case database.", ex); //NON-NLS logger.log(Level.SEVERE, "Exception while getting tags from the case database.", ex); //NON-NLS
} }
return new ArrayList<>();
} }
/** /**
* Populate the "Central Repository Comments" section with data. * Retrieves the blackboard artifacts for a source file matching a certain
* type that have a non-blank TSK_COMMENT.
*
* @param sourceFile The source file for which to fetch artifacts.
* @param type The type of blackboard artifact to fetch.
*
* @return The artifacts found matching this type.
*/
private static List<BlackboardArtifact> getFileSetHits(AbstractFile sourceFile, ARTIFACT_TYPE type) {
try {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
return tskCase.getBlackboardArtifacts(type, sourceFile.getId()).stream()
.filter((bba) -> hasTskComment(bba))
.collect(Collectors.toList());
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Exception while getting file set hits from the case database.", ex); //NON-NLS
}
return new ArrayList<>();
}
/**
* Returns true if the artifact contains a non-blank TSK_COMMENT attribute.
*
* @param artifact The artifact to check.
*
* @return True if it has a non-blank TSK_COMMENT.
*/
private static boolean hasTskComment(BlackboardArtifact artifact) {
return StringUtils.isNotBlank(tryGetAttribute(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT));
}
/**
* Attempts to retrieve the attribute of a particular type from a blackboard
* artifact.
*
* @param artifact The artifact from which to retrieve the information.
* @param attributeType The attribute type to retrieve from the artifact.
*
* @return The string value of the attribute or null if not found.
*/
private static String tryGetAttribute(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE attributeType) {
if (artifact == null) {
return null;
}
BlackboardAttribute attr = null;
try {
attr = artifact.getAttribute(new BlackboardAttribute.Type(attributeType));
} catch (TskCoreException ex) {
logger.log(Level.WARNING, String.format("Unable to fetch attribute of type %s for artifact %s", attributeType, artifact), ex);
}
if (attr == null) {
return null;
}
return attr.getValueString();
}
/**
* Gets the "Central Repository Comments" section with data for the
* blackboard artifact.
*
* @param artifact The selected artifact.
*
* @return The Correlation Attribute Instances associated with the artifact
* that have comments.
*/
private static List<CorrelationAttributeInstance> getCentralRepositoryData(BlackboardArtifact artifact) {
if (artifact == null) {
return new ArrayList<>();
}
List<Pair<CorrelationAttributeInstance.Type, String>> lookupKeys = CorrelationAttributeUtil.makeCorrAttrsForCorrelation(artifact)
.stream()
.map(cai -> Pair.of(cai.getCorrelationType(), cai.getCorrelationValue()))
.collect(Collectors.toList());
return getCorrelationAttributeComments(lookupKeys);
}
/**
* Gets the "Central Repository Comments" section with data.
* *
* @param html The HTML text to update.
* @param artifact A selected artifact (can be null).
* @param sourceFile A selected file, or a source file of the selected * @param sourceFile A selected file, or a source file of the selected
* artifact. * artifact.
*
* @return The Correlation Attribute Instances associated with the
* sourcefile that have comments.
*/ */
private void populateCentralRepositoryData(StringBuilder html, BlackboardArtifact artifact, AbstractFile sourceFile) { private static List<CorrelationAttributeInstance> getCentralRepositoryData(AbstractFile sourceFile) {
if (CentralRepository.isEnabled()) { if (sourceFile == null || StringUtils.isEmpty(sourceFile.getMd5Hash())) {
startSection(html, "Central Repository Comments"); return new ArrayList<>();
List<CorrelationAttributeInstance> instancesList = new ArrayList<>();
if (artifact != null) {
instancesList.addAll(CorrelationAttributeUtil.makeCorrAttrsForCorrelation(artifact));
}
try {
List<CorrelationAttributeInstance.Type> artifactTypes = CentralRepository.getInstance().getDefinedCorrelationTypes();
String md5 = sourceFile.getMd5Hash();
if (md5 != null && !md5.isEmpty() && null != artifactTypes && !artifactTypes.isEmpty()) {
for (CorrelationAttributeInstance.Type attributeType : artifactTypes) {
if (attributeType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) {
CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCase());
instancesList.add(new CorrelationAttributeInstance(
attributeType,
md5,
correlationCase,
CorrelationDataSource.fromTSKDataSource(correlationCase, sourceFile.getDataSource()),
sourceFile.getParentPath() + sourceFile.getName(),
"",
sourceFile.getKnown(),
sourceFile.getId()));
break;
}
}
}
boolean commentDataFound = false;
for (CorrelationAttributeInstance instance : instancesList) {
List<CorrelationAttributeInstance> correlatedInstancesList
= CentralRepository.getInstance().getArtifactInstancesByTypeValue(instance.getCorrelationType(), instance.getCorrelationValue());
for (CorrelationAttributeInstance correlatedInstance : correlatedInstancesList) {
if (correlatedInstance.getComment() != null && correlatedInstance.getComment().isEmpty() == false) {
commentDataFound = true;
addCentralRepositoryEntry(html, correlatedInstance);
}
}
}
if (commentDataFound == false) {
addMessage(html, "There is no comment data for the selected content in the Central Repository.");
}
} catch (CentralRepoException | TskCoreException ex) {
logger.log(Level.SEVERE, "Error connecting to the Central Repository database.", ex); // NON-NLS
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.SEVERE, "Error normalizing instance from Central Repository database.", ex); // NON-NLS
}
endSection(html);
} }
List<CorrelationAttributeInstance.Type> artifactTypes = null;
try {
artifactTypes = CentralRepository.getInstance().getDefinedCorrelationTypes();
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error connecting to the Central Repository database.", ex); // NON-NLS
}
if (artifactTypes == null || artifactTypes.isEmpty()) {
return new ArrayList<>();
}
String md5 = sourceFile.getMd5Hash();
// get key lookups for a file attribute types and the md5 hash
List<Pair<CorrelationAttributeInstance.Type, String>> lookupKeys = artifactTypes.stream()
.filter((attributeType) -> attributeType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID)
.map((attributeType) -> Pair.of(attributeType, md5))
.collect(Collectors.toList());
return getCorrelationAttributeComments(lookupKeys);
} }
/** /**
* Set the text of the text panel. * Given a type and a value for that type, does a lookup in the Central
* Repository for matching values that have comments.
* *
* @param text The text to set to the text panel. * @param lookupKeys The type and value to lookup.
*
* @return The found correlation attribute instances.
*/ */
private void setText(String text) { private static List<CorrelationAttributeInstance> getCorrelationAttributeComments(List<Pair<CorrelationAttributeInstance.Type, String>> lookupKeys) {
jTextPane1.setText("<html><body>" + text + "</body></html>"); //NON-NLS List<CorrelationAttributeInstance> instancesToRet = new ArrayList<>();
try {
// use lookup instances to find the actual correlation attributes for the items selected
for (Pair<CorrelationAttributeInstance.Type, String> typeVal : lookupKeys) {
instancesToRet.addAll(CentralRepository.getInstance()
.getArtifactInstancesByTypeValue(typeVal.getKey(), typeVal.getValue())
.stream()
// for each one found, if it has a comment, return
.filter((cai) -> StringUtils.isNotBlank(cai.getComment()))
.collect(Collectors.toList()));
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error connecting to the Central Repository database.", ex); // NON-NLS
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.SEVERE, "Error normalizing instance from Central Repository database.", ex); // NON-NLS
}
return instancesToRet;
} }
/** /**
* Start a new data section. * Append entries to the parent element in the annotations viewer. Entries
* will be formatted as a table in the format specified in the
* SectionConfig.
* *
* @param html The HTML text to add the section to. * @param parent The parent element for which the entries will be
* @param sectionName The name of the section. * attached.
* @param config The display configuration for this entry type (i.e.
* table type, name, if data is not present).
* @param items The items to display.
* @param isSubsection Whether or not this should be displayed as a
* subsection. If not displayed as a top-level section.
*
* @return If there was actual content rendered for this set of entries.
*/ */
private void startSection(StringBuilder html, String sectionName) { private static <T> boolean appendEntries(Element parent, SectionConfig<T> config, List<? extends T> items,
html.append("<p style=\"font-size:14px;font-weight:bold;\">") boolean isSubsection) {
.append(sectionName) if (items == null || items.isEmpty()) {
.append("</p><br>"); //NON-NLS return false;
}
Element sectionDiv = (isSubsection) ? appendSubsection(parent, config.getTitle()) : appendSection(parent, config.getTitle());
appendVerticalEntryTables(sectionDiv, items, config.getAttributes());
return true;
} }
/** /**
* Add a message. * Appends a table where items are displayed in rows of key-value pairs.
* *
* @param html The HTML text to add the message to. * @param parent The parent to append the table.
* @param message The message text. * @param items The items to process into a series of tables.
* @param rowHeaders The keys and the means to process items in order to get
* key-value pairs.
*
* @return The parent element provided as parameter.
*/ */
private void addMessage(StringBuilder html, String message) { private static <T> Element appendVerticalEntryTables(Element parent, List<? extends T> items, List<ItemEntry<T>> rowHeaders) {
html.append("<p style=\"font-size:11px;font-style:italic;\">") boolean isFirst = true;
.append(message) for (T item : items) {
.append("</p><br>"); //NON-NLS if (item == null) {
continue;
}
List<List<String>> tableData = rowHeaders.stream()
.map(row -> Arrays.asList(row.getItemName(), row.retrieveValue(item)))
.collect(Collectors.toList());
Element childTable = appendTable(parent, 2, tableData, null);
childTable.attr("class", VERTICAL_TABLE_CLASSNAME);
if (isFirst) {
isFirst = false;
} else {
childTable.attr("style", String.format("margin-top: %dpx;", DEFAULT_TABLE_SPACING));
}
}
return parent;
} }
/** /**
* Add a data table containing information about a tag. * Appends a generic table to the parent element.
* *
* @param html The HTML text to add the table to. * @param parent The parent element that will have a table appended
* @param tag The tag whose information will be used to populate the table. * to it.
* @param columnNumber The number of columns to append.
* @param content The content in content.get(row).get(column) format.
* @param columnHeaders The column headers or null if no column headers
* should be created.
*
* @return The created table.
*/ */
@NbBundle.Messages({ private static Element appendTable(Element parent, int columnNumber, List<List<String>> content, List<String> columnHeaders) {
"AnnotationsContentViewer.tagEntryDataLabel.tag=Tag:", Element table = parent.appendElement("table");
"AnnotationsContentViewer.tagEntryDataLabel.tagUser=Tag User:", if (columnHeaders != null && !columnHeaders.isEmpty()) {
"AnnotationsContentViewer.tagEntryDataLabel.comment=Comment:" Element header = table.appendElement("thead");
}) appendRow(header, columnHeaders, columnNumber, true);
private void addTagEntry(StringBuilder html, Tag tag) { }
startTable(html); Element tableBody = table.appendElement("tbody");
addRow(html, Bundle.AnnotationsContentViewer_tagEntryDataLabel_tag(), tag.getName().getDisplayName());
addRow(html, Bundle.AnnotationsContentViewer_tagEntryDataLabel_tagUser(), tag.getUserName()); content.forEach((rowData) -> appendRow(tableBody, rowData, columnNumber, false));
addRow(html, Bundle.AnnotationsContentViewer_tagEntryDataLabel_comment(), formatHtmlString(tag.getComment())); return table;
endTable(html);
} }
/** /**
* Add a data table containing information about a correlation attribute * Appends a row to the parent element (should be thead or tbody).
* instance in the Central Repository.
* *
* @param html The HTML text to add the table to. * @param rowParent The parent table element.
* @param attributeInstance The attribute instance whose information will be * @param data The data to place in columns within the table.
* used to populate the table. * @param columnNumber The number of columns to append.
* @param isHeader Whether or not this should have header cells ('th')
* instead of regular cells ('td').
*
* @return The row created.
*/ */
@NbBundle.Messages({ private static Element appendRow(Element rowParent, List<String> data, int columnNumber, boolean isHeader) {
"AnnotationsContentViewer.centralRepositoryEntryDataLabel.case=Case:", String cellType = isHeader ? "th" : "td";
"AnnotationsContentViewer.centralRepositoryEntryDataLabel.type=Type:", Element row = rowParent.appendElement("tr");
"AnnotationsContentViewer.centralRepositoryEntryDataLabel.comment=Comment:", for (int i = 0; i < columnNumber; i++) {
"AnnotationsContentViewer.centralRepositoryEntryDataLabel.path=Path:" Element cell = row.appendElement(cellType);
}) if (data != null && i < data.size()) {
private void addCentralRepositoryEntry(StringBuilder html, CorrelationAttributeInstance attributeInstance) { cell.text(StringUtils.isEmpty(data.get(i)) ? "" : data.get(i));
startTable(html); }
addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_case(), attributeInstance.getCorrelationCase().getDisplayName()); }
addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_type(), attributeInstance.getCorrelationType().getDisplayName()); return row;
addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_comment(), formatHtmlString(attributeInstance.getComment()));
addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_path(), attributeInstance.getFilePath());
endTable(html);
} }
/** /**
* Start a data table. * Appends a new section with a section header to the parent element.
* *
* @param html The HTML text to add the table to. * @param parent The element to append this section to.
* @param headerText The text for the section.
*
* @return The div for the new section.
*/ */
private void startTable(StringBuilder html) { private static Element appendSection(Element parent, String headerText) {
html.append("<table>"); //NON-NLS Element sectionDiv = parent.appendElement("div");
sectionDiv.attr("class", SECTION_CLASSNAME);
Element header = sectionDiv.appendElement("h1");
header.text(headerText);
header.attr("class", HEADER_CLASSNAME);
return sectionDiv;
} }
/** /**
* Add a data row to a table. * Appends a new subsection with a subsection header to the parent element.
* *
* @param html The HTML text to add the row to. * @param parent The element to append this subsection to.
* @param key The key for the left column of the data row. * @param headerText The text for the subsection.
* @param value The value for the right column of the data row. *
* @return The div for the new subsection.
*/ */
private void addRow(StringBuilder html, String key, String value) { private static Element appendSubsection(Element parent, String headerText) {
html.append("<tr><td valign=\"top\">"); //NON-NLS Element subsectionDiv = parent.appendElement("div");
html.append(key); subsectionDiv.attr("class", SUBSECTION_CLASSNAME);
html.append("</td><td>"); //NON-NLS Element header = subsectionDiv.appendElement("h2");
html.append(value); header.text(headerText);
html.append("</td></tr>"); //NON-NLS header.attr("class", SUBHEADER_CLASSNAME);
return subsectionDiv;
} }
/** /**
* End a data table. * Appends a message to the parent element. This is typically used in the
* event that no data exists for a certain type.
* *
* @param html The HTML text on which to end a table. * @param parent The parent element that will have this message appended to
* it.
* @param message The message to append.
*
* @return The paragraph element for the new message.
*/ */
private void endTable(StringBuilder html) { private static Element appendMessage(Element parent, String message) {
html.append("</table><br><br>"); //NON-NLS Element messageEl = parent.appendElement("p");
} messageEl.text(message);
messageEl.attr("class", MESSAGE_CLASSNAME);
/** return messageEl;
* End a data section.
*
* @param html The HTML text on which to end a section.
*/
private void endSection(StringBuilder html) {
html.append("<br>"); //NON-NLS
}
/**
* Apply escape sequence to special characters. Line feed and carriage
* return character combinations will be converted to HTML line breaks.
*
* @param text The text to format.
*
* @return The formatted text.
*/
private String formatHtmlString(String text) {
String formattedString = StringEscapeUtils.escapeHtml4(text);
return formattedString.replaceAll("(\r\n|\r|\n|\n\r)", "<br>");
} }
/** /**
@ -462,6 +793,6 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
@Override @Override
public void resetComponent() { public void resetComponent() {
setText(""); jTextPane1.setText(EMPTY_HTML);
} }
} }

View File

@ -0,0 +1,56 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.awt.Component;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Common interface implemented by artifact viewers.
*
* An artifact viewer displays the artifact in a custom
* layout panel suitable for the artifact type.
*
*/
public interface ArtifactContentViewer {
/**
* Called to display the contents of the given artifact.
*
* @param artifact the artifact to display.
*/
void setArtifact(BlackboardArtifact artifact);
/**
* Returns the panel.
*
* @return display panel.
*/
Component getComponent();
/**
* Checks whether the given artifact is supported by the viewer.
*
* @param artifact Artifact to check.
*
* @return True if the artifact can be displayed by the viewer, false otherwise.
*/
boolean isSupported(BlackboardArtifact artifact);
}

View File

@ -953,4 +953,10 @@ manager.properties.lafError =\
manager.properties.brokenProperty = Broken default property {0} value: {1} manager.properties.brokenProperty = Broken default property {0} value: {1}
manager.properties.missingProperty = Missing default property {0} value: {1} manager.properties.missingProperty = Missing default property {0} value: {1}
DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
ContactArtifactViewer.contactNameLabel.text=Joanna Doe
ContactArtifactViewer.phonesLabel.text=Phone
ContactArtifactViewer.emailsLabel.text=Email
ContactArtifactViewer.othersLabel.text=Other

View File

@ -15,13 +15,22 @@
# governing permissions and limitations under the License. # governing permissions and limitations under the License.
# #
AnnotationsContentViewer.centralRepositoryEntry.title=Central Repository Comments
AnnotationsContentViewer.centralRepositoryEntryDataLabel.case=Case: AnnotationsContentViewer.centralRepositoryEntryDataLabel.case=Case:
AnnotationsContentViewer.centralRepositoryEntryDataLabel.comment=Comment: AnnotationsContentViewer.centralRepositoryEntryDataLabel.comment=Comment:
AnnotationsContentViewer.centralRepositoryEntryDataLabel.path=Path: AnnotationsContentViewer.centralRepositoryEntryDataLabel.path=Path:
AnnotationsContentViewer.centralRepositoryEntryDataLabel.type=Type: AnnotationsContentViewer.centralRepositoryEntryDataLabel.type=Type:
AnnotationsContentViewer.fileHitEntry.artifactCommentTitle=Artifact Comment
AnnotationsContentViewer.fileHitEntry.comment=Comment:
AnnotationsContentViewer.fileHitEntry.hashSetHitTitle=Hash Set Hit Comments
AnnotationsContentViewer.fileHitEntry.interestingFileHitTitle=Interesting File Hit Comments
AnnotationsContentViewer.fileHitEntry.setName=Set Name:
AnnotationsContentViewer.onEmpty=No annotations were found for this particular item.
AnnotationsContentViewer.sourceFile.title=Source File
AnnotationsContentViewer.tagEntry.title=Tags
AnnotationsContentViewer.tagEntryDataLabel.comment=Comment: AnnotationsContentViewer.tagEntryDataLabel.comment=Comment:
AnnotationsContentViewer.tagEntryDataLabel.tag=Tag: AnnotationsContentViewer.tagEntryDataLabel.tag=Tag:
AnnotationsContentViewer.tagEntryDataLabel.tagUser=Tag User: AnnotationsContentViewer.tagEntryDataLabel.tagUser=Examiner:
AnnotationsContentViewer.title=Annotations AnnotationsContentViewer.title=Annotations
AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content. AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content.
ApplicationContentViewer.title=Application ApplicationContentViewer.title=Application

View File

@ -164,3 +164,5 @@ MediaViewImagePanel.tagsMenu.text_1=\u30bf\u30b0\u30e1\u30cb\u30e5\u30fc
SQLiteViewer.readTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} SQLiteViewer.readTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
# {0} - tableName # {0} - tableName
SQLiteViewer.selectTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u30ab\u30a6\u30f3\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} SQLiteViewer.selectTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u30ab\u30a6\u30f3\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
DefaultArtifactContentViewer.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e
DefaultArtifactContentViewer.copyMenuItem.text=\u30b3\u30d4\u30fc

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<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"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-74,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="namePanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="4" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="contactNameLabel" min="-2" pref="240" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="contactNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="contactNameLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="contactNameLabel" italic="true" property="font" relativeSize="true" size="6"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContactArtifactViewer.contactNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="phonesLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="phonesLabel" property="font" relativeSize="true" size="2"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContactArtifactViewer.phonesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="3" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Container class="javax.swing.JPanel" name="phoneNumbersPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="4" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
</Container>
<Component class="javax.swing.JLabel" name="emailsLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="emailsLabel" property="font" relativeSize="true" size="2"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContactArtifactViewer.emailsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="3" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Container class="javax.swing.JPanel" name="emailsPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="4" gridWidth="4" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
</Container>
<Component class="javax.swing.JLabel" name="othersLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="othersLabel" property="font" relativeSize="true" size="2"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContactArtifactViewer.othersLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="5" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Container class="javax.swing.JPanel" name="otherAttrsPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="6" gridWidth="4" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
</Container>
<Component class="javax.swing.Box$Filler" name="filler1">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 32767]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalGlue"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="7" gridWidth="4" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.Box$Filler" name="filler2">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 0]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.HorizontalGlue"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="4" gridY="0" gridWidth="1" gridHeight="8" fill="1" ipadX="2" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,320 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* This class displays TSK_CONTACT artifact.
*/
public class ContactArtifactViewer extends javax.swing.JPanel implements ArtifactContentViewer {
private final static Logger logger = Logger.getLogger(ContactArtifactViewer.class.getName());
private static final long serialVersionUID = 1L;
/**
* Creates new form for ContactArtifactViewer
*/
public ContactArtifactViewer() {
initComponents();
}
/**
* 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() {
java.awt.GridBagConstraints gridBagConstraints;
namePanel = new javax.swing.JPanel();
contactNameLabel = new javax.swing.JLabel();
phonesLabel = new javax.swing.JLabel();
phoneNumbersPanel = new javax.swing.JPanel();
emailsLabel = new javax.swing.JLabel();
emailsPanel = new javax.swing.JPanel();
othersLabel = new javax.swing.JLabel();
otherAttrsPanel = new javax.swing.JPanel();
filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
setLayout(new java.awt.GridBagLayout());
contactNameLabel.setFont(contactNameLabel.getFont().deriveFont((contactNameLabel.getFont().getStyle() | java.awt.Font.ITALIC) | java.awt.Font.BOLD, contactNameLabel.getFont().getSize()+6));
org.openide.awt.Mnemonics.setLocalizedText(contactNameLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.contactNameLabel.text")); // NOI18N
javax.swing.GroupLayout namePanelLayout = new javax.swing.GroupLayout(namePanel);
namePanel.setLayout(namePanelLayout);
namePanelLayout.setHorizontalGroup(
namePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(namePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(contactNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 240, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
namePanelLayout.setVerticalGroup(
namePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(namePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(contactNameLabel)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
add(namePanel, gridBagConstraints);
phonesLabel.setFont(phonesLabel.getFont().deriveFont(phonesLabel.getFont().getStyle() | java.awt.Font.BOLD, phonesLabel.getFont().getSize()+2));
org.openide.awt.Mnemonics.setLocalizedText(phonesLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.phonesLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(phonesLabel, gridBagConstraints);
phoneNumbersPanel.setLayout(new java.awt.GridBagLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(phoneNumbersPanel, gridBagConstraints);
emailsLabel.setFont(emailsLabel.getFont().deriveFont(emailsLabel.getFont().getStyle() | java.awt.Font.BOLD, emailsLabel.getFont().getSize()+2));
org.openide.awt.Mnemonics.setLocalizedText(emailsLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.emailsLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(emailsLabel, gridBagConstraints);
emailsPanel.setLayout(new java.awt.GridBagLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 4;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(emailsPanel, gridBagConstraints);
othersLabel.setFont(othersLabel.getFont().deriveFont(othersLabel.getFont().getStyle() | java.awt.Font.BOLD, othersLabel.getFont().getSize()+2));
org.openide.awt.Mnemonics.setLocalizedText(othersLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.othersLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 5;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(othersLabel, gridBagConstraints);
otherAttrsPanel.setLayout(new java.awt.GridBagLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 6;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(otherAttrsPanel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 7;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weighty = 1.0;
add(filler1, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 4;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridheight = 8;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.ipadx = 2;
gridBagConstraints.weightx = 1.0;
add(filler2, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
@Override
public void setArtifact(BlackboardArtifact artifact) {
// wipe the panel clean
this.removeAll();
initComponents();
List<BlackboardAttribute> phoneNumList = new ArrayList<>();
List<BlackboardAttribute> emailList = new ArrayList<>();
List<BlackboardAttribute> nameList = new ArrayList<>();
List<BlackboardAttribute> otherList = new ArrayList<>();
try {
// Get all the attributes and group them by the section panels they go in
for (BlackboardAttribute bba : artifact.getAttributes()) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_PHONE")) {
phoneNumList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_EMAIL")) {
emailList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_NAME")) {
nameList.add(bba);
} else {
otherList.add(bba);
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting attributes for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
}
// update name section
updateNamePanel(nameList);
// update contact attributes sections
updateSection(phoneNumList, this.phonesLabel, this.phoneNumbersPanel);
updateSection(emailList, this.emailsLabel, this.emailsPanel);
updateSection(otherList, this.othersLabel, this.otherAttrsPanel);
// repaint
this.revalidate();
}
@Override
public Component getComponent() {
// Slap a vertical scrollbar on the panel.
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
/**
* Checks if the given artifact is supported by this viewer.
* This viewer supports TSK_CONTACT artifacts.
*
* @param artifact artifact to check.
* @return True if the artifact is supported, false otherwise.
*/
@Override
public boolean isSupported(BlackboardArtifact artifact) {
return artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID();
}
/**
* Updates the contact name in the view.
*
* @param attributesList
*/
private void updateNamePanel(List<BlackboardAttribute> attributesList) {
for (BlackboardAttribute bba : attributesList) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_NAME")) {
contactNameLabel.setText(bba.getDisplayString());
break;
}
}
contactNameLabel.revalidate();
}
/**
* Updates the view by displaying the given list of attributes in the given section panel.
*
* @param sectionAttributesList list of attributes to display.
* @param sectionLabel section name label.
* @param sectionPanel section panel to display the attributes in.
*/
private void updateSection(List<BlackboardAttribute> sectionAttributesList, JLabel sectionLabel, JPanel sectionPanel) {
// If there are no attributes for tis section, hide the section panel and the section label
if (sectionAttributesList.isEmpty()) {
sectionLabel.setVisible(false);
sectionPanel.setVisible(false);
return;
}
// create a gridbag layout to show each attribute on one line
GridBagLayout gridBagLayout = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.FIRST_LINE_START;
constraints.gridy = 0;
constraints.insets = new java.awt.Insets(4, 12, 0, 0);
for (BlackboardAttribute bba : sectionAttributesList) {
constraints.fill = GridBagConstraints.NONE;
constraints.weightx = 0;
constraints.gridx = 0;
// Add a label for attribute type
javax.swing.JLabel attrTypeLabel = new javax.swing.JLabel();
String attrLabel = bba.getAttributeType().getDisplayName();
attrTypeLabel.setText(attrLabel);
// make type label bold - uncomment if needed.
//attrTypeLabel.setFont(attrTypeLabel.getFont().deriveFont(Font.BOLD, attrTypeLabel.getFont().getSize() ));
gridBagLayout.setConstraints(attrTypeLabel, constraints);
sectionPanel.add(attrTypeLabel);
// Add the attribute value
constraints.gridx++;
javax.swing.JLabel attrValueLabel = new javax.swing.JLabel();
attrValueLabel.setText(bba.getValueString());
gridBagLayout.setConstraints(attrValueLabel, constraints);
sectionPanel.add(attrValueLabel);
// add a filler to take up rest of the space
constraints.gridx++;
constraints.weightx = 1.0;
constraints.fill = GridBagConstraints.HORIZONTAL;
sectionPanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0)));
constraints.gridy++;
}
sectionPanel.setLayout(gridBagLayout);
sectionPanel.revalidate();
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel contactNameLabel;
private javax.swing.JLabel emailsLabel;
private javax.swing.JPanel emailsPanel;
private javax.swing.Box.Filler filler1;
private javax.swing.Box.Filler filler2;
private javax.swing.JPanel namePanel;
private javax.swing.JPanel otherAttrsPanel;
private javax.swing.JLabel othersLabel;
private javax.swing.JPanel phoneNumbersPanel;
private javax.swing.JLabel phonesLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Container class="javax.swing.JPopupMenu" name="rightClickMenu">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
<Property name="useNullLayout" type="boolean" value="true"/>
</Layout>
<SubComponents>
<MenuItem class="javax.swing.JMenuItem" name="copyMenuItem">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="DefaultArtifactContentViewer.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</MenuItem>
<MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="DefaultArtifactContentViewer.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</MenuItem>
</SubComponents>
</Container>
</NonVisualComponents>
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 58]"/>
</Property>
</Properties>
<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="false"/>
<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="resultsTableScrollPane" pref="100" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="resultsTableScrollPane" alignment="1" pref="58" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="resultsTableScrollPane">
<Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="32"/>
<Property name="verticalScrollBarPolicy" type="int" value="22"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[620, 34]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,524 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2020 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.awt.Component;
import java.awt.Cursor;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.datatransfer.StringSelection;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JMenuItem;
import javax.swing.JTextArea;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.event.TableColumnModelListener;
import javax.swing.text.View;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
import org.netbeans.swing.etable.ETable;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonArray;
import java.util.Locale;
import java.util.Map;
import javax.swing.SwingUtilities;
/**
* This class displays a Blackboard artifact as a table listing all it's
* attributes names and values.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class DefaultArtifactContentViewer extends javax.swing.JPanel implements ArtifactContentViewer {
@NbBundle.Messages({
"DefaultArtifactContentViewer.attrsTableHeader.type=Type",
"DefaultArtifactContentViewer.attrsTableHeader.value=Value",
"DefaultArtifactContentViewer.attrsTableHeader.sources=Source(s)",
"DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database",
"DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database"
})
private final static Logger logger = Logger.getLogger(DefaultArtifactContentViewer.class.getName());
private static final long serialVersionUID = 1L;
private static final String[] COLUMN_HEADERS = {
Bundle.DefaultArtifactContentViewer_attrsTableHeader_type(),
Bundle.DefaultArtifactContentViewer_attrsTableHeader_value(),
Bundle.DefaultArtifactContentViewer_attrsTableHeader_sources()};
private static final int[] COLUMN_WIDTHS = {100, 800, 100};
private static final int CELL_BOTTOM_MARGIN = 5;
private static final int CELL_RIGHT_MARGIN = 1;
public DefaultArtifactContentViewer() {
initResultsTable();
initComponents();
resultsTableScrollPane.setViewportView(resultsTable);
customizeComponents();
resetComponents();
resultsTable.setDefaultRenderer(Object.class, new MultiLineTableCellRenderer());
}
private void initResultsTable() {
resultsTable = new ETable();
resultsTable.setModel(new javax.swing.table.DefaultTableModel() {
private static final long serialVersionUID = 1L;
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
});
resultsTable.setCellSelectionEnabled(true);
resultsTable.getTableHeader().setReorderingAllowed(false);
resultsTable.setColumnHidingAllowed(false);
resultsTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
resultsTable.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
@Override
public void columnAdded(TableColumnModelEvent e) {
// do nothing
}
@Override
public void columnRemoved(TableColumnModelEvent e) {
// do nothing
}
@Override
public void columnMoved(TableColumnModelEvent e) {
// do nothing
}
@Override
public void columnMarginChanged(ChangeEvent e) {
updateRowHeights(); //When the user changes column width we may need to resize row height
}
@Override
public void columnSelectionChanged(ListSelectionEvent e) {
// do nothing
}
});
resultsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN);
}
/**
* Sets the row heights to the heights of the content in their Value column.
*/
private void updateRowHeights() {
int valueColIndex = -1;
for (int col = 0; col < resultsTable.getColumnCount(); col++) {
if (resultsTable.getColumnName(col).equals(COLUMN_HEADERS[1])) {
valueColIndex = col;
}
}
if (valueColIndex != -1) {
for (int row = 0; row < resultsTable.getRowCount(); row++) {
Component comp = resultsTable.prepareRenderer(
resultsTable.getCellRenderer(row, valueColIndex), row, valueColIndex);
final int rowHeight;
if (comp instanceof JTextArea) {
final JTextArea tc = (JTextArea) comp;
final View rootView = tc.getUI().getRootView(tc);
java.awt.Insets i = tc.getInsets();
rootView.setSize(resultsTable.getColumnModel().getColumn(valueColIndex)
.getWidth() - (i.left + i.right +CELL_RIGHT_MARGIN), //current width minus borders
Integer.MAX_VALUE);
rowHeight = (int) rootView.getPreferredSpan(View.Y_AXIS);
} else {
rowHeight = comp.getPreferredSize().height;
}
if (rowHeight > 0) {
resultsTable.setRowHeight(row, rowHeight + CELL_BOTTOM_MARGIN);
}
}
}
}
/**
* Update the column widths so that the Value column has most of the space.
*/
private void updateColumnSizes() {
Enumeration<TableColumn> columns = resultsTable.getColumnModel().getColumns();
while (columns.hasMoreElements()) {
TableColumn col = columns.nextElement();
if (col.getHeaderValue().equals(COLUMN_HEADERS[0])) {
col.setPreferredWidth(COLUMN_WIDTHS[0]);
} else if (col.getHeaderValue().equals(COLUMN_HEADERS[1])) {
col.setPreferredWidth(COLUMN_WIDTHS[1]);
} else if (col.getHeaderValue().equals(COLUMN_HEADERS[2])) {
col.setPreferredWidth(COLUMN_WIDTHS[2]);
}
}
}
/**
* 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() {
rightClickMenu = new javax.swing.JPopupMenu();
copyMenuItem = new javax.swing.JMenuItem();
selectAllMenuItem = new javax.swing.JMenuItem();
resultsTableScrollPane = new javax.swing.JScrollPane();
copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultArtifactContentViewer.class, "DefaultArtifactContentViewer.copyMenuItem.text")); // NOI18N
rightClickMenu.add(copyMenuItem);
selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DefaultArtifactContentViewer.class, "DefaultArtifactContentViewer.selectAllMenuItem.text")); // NOI18N
rightClickMenu.add(selectAllMenuItem);
setPreferredSize(new java.awt.Dimension(100, 58));
resultsTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
resultsTableScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
resultsTableScrollPane.setPreferredSize(new java.awt.Dimension(620, 34));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(resultsTableScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 58, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JMenuItem copyMenuItem;
private javax.swing.JScrollPane resultsTableScrollPane;
private javax.swing.JPopupMenu rightClickMenu;
private javax.swing.JMenuItem selectAllMenuItem;
// End of variables declaration//GEN-END:variables
private ETable resultsTable;
private void customizeComponents() {
resultsTable.setComponentPopupMenu(rightClickMenu);
ActionListener actList = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JMenuItem jmi = (JMenuItem) e.getSource();
if (jmi.equals(copyMenuItem)) {
StringBuilder selectedText = new StringBuilder(512);
for (int row : resultsTable.getSelectedRows()) {
for (int col : resultsTable.getSelectedColumns()) {
selectedText.append((String) resultsTable.getValueAt(row, col));
selectedText.append('\t');
}
//if its the last row selected don't add a new line
if (row != resultsTable.getSelectedRows()[resultsTable.getSelectedRows().length - 1]) {
selectedText.append(System.lineSeparator());
}
}
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(selectedText.toString()), null);
} else if (jmi.equals(selectAllMenuItem)) {
resultsTable.selectAll();
}
}
};
copyMenuItem.addActionListener(actList);
selectAllMenuItem.addActionListener(actList);
}
/**
* Resets the components to an empty view state.
*/
private void resetComponents() {
((DefaultTableModel) resultsTable.getModel()).setRowCount(0);
}
@Override
public Component getComponent() {
return this;
}
@Override
public void setArtifact(BlackboardArtifact artifact) {
try {
ResultsTableArtifact resultsTableArtifact = new ResultsTableArtifact(artifact, artifact.getParent());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
updateView(resultsTableArtifact);
}
});
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting parent content for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
}
}
@Override
public boolean isSupported(BlackboardArtifact artifact) {
// This viewer supports all artifacts.
return true;
}
/**
* This class is a container to hold the data necessary for the artifact
* being viewed.
*/
private class ResultsTableArtifact {
private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
private String[][] rowData = null;
private final String artifactDisplayName;
private final Content content;
ResultsTableArtifact(BlackboardArtifact artifact, Content content) {
artifactDisplayName = artifact.getDisplayName();
this.content = content;
addRows(artifact);
}
ResultsTableArtifact(String errorMsg) {
artifactDisplayName = errorMsg;
rowData = new String[1][3];
rowData[0] = new String[]{"", errorMsg, ""};
content = null;
}
private String[][] getRows() {
return rowData;
}
private void addRows(BlackboardArtifact artifact) {
List<String[]> rowsToAdd = new ArrayList<>();
try {
/*
* Add rows for each attribute.
*/
for (BlackboardAttribute attr : artifact.getAttributes()) {
/*
* Attribute value column.
*/
String value;
switch (attr.getAttributeType().getValueType()) {
// Use Autopsy date formatting settings, not TSK defaults
case DATETIME:
value = epochTimeToString(attr.getValueLong());
break;
case JSON:
// Get the attribute's JSON value and convert to indented multiline display string
String jsonVal = attr.getValueString();
JsonParser parser = new JsonParser();
JsonObject json = parser.parse(jsonVal).getAsJsonObject();
value = toJsonDisplayString(json, "");
break;
case STRING:
case INTEGER:
case LONG:
case DOUBLE:
case BYTE:
default:
value = attr.getDisplayString();
break;
}
/*
* Attribute sources column.
*/
String sources = StringUtils.join(attr.getSources(), ", ");
rowsToAdd.add(new String[]{attr.getAttributeType().getDisplayName(), value, sources});
}
/*
* Add a row for the source content path.
*/
String path = "";
try {
if (null != content) {
path = content.getUniquePath();
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting source content path for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
path = Bundle.DataContentViewerArtifact_failedToGetSourcePath_message();
}
rowsToAdd.add(new String[]{"Source File Path", path, ""});
/*
* Add a row for the artifact id.
*/
rowsToAdd.add(new String[]{"Artifact ID", Long.toString(artifact.getArtifactID()), ""});
} catch (TskCoreException ex) {
rowsToAdd.add(new String[]{"", Bundle.DataContentViewerArtifact_failedToGetAttributes_message(), ""});
}
rowData = rowsToAdd.toArray(new String[0][0]);
}
/**
* @return the artifactDisplayName
*/
String getArtifactDisplayName() {
return artifactDisplayName;
}
private static final String INDENT_RIGHT = " ";
private static final String NEW_LINE = "\n";
/**
* Recursively converts a JSON element into an indented multi-line
* display string.
*
* @param element JSON element to convert
* @param startIndent Starting indentation for the element.
*
* @return A multi-line display string.
*/
private String toJsonDisplayString(JsonElement element, String startIndent) {
StringBuilder sb = new StringBuilder("");
JsonObject obj = element.getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb );
}
String returnString = sb.toString();
if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
returnString = returnString.substring(NEW_LINE.length());
}
return returnString;
}
/**
* Converts the given JSON element into string and appends to the given string builder.
*
* @param jsonKey
* @param jsonElement
* @param startIndent Starting indentation for the element.
* @param sb String builder to append to.
*/
private void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb) {
if (jsonElement.isJsonArray()) {
JsonArray jsonArray = jsonElement.getAsJsonArray();
if (jsonArray.size() > 0) {
int count = 1;
sb.append(NEW_LINE).append(String.format("%s%s", startIndent, jsonKey));
for (JsonElement arrayMember : jsonArray) {
sb.append(NEW_LINE).append(String.format("%s%d", startIndent.concat(INDENT_RIGHT), count));
sb.append(toJsonDisplayString(arrayMember, startIndent.concat(INDENT_RIGHT).concat(INDENT_RIGHT)));
count++;
}
}
} else if (jsonElement.isJsonObject()) {
sb.append(NEW_LINE).append(String.format("%s%s %s", startIndent, jsonKey, toJsonDisplayString(jsonElement.getAsJsonObject(), startIndent + INDENT_RIGHT)));
} else if (jsonElement.isJsonPrimitive()) {
String attributeName = jsonKey;
String attributeValue;
if (attributeName.toUpperCase().contains("DATETIME")) {
attributeValue = epochTimeToString(Long.parseLong(jsonElement.getAsString()));
} else {
attributeValue = jsonElement.getAsString();
}
sb.append(NEW_LINE).append(String.format("%s%s = %s", startIndent, attributeName, attributeValue));
} else if (jsonElement.isJsonNull()) {
sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey));
}
}
/**
* Converts epoch time to readable string.
*
* @param epochTime epoch time value to be converted to string.
* @return String with human readable time.
*/
private String epochTimeToString(long epochTime) {
String dateTimeString = "0000-00-00 00:00:00";
if (null != content && 0 != epochTime) {
dateFormatter.setTimeZone(ContentUtils.getTimeZone(content));
dateTimeString = dateFormatter.format(new java.util.Date(epochTime * 1000));
}
return dateTimeString;
}
}
/**
* Updates the table view with the given artifact data.
*
* It should be called on EDT.
*
* @param resultsTableArtifact Artifact data to display in the view.
*/
private void updateView(ResultsTableArtifact resultsTableArtifact) {
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
tModel.setDataVector(resultsTableArtifact.getRows(), COLUMN_HEADERS);
updateColumnSizes();
updateRowHeights();
resultsTable.clearSelection();
this.setCursor(null);
}
/**
* TableCellRenderer for displaying multiline text.
*/
private class MultiLineTableCellRenderer implements javax.swing.table.TableCellRenderer {
@Override
public Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
javax.swing.JTextArea jtex = new javax.swing.JTextArea();
if (value instanceof String) {
jtex.setText((String) value);
jtex.setLineWrap(true);
jtex.setWrapStyleWord(false);
}
//cell backgroud color when selected
if (isSelected) {
jtex.setBackground(javax.swing.UIManager.getColor("Table.selectionBackground"));
} else {
jtex.setBackground(javax.swing.UIManager.getColor("Table.background"));
}
return jtex;
}
}
}

View File

@ -30,8 +30,6 @@ DataContentViewerArtifact.pageLabel2.text=Result
DataContentViewerArtifact.nextPageButton.text= DataContentViewerArtifact.nextPageButton.text=
DataContentViewerArtifact.currentPageLabel.text=1 DataContentViewerArtifact.currentPageLabel.text=1
DataContentViewerArtifact.ofLabel.text=of DataContentViewerArtifact.ofLabel.text=of
DataContentViewerArtifact.copyMenuItem.text=Copy
DataContentViewerArtifact.selectAllMenuItem.text=Select All
DataContentViewerArtifact.pageLabel.text=Result: DataContentViewerArtifact.pageLabel.text=Result:
AdvancedConfigurationDialog.applyButton.text=OK AdvancedConfigurationDialog.applyButton.text=OK
DataContentViewerHex.goToPageTextField.text= DataContentViewerHex.goToPageTextField.text=

View File

@ -82,8 +82,6 @@ DataContentViewerArtifact.pageLabel2.text=\u7d50\u679c
DataContentViewerArtifact.nextPageButton.text= DataContentViewerArtifact.nextPageButton.text=
DataContentViewerArtifact.currentPageLabel.text=1 DataContentViewerArtifact.currentPageLabel.text=1
DataContentViewerArtifact.ofLabel.text=/ DataContentViewerArtifact.ofLabel.text=/
DataContentViewerArtifact.copyMenuItem.text=\u30b3\u30d4\u30fc
DataContentViewerArtifact.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e
DataContentViewerArtifact.pageLabel.text=\u7d50\u679c: DataContentViewerArtifact.pageLabel.text=\u7d50\u679c:
AdvancedConfigurationDialog.applyButton.text=OK AdvancedConfigurationDialog.applyButton.text=OK
DataContentViewerHex.goToPageTextField.text= DataContentViewerHex.goToPageTextField.text=

View File

@ -1,30 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.9" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Container class="javax.swing.JPopupMenu" name="rightClickMenu">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
<Property name="useNullLayout" type="boolean" value="true"/>
</Layout>
<SubComponents>
<MenuItem class="javax.swing.JMenuItem" name="copyMenuItem">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerArtifact.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</MenuItem>
<MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerArtifact.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</MenuItem>
</SubComponents>
</Container>
</NonVisualComponents>
<Properties> <Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 58]"/> <Dimension value="[100, 58]"/>
@ -45,16 +21,16 @@
<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" max="32767" attributes="0"/> <Component id="jScrollPane1" pref="561" max="32767" attributes="0"/>
<Component id="resultsTableScrollPane" max="32767" attributes="0"/> <Component id="artifactContentPanel" alignment="0" max="32767" attributes="0"/>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="jScrollPane1" min="-2" pref="24" max="-2" attributes="0"/> <Component id="jScrollPane1" min="-2" pref="24" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="resultsTableScrollPane" max="32767" attributes="0"/> <Component id="artifactContentPanel" pref="397" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -248,19 +224,34 @@
</Container> </Container>
</SubComponents> </SubComponents>
</Container> </Container>
<Container class="javax.swing.JScrollPane" name="resultsTableScrollPane"> <Container class="javax.swing.JPanel" name="artifactContentPanel">
<Properties> <LayoutCode>
<Property name="horizontalScrollBarPolicy" type="int" value="32"/> <CodeStatement>
<Property name="verticalScrollBarPolicy" type="int" value="22"/> <CodeExpression id="1_artifactContentPanel">
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <CodeVariable name="artifactContentPanel" type="8194" declaredType="javax.swing.JPanel"/>
<Dimension value="[620, 34]"/> <ExpressionOrigin>
</Property> <ExpressionProvider type="ComponentRef">
</Properties> <ComponentRef name="artifactContentPanel"/>
<AuxValues> </ExpressionProvider>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/> </ExpressionOrigin>
</AuxValues> </CodeExpression>
<StatementProvider type="CodeMethod">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <CodeMethod name="setLayout" class="java.awt.Container" parameterTypes="java.awt.LayoutManager"/>
</StatementProvider>
<Parameters>
<CodeExpression id="2">
<ExpressionOrigin>
<ExpressionProvider type="CodeConstructor">
<CodeConstructor class="javax.swing.OverlayLayout" parameterTypes="java.awt.Container"/>
</ExpressionProvider>
<Parameters>
<CodeExpression id="1_artifactContentPanel"/>
</Parameters>
</ExpressionOrigin>
</CodeExpression>
</Parameters>
</CodeStatement>
</LayoutCode>
</Container> </Container>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -20,178 +20,68 @@ package org.sleuthkit.autopsy.corecomponents;
import java.awt.Component; import java.awt.Component;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.datatransfer.StringSelection;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JMenuItem;
import javax.swing.JTextArea;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.event.TableColumnModelListener;
import javax.swing.text.View;
import org.apache.commons.lang.StringUtils;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.TskException;
import org.netbeans.swing.etable.ETable; import java.util.Arrays;
import com.google.gson.JsonElement; import java.util.Collections;
import com.google.gson.JsonObject; import org.sleuthkit.autopsy.contentviewers.ArtifactContentViewer;
import com.google.gson.JsonParser; import org.sleuthkit.autopsy.contentviewers.ContactArtifactViewer;
import com.google.gson.JsonArray; import org.sleuthkit.autopsy.contentviewers.DefaultArtifactContentViewer;
import java.util.Map;
/** /**
* Instances of this class display the BlackboardArtifacts associated with the * Instances of this class display the BlackboardArtifacts associated with the
* Content represented by a Node. Each BlackboardArtifact is rendered displayed * Content represented by a Node.
* in a JTable representation of its BlackboardAttributes. *
* It goes through a list of known ArtifactContentViewer to find a viewer that
* supports a given artifact and then hands it the artifact to display.
*/ */
@ServiceProvider(service = DataContentViewer.class, position = 7) @ServiceProvider(service = DataContentViewer.class, position = 7)
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer { public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer {
private static final long serialVersionUID = 1L;
@NbBundle.Messages({ @NbBundle.Messages({
"DataContentViewerArtifact.attrsTableHeader.type=Type",
"DataContentViewerArtifact.attrsTableHeader.value=Value",
"DataContentViewerArtifact.attrsTableHeader.sources=Source(s)",
"DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database", "DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database",
"DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database" "DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database"
}) })
private final static Logger logger = Logger.getLogger(DataContentViewerArtifact.class.getName()); private final static Logger logger = Logger.getLogger(DataContentViewerArtifact.class.getName());
private final static String WAIT_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.waitText"); private final static String WAIT_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.waitText");
private final static String ERROR_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.errorText"); private final static String ERROR_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.errorText");
private Node currentNode; // @@@ Remove this when the redundant setNode() calls problem is fixed. private Node currentNode; // @@@ Remove this when the redundant setNode() calls problem is fixed.
private int currentPage = 1; private int currentPage = 1;
private final Object lock = new Object(); private final Object lock = new Object();
private List<ResultsTableArtifact> artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents() private List<BlackboardArtifact> artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents()
SwingWorker<ViewUpdate, Void> currentTask; // Accessed by multiple threads, use startNewTask() private SwingWorker<ViewUpdate, Void> currentTask; // Accessed by multiple threads, use startNewTask()
private static final String[] COLUMN_HEADERS = {
Bundle.DataContentViewerArtifact_attrsTableHeader_type(),
Bundle.DataContentViewerArtifact_attrsTableHeader_value(),
Bundle.DataContentViewerArtifact_attrsTableHeader_sources()};
private static final int[] COLUMN_WIDTHS = {100, 800, 100};
private static final int CELL_BOTTOM_MARGIN = 5;
private static final int CELL_RIGHT_MARGIN = 1;
private final Collection<ArtifactContentViewer> KNOWN_ARTIFACT_VIEWERS =
Arrays.asList(
new ContactArtifactViewer()
);
public DataContentViewerArtifact() { public DataContentViewerArtifact() {
initResultsTable();
initComponents(); initComponents();
resultsTableScrollPane.setViewportView(resultsTable);
customizeComponents();
resetComponents(); resetComponents();
resultsTable.setDefaultRenderer(Object.class, new MultiLineTableCellRenderer());
}
private void initResultsTable() {
resultsTable = new ETable();
resultsTable.setModel(new javax.swing.table.DefaultTableModel() {
private static final long serialVersionUID = 1L;
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
});
resultsTable.setCellSelectionEnabled(true);
resultsTable.getTableHeader().setReorderingAllowed(false);
resultsTable.setColumnHidingAllowed(false);
resultsTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
resultsTable.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
@Override
public void columnAdded(TableColumnModelEvent e) {
}
@Override
public void columnRemoved(TableColumnModelEvent e) {
}
@Override
public void columnMoved(TableColumnModelEvent e) {
}
@Override
public void columnMarginChanged(ChangeEvent e) {
updateRowHeights(); //When the user changes column width we may need to resize row height
}
@Override
public void columnSelectionChanged(ListSelectionEvent e) {
}
});
resultsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN);
}
/**
* Sets the row heights to the heights of the content in their Value column.
*/
private void updateRowHeights() {
int valueColIndex = -1;
for (int col = 0; col < resultsTable.getColumnCount(); col++) {
if (resultsTable.getColumnName(col).equals(COLUMN_HEADERS[1])) {
valueColIndex = col;
}
}
if (valueColIndex != -1) {
for (int row = 0; row < resultsTable.getRowCount(); row++) {
Component comp = resultsTable.prepareRenderer(
resultsTable.getCellRenderer(row, valueColIndex), row, valueColIndex);
final int rowHeight;
if (comp instanceof JTextArea) {
final JTextArea tc = (JTextArea) comp;
final View rootView = tc.getUI().getRootView(tc);
java.awt.Insets i = tc.getInsets();
rootView.setSize(resultsTable.getColumnModel().getColumn(valueColIndex)
.getWidth() - (i.left + i.right +CELL_RIGHT_MARGIN), //current width minus borders
Integer.MAX_VALUE);
rowHeight = (int) rootView.getPreferredSpan(View.Y_AXIS);
} else {
rowHeight = comp.getPreferredSize().height;
}
if (rowHeight > 0) {
resultsTable.setRowHeight(row, rowHeight + CELL_BOTTOM_MARGIN);
}
}
}
}
/**
* Update the column widths so that the Value column has most of the space.
*/
private void updateColumnSizes() {
Enumeration<TableColumn> columns = resultsTable.getColumnModel().getColumns();
while (columns.hasMoreElements()) {
TableColumn col = columns.nextElement();
if (col.getHeaderValue().equals(COLUMN_HEADERS[0])) {
col.setPreferredWidth(COLUMN_WIDTHS[0]);
} else if (col.getHeaderValue().equals(COLUMN_HEADERS[1])) {
col.setPreferredWidth(COLUMN_WIDTHS[1]);
} else if (col.getHeaderValue().equals(COLUMN_HEADERS[2])) {
col.setPreferredWidth(COLUMN_WIDTHS[2]);
}
}
} }
/** /**
@ -204,9 +94,6 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
private void initComponents() { private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints; java.awt.GridBagConstraints gridBagConstraints;
rightClickMenu = new javax.swing.JPopupMenu();
copyMenuItem = new javax.swing.JMenuItem();
selectAllMenuItem = new javax.swing.JMenuItem();
jScrollPane1 = new javax.swing.JScrollPane(); jScrollPane1 = new javax.swing.JScrollPane();
jPanel1 = new javax.swing.JPanel(); jPanel1 = new javax.swing.JPanel();
totalPageLabel = new javax.swing.JLabel(); totalPageLabel = new javax.swing.JLabel();
@ -218,13 +105,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
prevPageButton = new javax.swing.JButton(); prevPageButton = new javax.swing.JButton();
artifactLabel = new javax.swing.JLabel(); artifactLabel = new javax.swing.JLabel();
filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0)); filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
resultsTableScrollPane = new javax.swing.JScrollPane(); artifactContentPanel = new javax.swing.JPanel();
copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.copyMenuItem.text")); // NOI18N
rightClickMenu.add(copyMenuItem);
selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.selectAllMenuItem.text")); // NOI18N
rightClickMenu.add(selectAllMenuItem);
setPreferredSize(new java.awt.Dimension(100, 58)); setPreferredSize(new java.awt.Dimension(100, 58));
@ -341,43 +222,41 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
jScrollPane1.setViewportView(jPanel1); jScrollPane1.setViewportView(jPanel1);
resultsTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); artifactContentPanel.setLayout(new javax.swing.OverlayLayout(artifactContentPanel));
resultsTableScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
resultsTableScrollPane.setPreferredSize(new java.awt.Dimension(620, 34));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
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, 561, Short.MAX_VALUE)
.addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(artifactContentPanel, 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)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 397, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
currentPage = currentPage + 1; currentPage += 1;
currentPageLabel.setText(Integer.toString(currentPage)); currentPageLabel.setText(Integer.toString(currentPage));
artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName()); artifactLabel.setText(artifactTableContents.get(currentPage - 1).getDisplayName());
startNewTask(new SelectedArtifactChangedTask(currentPage)); startNewTask(new SelectedArtifactChangedTask(currentPage));
}//GEN-LAST:event_nextPageButtonActionPerformed }//GEN-LAST:event_nextPageButtonActionPerformed
private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
currentPage = currentPage - 1; currentPage -= 1;
currentPageLabel.setText(Integer.toString(currentPage)); currentPageLabel.setText(Integer.toString(currentPage));
artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName()); artifactLabel.setText(artifactTableContents.get(currentPage - 1).getDisplayName());
startNewTask(new SelectedArtifactChangedTask(currentPage)); startNewTask(new SelectedArtifactChangedTask(currentPage));
}//GEN-LAST:event_prevPageButtonActionPerformed }//GEN-LAST:event_prevPageButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel artifactContentPanel;
private javax.swing.JLabel artifactLabel; private javax.swing.JLabel artifactLabel;
private javax.swing.JMenuItem copyMenuItem;
private javax.swing.JLabel currentPageLabel; private javax.swing.JLabel currentPageLabel;
private javax.swing.Box.Filler filler1; private javax.swing.Box.Filler filler1;
private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel1;
@ -387,41 +266,9 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
private javax.swing.JLabel pageLabel; private javax.swing.JLabel pageLabel;
private javax.swing.JLabel pageLabel2; private javax.swing.JLabel pageLabel2;
private javax.swing.JButton prevPageButton; private javax.swing.JButton prevPageButton;
private javax.swing.JScrollPane resultsTableScrollPane;
private javax.swing.JPopupMenu rightClickMenu;
private javax.swing.JMenuItem selectAllMenuItem;
private javax.swing.JLabel totalPageLabel; private javax.swing.JLabel totalPageLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
private ETable resultsTable;
private void customizeComponents() {
resultsTable.setComponentPopupMenu(rightClickMenu);
ActionListener actList = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JMenuItem jmi = (JMenuItem) e.getSource();
if (jmi.equals(copyMenuItem)) {
StringBuilder selectedText = new StringBuilder(512);
for (int row : resultsTable.getSelectedRows()) {
for (int col : resultsTable.getSelectedColumns()) {
selectedText.append((String) resultsTable.getValueAt(row, col));
selectedText.append("\t");
}
//if its the last row selected don't add a new line
if (row != resultsTable.getSelectedRows()[resultsTable.getSelectedRows().length - 1]) {
selectedText.append(System.lineSeparator());
}
}
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(selectedText.toString()), null);
} else if (jmi.equals(selectAllMenuItem)) {
resultsTable.selectAll();
}
}
};
copyMenuItem.addActionListener(actList);
selectAllMenuItem.addActionListener(actList);
}
/** /**
* Resets the components to an empty view state. * Resets the components to an empty view state.
@ -431,10 +278,12 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
currentPageLabel.setText(""); currentPageLabel.setText("");
artifactLabel.setText(""); artifactLabel.setText("");
totalPageLabel.setText(""); totalPageLabel.setText("");
((DefaultTableModel) resultsTable.getModel()).setRowCount(0);
prevPageButton.setEnabled(false); prevPageButton.setEnabled(false);
nextPageButton.setEnabled(false); nextPageButton.setEnabled(false);
currentNode = null; currentNode = null;
artifactContentPanel.removeAll();
} }
@Override @Override
@ -521,183 +370,13 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
} }
} }
/** private ArtifactContentViewer getSupportingViewer(BlackboardArtifact artifact) {
* This class is a container to hold the data necessary for each of the
* result pages associated with file or artifact beivng viewed.
*/
private class ResultsTableArtifact {
private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private String[][] rowData = null;
private final String artifactDisplayName;
private final Content content;
ResultsTableArtifact(BlackboardArtifact artifact, Content content) {
artifactDisplayName = artifact.getDisplayName();
this.content = content;
addRows(artifact);
}
ResultsTableArtifact(String errorMsg) {
artifactDisplayName = errorMsg;
rowData = new String[1][3];
rowData[0] = new String[]{"", errorMsg, ""};
content = null;
}
private String[][] getRows() {
return rowData;
}
private void addRows(BlackboardArtifact artifact) {
List<String[]> rowsToAdd = new ArrayList<>();
try {
/*
* Add rows for each attribute.
*/
for (BlackboardAttribute attr : artifact.getAttributes()) {
/*
* Attribute value column.
*/
String value = "";
switch (attr.getAttributeType().getValueType()) {
case STRING:
case INTEGER:
case LONG:
case DOUBLE:
case BYTE:
default:
value = attr.getDisplayString();
break;
// Use Autopsy date formatting settings, not TSK defaults
case DATETIME:
value = epochTimeToString(attr.getValueLong());
break;
case JSON:
// Get the attribute's JSON value and convert to indented multiline display string
String jsonVal = attr.getValueString();
JsonParser parser = new JsonParser();
JsonObject json = parser.parse(jsonVal).getAsJsonObject();
value = toJsonDisplayString(json, "");
break;
}
/*
* Attribute sources column.
*/
String sources = StringUtils.join(attr.getSources(), ", ");
rowsToAdd.add(new String[]{attr.getAttributeType().getDisplayName(), value, sources});
}
/*
* Add a row for the source content path.
*/
String path = "";
try {
if (null != content) {
path = content.getUniquePath();
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting source content path for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
path = Bundle.DataContentViewerArtifact_failedToGetSourcePath_message();
}
rowsToAdd.add(new String[]{"Source File Path", path, ""});
/*
* Add a row for the artifact id.
*/
rowsToAdd.add(new String[]{"Artifact ID", Long.toString(artifact.getArtifactID()), ""});
} catch (TskCoreException ex) {
rowsToAdd.add(new String[]{"", Bundle.DataContentViewerArtifact_failedToGetAttributes_message(), ""});
}
rowData = rowsToAdd.toArray(new String[0][0]);
}
/**
* @return the artifactDisplayName
*/
String getArtifactDisplayName() {
return artifactDisplayName;
}
private static final String INDENT_RIGHT = " "; return this.KNOWN_ARTIFACT_VIEWERS.stream()
private static final String NEW_LINE = "\n"; .filter(knownViewer -> knownViewer.isSupported(artifact))
.findAny()
/** .orElse(new DefaultArtifactContentViewer());
* Recursively converts a JSON element into an indented multi-line
* display string.
*
* @param element JSON element to convert
* @param startIndent Starting indentation for the element.
*
* @return A multi-line display string.
*/
private String toJsonDisplayString(JsonElement element, String startIndent) {
StringBuilder sb = new StringBuilder("");
JsonObject obj = element.getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
appendJsonElementToString(entry.getKey(), entry.getValue(), startIndent, sb );
}
String returnString = sb.toString();
if (startIndent.length() == 0 && returnString.startsWith(NEW_LINE)) {
returnString = returnString.substring(NEW_LINE.length());
}
return returnString;
}
/**
* Converts the given JSON element into string and appends to the given string builder.
*
* @param jsonKey
* @param jsonElement
* @param startIndent Starting indentation for the element.
* @param sb String builder to append to.
*/
private void appendJsonElementToString(String jsonKey, JsonElement jsonElement, String startIndent, StringBuilder sb) {
if (jsonElement.isJsonArray()) {
JsonArray jsonArray = jsonElement.getAsJsonArray();
if (jsonArray.size() > 0) {
int count = 1;
sb.append(NEW_LINE).append(String.format("%s%s", startIndent, jsonKey));
for (JsonElement arrayMember : jsonArray) {
sb.append(NEW_LINE).append(String.format("%s%d", startIndent.concat(INDENT_RIGHT), count));
sb.append(toJsonDisplayString(arrayMember, startIndent.concat(INDENT_RIGHT).concat(INDENT_RIGHT)));
count++;
}
}
} else if (jsonElement.isJsonObject()) {
sb.append(NEW_LINE).append(String.format("%s%s %s", startIndent, jsonKey, toJsonDisplayString(jsonElement.getAsJsonObject(), startIndent + INDENT_RIGHT)));
} else if (jsonElement.isJsonPrimitive()) {
String attributeName = jsonKey;
String attributeValue;
if (attributeName.toUpperCase().contains("DATETIME")) {
attributeValue = epochTimeToString(Long.parseLong(jsonElement.getAsString()));
} else {
attributeValue = jsonElement.getAsString();
}
sb.append(NEW_LINE).append(String.format("%s%s = %s", startIndent, attributeName, attributeValue));
} else if (jsonElement.isJsonNull()) {
sb.append(NEW_LINE).append(String.format("%s%s = null", startIndent, jsonKey));
}
}
/**
* Converts epoch time to readable string.
*
* @param epochTime epoch time value to be converted to string.
* @return String with human readable time.
*/
private String epochTimeToString(long epochTime) {
String dateTimeString = "0000-00-00 00:00:00";
if (null != content && 0 != epochTime) {
dateFormatter.setTimeZone(ContentUtils.getTimeZone(content));
dateTimeString = dateFormatter.format(new java.util.Date(epochTime * 1000));
}
return dateTimeString;
}
} }
/** /**
@ -708,18 +387,21 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
int numberOfPages; int numberOfPages;
int currentPage; int currentPage;
ResultsTableArtifact tableContents; BlackboardArtifact artifact;
String errorMsg;
ViewUpdate(int numberOfPages, int currentPage, ResultsTableArtifact contents) { ViewUpdate(int numberOfPages, int currentPage, BlackboardArtifact artifact) {
this.currentPage = currentPage; this.currentPage = currentPage;
this.numberOfPages = numberOfPages; this.numberOfPages = numberOfPages;
this.tableContents = contents; this.artifact = artifact;
this.errorMsg = null;
} }
ViewUpdate(int numberOfPages, int currentPage, String errorMsg) { ViewUpdate(int numberOfPages, int currentPage, String errorMsg) {
this.currentPage = currentPage; this.currentPage = currentPage;
this.numberOfPages = numberOfPages; this.numberOfPages = numberOfPages;
this.tableContents = new ResultsTableArtifact(errorMsg); this.errorMsg = errorMsg;
this.artifact = null;
} }
} }
@ -738,14 +420,26 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
currentPage = viewUpdate.currentPage; currentPage = viewUpdate.currentPage;
totalPageLabel.setText(Integer.toString(viewUpdate.numberOfPages)); totalPageLabel.setText(Integer.toString(viewUpdate.numberOfPages));
currentPageLabel.setText(Integer.toString(currentPage)); currentPageLabel.setText(Integer.toString(currentPage));
artifactLabel.setText(viewUpdate.tableContents.getArtifactDisplayName());
DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
tModel.setDataVector(viewUpdate.tableContents.getRows(), COLUMN_HEADERS); artifactContentPanel.removeAll();
updateColumnSizes();
updateRowHeights(); if (viewUpdate.artifact != null) {
resultsTable.clearSelection(); artifactLabel.setText(viewUpdate.artifact.getDisplayName());
BlackboardArtifact artifact = viewUpdate.artifact;
ArtifactContentViewer viewer = this.getSupportingViewer(artifact);
viewer.setArtifact(artifact);
artifactContentPanel.add(viewer.getComponent());
} else {
artifactLabel.setText(viewUpdate.errorMsg);
}
artifactContentPanel.revalidate();
this.setCursor(null); this.setCursor(null);
this.revalidate();
} }
/** /**
@ -755,13 +449,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
* @param task A new SwingWorker object to execute as a background thread. * @param task A new SwingWorker object to execute as a background thread.
*/ */
private synchronized void startNewTask(SwingWorker<ViewUpdate, Void> task) { private synchronized void startNewTask(SwingWorker<ViewUpdate, Void> task) {
String[][] waitRow = new String[1][3];
waitRow[0] = new String[]{"", WAIT_TEXT, ""};
DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
tModel.setDataVector(waitRow, COLUMN_HEADERS);
updateColumnSizes();
updateRowHeights();
resultsTable.clearSelection();
// The output of the previous task is no longer relevant. // The output of the previous task is no longer relevant.
if (currentTask != null) { if (currentTask != null) {
// This call sets a cancellation flag. It does not terminate the background thread running the task. // This call sets a cancellation flag. It does not terminate the background thread running the task.
@ -775,12 +463,12 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
} }
/** /**
* Populate the cache of artifact represented as ResultsTableArtifacts. * Populate the cache of artifacts represented as ResultsTableArtifacts.
* *
* @param artifactList A list of ResultsTableArtifact representations of * @param artifactList A list of ResultsTableArtifact representations of
* artifacts. * artifacts.
*/ */
private void setArtifactContents(List<ResultsTableArtifact> artifactList) { private void setArtifactContents(List<BlackboardArtifact> artifactList) {
synchronized (lock) { synchronized (lock) {
this.artifactTableContents = artifactList; this.artifactTableContents = artifactList;
} }
@ -789,11 +477,11 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
/** /**
* Retrieve the cache of artifact represented as ResultsTableArtifacts. * Retrieve the cache of artifact represented as ResultsTableArtifacts.
* *
* @return A list of ResultsTableArtifact representations of artifacts. * @return A list of artifacts.
*/ */
private List<ResultsTableArtifact> getArtifactContents() { private List<BlackboardArtifact> getArtifactContents() {
synchronized (lock) { synchronized (lock) {
return artifactTableContents; return Collections.unmodifiableList(artifactTableContents);
} }
} }
@ -843,9 +531,9 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
} }
// Build the new artifact contents cache. // Build the new artifact contents cache.
ArrayList<ResultsTableArtifact> artifactContents = new ArrayList<>(); ArrayList<BlackboardArtifact> artifactContents = new ArrayList<>();
for (BlackboardArtifact artifact : artifacts) { for (BlackboardArtifact artifact : artifacts) {
artifactContents.add(new ResultsTableArtifact(artifact, underlyingContent)); artifactContents.add(artifact);
} }
// If the node has an underlying blackboard artifact, show it. If not, // If the node has an underlying blackboard artifact, show it. If not,
@ -932,14 +620,14 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
protected ViewUpdate doInBackground() { protected ViewUpdate doInBackground() {
// Get the artifact content to display from the cache. Note that one must be subtracted from the // Get the artifact content to display from the cache. Note that one must be subtracted from the
// page index to get the corresponding artifact content index. // page index to get the corresponding artifact content index.
List<ResultsTableArtifact> artifactContents = getArtifactContents(); List<BlackboardArtifact> artifactContents = getArtifactContents();
ResultsTableArtifact artifactContent = artifactContents.get(pageIndex - 1);
// It may take a considerable amount of time to fetch the attributes of the selected artifact so check for cancellation. // It may take a considerable amount of time to fetch the attributes of the selected artifact so check for cancellation.
if (isCancelled()) { if (isCancelled()) {
return null; return null;
} }
BlackboardArtifact artifactContent = artifactContents.get(pageIndex - 1);
return new ViewUpdate(artifactContents.size(), pageIndex, artifactContent); return new ViewUpdate(artifactContents.size(), pageIndex, artifactContent);
} }
@ -957,27 +645,4 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
} }
} }
} }
/**
* TableCellRenderer for displaying multiline text.
*/
private class MultiLineTableCellRenderer implements javax.swing.table.TableCellRenderer {
@Override
public Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
javax.swing.JTextArea jtex = new javax.swing.JTextArea();
if (value instanceof String) {
jtex.setText((String) value);
jtex.setLineWrap(true);
jtex.setWrapStyleWord(false);
}
//cell backgroud color when selected
if (isSelected) {
jtex.setBackground(javax.swing.UIManager.getColor("Table.selectionBackground"));
} else {
jtex.setBackground(javax.swing.UIManager.getColor("Table.background"));
}
return jtex;
}
}
} }

View File

@ -30,6 +30,7 @@ import javax.swing.JOptionPane;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTextArea; import javax.swing.JTextArea;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
import org.sleuthkit.datamodel.HashEntry; import org.sleuthkit.datamodel.HashEntry;
@ -46,7 +47,14 @@ public class AddHashValuesToDatabaseProgressDialog extends javax.swing.JDialog {
private final HashDb hashDb; private final HashDb hashDb;
private final List<HashEntry> hashes; private final List<HashEntry> hashes;
private final List<String> invalidHashes; private final List<String> invalidHashes;
private final Pattern md5Pattern;
// Matches hash with optional comma separated comment.
private static final Pattern HASH_LINE_PATTERN = Pattern.compile("^([a-fA-F0-9]{32})(,(.*))?$");
// The regex group for the hash.
private static final int HASH_GROUP = 1;
// The regex group for the comment.
private static final int COMMENT_GROUP = 3;
private String errorTitle; private String errorTitle;
private String errorMessage; private String errorMessage;
private final String text; private final String text;
@ -64,7 +72,6 @@ public class AddHashValuesToDatabaseProgressDialog extends javax.swing.JDialog {
display(parent); display(parent);
this.hashes = new ArrayList<>(); this.hashes = new ArrayList<>();
this.invalidHashes = new ArrayList<>(); this.invalidHashes = new ArrayList<>();
this.md5Pattern = Pattern.compile("^([a-fA-F0-9]{32})"); // NON-NLS
this.parentRef = parent; this.parentRef = parent;
this.hashDb = hashDb; this.hashDb = hashDb;
this.text = text; this.text = text;
@ -161,17 +168,15 @@ public class AddHashValuesToDatabaseProgressDialog extends javax.swing.JDialog {
// These entries may be of <MD5> or <MD5, comment> format // These entries may be of <MD5> or <MD5, comment> format
for (String hashEntry : linesInTextArea) { for (String hashEntry : linesInTextArea) {
hashEntry = hashEntry.trim(); hashEntry = hashEntry.trim();
Matcher m = md5Pattern.matcher(hashEntry); Matcher m = HASH_LINE_PATTERN.matcher(hashEntry);
if (m.find()) { if (m.find()) {
// Is there any text left on this line? If so, treat it as a comment. String hash = m.group(HASH_GROUP);
String comment = hashEntry.substring(m.end()).trim();
if (comment.length() > 0) { // if there was a match and the match is not empty, assign to comment
comment = (comment.charAt(0) == ',') ? comment.substring(1) : comment; String comment = StringUtils.isNotBlank(m.group(COMMENT_GROUP)) ?
hashes.add(new HashEntry(null, m.group(0), null, null, comment)); m.group(COMMENT_GROUP).trim() : null;
} else {
// more information can be added to the HashEntry - sha-1, sha-512, comment hashes.add(new HashEntry(null, hash, null, null, comment));
hashes.add(new HashEntry(null, m.group(0), null, null, null));
}
} else { } else {
if (!hashEntry.isEmpty()) { if (!hashEntry.isEmpty()) {
invalidHashes.add(hashEntry); invalidHashes.add(hashEntry);

View File

@ -94,8 +94,6 @@ HashDbIngestModule.fileReadErrorMsg=Read Error: {0}
HashDbIngestModule.calcHashValueErr=Error encountered while calculating the hash value for {0} ({1}). 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.lookingUpKnownHashValueErr=Error encountered while looking up known hash value for {0}.
HashDbIngestModule.postToBB.fileName=File Name HashDbIngestModule.postToBB.fileName=File Name
HashDbIngestModule.postToBB.md5Hash=MD5 Hash HashDbIngestModule.postToBB.md5Hash=MD5 Hash
HashDbIngestModule.postToBB.hashsetName=Hash Set Name HashDbIngestModule.postToBB.hashsetName=Hash Set Name
@ -145,8 +143,6 @@ HashDbManager.hashDbFileExistsExceptionMsg=A file already exists at\n{0}
HashDbManager.hashDbAlreadyAddedExceptionMsg=The hash set at\n{0}\nhas already been created or imported. HashDbManager.hashDbAlreadyAddedExceptionMsg=The hash set at\n{0}\nhas already been created or imported.
HashDbManager.illegalHashDbFileNameExtensionMsg=The hash set file name must have a .{0} extension. HashDbManager.illegalHashDbFileNameExtensionMsg=The hash set file name must have a .{0} extension.
HashDbManager.moduleErr=Module Error HashDbManager.moduleErr=Module Error
HashDbManager.knownBad.text=Notable
HashDbManager.known.text=Known
HashDbManager.fileNameExtensionFilter.title=Hash Set File HashDbManager.fileNameExtensionFilter.title=Hash Set File
HashDbSearchAction.dlgMsg.title=File Search by MD5 Hash HashDbSearchAction.dlgMsg.title=File Search by MD5 Hash
HashDbSearchAction.getName.text=Hash Search HashDbSearchAction.getName.text=Hash Search
@ -162,8 +158,6 @@ AddContentToHashDbAction.multipleSelectionNameEmpty=Add Files to Hash Set (Empty
HashDbManager.ingestRunningExceptionMsg=Ingest is ongoing; this service will be unavailable until it finishes. HashDbManager.ingestRunningExceptionMsg=Ingest is ongoing; this service will be unavailable until it finishes.
HashDbManager.saveErrorExceptionMsg=Error saving hash configuration HashDbManager.saveErrorExceptionMsg=Error saving hash configuration
HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.text=Calculate MD5 even if no hash set is selected HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.text=Calculate MD5 even if no hash set is selected
HashLookupModuleSettingsPanel.knownHashDbsLabel.text=Select known hash sets to use:
HashLookupModuleSettingsPanel.knownBadHashDbsLabel.text=Select notable hash sets to use:
AddContentToHashDbAction.addFilesToHashSet.files=files AddContentToHashDbAction.addFilesToHashSet.files=files
AddContentToHashDbAction.addFilesToHashSet.file=file AddContentToHashDbAction.addFilesToHashSet.file=file
HashDbManager.errCreatingIndex.title=Error creating index HashDbManager.errCreatingIndex.title=Error creating index
@ -241,3 +235,7 @@ AddHashValuesToDatabaseDialog.okButton.text_2=OK
HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.text=Copy hash set into user configuration folder HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.text=Copy hash set into user configuration folder
HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.toolTipText=In Live Triage situations, this option ensures that path to the hash set will be valid HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.toolTipText=In Live Triage situations, this option ensures that path to the hash set will be valid
HashLookupSettingsPanel.indexPathLabel.text= HashLookupSettingsPanel.indexPathLabel.text=
HashLookupModuleSettingsPanel.hashDbsLabel.text=Select hash sets to use:
HashDbCreateDatabaseDialog.noChangeRadioButton.text=No Change
HashDbImportDatabaseDialog.noChangeRadioButton.toolTipText=
HashDbImportDatabaseDialog.noChangeRadioButton.text=No Change

View File

@ -9,6 +9,7 @@ HashDbImportDatabaseDialog.missingOrg=An organization must be selected
HashDbImportDatabaseDialog.missingVersion=A version must be entered HashDbImportDatabaseDialog.missingVersion=A version must be entered
HashDbImportDatabaseDialog.mustEnterHashSetNameMsg=A hash set name must be entered. HashDbImportDatabaseDialog.mustEnterHashSetNameMsg=A hash set name must be entered.
HashDbImportDatabaseDialog.populateOrgsError.message=Failure loading organizations. HashDbImportDatabaseDialog.populateOrgsError.message=Failure loading organizations.
HashDbIngestModule.complete.noChangesFound=No Change items found:
# {0} - File name # {0} - File name
HashDbIngestModule.dialogTitle.errorFindingArtifacts=Error Finding Artifacts: {0} HashDbIngestModule.dialogTitle.errorFindingArtifacts=Error Finding Artifacts: {0}
# {0} - File name # {0} - File name
@ -16,10 +17,21 @@ HashDbIngestModule.errorMessage.lookingForFileArtifacts=Error encountered while
HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search. HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search.
HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable file search will not be executed. HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable file search will not be executed.
HashDbIngestModule.knownFileSearchWillNotExecuteWarn=Known file search will not be executed. HashDbIngestModule.knownFileSearchWillNotExecuteWarn=Known file search will not be executed.
# {0} - fileName
HashDbIngestModule.lookingUpKnownBadHashValueErr=Error encountered while looking up notable hash value for {0}.
# {0} - fileName
HashDbIngestModule.lookingUpKnownHashValueErr=Error encountered while looking up known hash value for {0}.
# {0} - fileName
HashDbIngestModule.lookingUpNoChangeHashValueErr=Error encountered while looking up no change hash value for {0}.
HashDbIngestModule.noChangeFileSearchWillNotExecuteWarn='No Change' file search will not be executed.
HashDbIngestModule.noChangeHashDbSetMsg=No 'No Change' hash set.
HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set. HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.
HashDbIngestModule.noKnownHashDbSetMsg=No known hash set. HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.
HashDbManager.CentralRepoHashDb.orgError=Error loading organization HashDbManager.CentralRepoHashDb.orgError=Error loading organization
HashDbManager.centralRepoLoadError.message=Error loading central repository hash sets HashDbManager.centralRepoLoadError.message=Error loading central repository hash sets
HashDbManager.known.text=Known
HashDbManager.knownBad.text=Notable
HashDbManager.noChange.text=No Change
# {0} - hash set name # {0} - hash set name
HashDbManager.noDbPath.message=Couldn't get valid hash set path for: {0} HashDbManager.noDbPath.message=Couldn't get valid hash set path for: {0}
HashDbSearchAction.noOpenCase.errMsg=No open case available. HashDbSearchAction.noOpenCase.errMsg=No open case available.
@ -49,7 +61,10 @@ ImportCentralRepoDbProgressDialog.errorParsingFile.message=Error parsing hash se
ImportCentralRepoDbProgressDialog.linesProcessed.message=\ hashes processed ImportCentralRepoDbProgressDialog.linesProcessed.message=\ hashes processed
ImportCentralRepoDbProgressDialog.title.text=Central Repository Import Progress ImportCentralRepoDbProgressDialog.title.text=Central Repository Import Progress
OpenIDE-Module-Display-Category=Ingest Module OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Long-Description=Hash Set ingest module. \n\nThe ingest module analyzes files in the disk image and marks them as "known" (based on NSRL hashset lookup for "known" files) and "bad / interesting" (based on one or more hash sets supplied by the user).\n\nThe module also contains additional non-ingest tools that are integrated in the GUI, such as file lookup by hash and hash set configuration. OpenIDE-Module-Long-Description=\
Hash Set ingest module. \n\n\
The ingest module analyzes files in the disk image and marks them as "known" (based on NSRL hashset lookup for "known" files) and "bad / interesting" (based on one or more hash sets supplied by the user).\n\n\
The module also contains additional non-ingest tools that are integrated in the GUI, such as file lookup by hash and hash set configuration.
OpenIDE-Module-Name=HashDatabases OpenIDE-Module-Name=HashDatabases
OptionsCategory_Name_HashDatabase=Hash Sets OptionsCategory_Name_HashDatabase=Hash Sets
OptionsCategory_Keywords_HashDatabase=Hash Sets OptionsCategory_Keywords_HashDatabase=Hash Sets
@ -141,8 +156,6 @@ HashDbIngestModule.fileReadErrorMsg=Read Error: {0}
HashDbIngestModule.calcHashValueErr=Error encountered while calculating the hash value for {0} ({1}). 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.lookingUpKnownHashValueErr=Error encountered while looking up known hash value for {0}.
HashDbIngestModule.postToBB.fileName=File Name HashDbIngestModule.postToBB.fileName=File Name
HashDbIngestModule.postToBB.md5Hash=MD5 Hash HashDbIngestModule.postToBB.md5Hash=MD5 Hash
HashDbIngestModule.postToBB.hashsetName=Hash Set Name HashDbIngestModule.postToBB.hashsetName=Hash Set Name
@ -178,7 +191,10 @@ HashDbSearchThread.name.searching=Searching
HashDbSearchThread.noMoreFilesWithMD5Msg=No other files with the same MD5 hash were found. HashDbSearchThread.noMoreFilesWithMD5Msg=No other files with the same MD5 hash were found.
ModalNoButtons.indexingDbsTitle=Indexing hash sets ModalNoButtons.indexingDbsTitle=Indexing hash sets
ModalNoButtons.indexingDbTitle=Indexing hash set ModalNoButtons.indexingDbTitle=Indexing hash set
ModalNoButtons.exitHashDbIndexingMsg=You are about to exit out of indexing your hash sets. \nThe generated index will be left unusable. If you choose to continue,\nplease delete the corresponding -md5.idx file in the hash folder.\nExit indexing? ModalNoButtons.exitHashDbIndexingMsg=You are about to exit out of indexing your hash sets. \n\
The generated index will be left unusable. If you choose to continue,\n\
please delete the corresponding -md5.idx file in the hash folder.\n\
Exit indexing?
ModalNoButtons.dlgTitle.unfinishedIndexing=Unfinished Indexing ModalNoButtons.dlgTitle.unfinishedIndexing=Unfinished Indexing
ModalNoButtons.indexThis.currentlyIndexing1Db=Currently indexing 1 hash set ModalNoButtons.indexThis.currentlyIndexing1Db=Currently indexing 1 hash set
ModalNoButtons.indexThese.currentlyIndexing1OfNDbs=Currently indexing 1 of {0} ModalNoButtons.indexThese.currentlyIndexing1OfNDbs=Currently indexing 1 of {0}
@ -189,8 +205,6 @@ HashDbManager.hashDbFileExistsExceptionMsg=A file already exists at\n{0}
HashDbManager.hashDbAlreadyAddedExceptionMsg=The hash set at\n{0}\nhas already been created or imported. HashDbManager.hashDbAlreadyAddedExceptionMsg=The hash set at\n{0}\nhas already been created or imported.
HashDbManager.illegalHashDbFileNameExtensionMsg=The hash set file name must have a .{0} extension. HashDbManager.illegalHashDbFileNameExtensionMsg=The hash set file name must have a .{0} extension.
HashDbManager.moduleErr=Module Error HashDbManager.moduleErr=Module Error
HashDbManager.knownBad.text=Notable
HashDbManager.known.text=Known
HashDbManager.fileNameExtensionFilter.title=Hash Set File HashDbManager.fileNameExtensionFilter.title=Hash Set File
HashDbSearchAction.dlgMsg.title=File Search by MD5 Hash HashDbSearchAction.dlgMsg.title=File Search by MD5 Hash
HashDbSearchAction.getName.text=Hash Search HashDbSearchAction.getName.text=Hash Search
@ -205,13 +219,7 @@ AddContentToHashDbAction.singleSelectionNameEmpty=Add File to Hash Set (Empty Fi
AddContentToHashDbAction.multipleSelectionNameEmpty=Add Files to Hash Set (Empty File) AddContentToHashDbAction.multipleSelectionNameEmpty=Add Files to Hash Set (Empty File)
HashDbManager.ingestRunningExceptionMsg=Ingest is ongoing; this service will be unavailable until it finishes. HashDbManager.ingestRunningExceptionMsg=Ingest is ongoing; this service will be unavailable until it finishes.
HashDbManager.saveErrorExceptionMsg=Error saving hash configuration HashDbManager.saveErrorExceptionMsg=Error saving hash configuration
HashLookupSettingsPanel.jButton3.text=Import Hash Set
HashLookupSettingsPanel.jLabel6.text=Type:
HashLookupSettingsPanel.jLabel4.text=Location:
HashLookupSettingsPanel.jLabel2.text=Name:
HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.text=Calculate MD5 even if no hash set is selected HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.text=Calculate MD5 even if no hash set is selected
HashLookupModuleSettingsPanel.knownHashDbsLabel.text=Select known hash sets to use:
HashLookupModuleSettingsPanel.knownBadHashDbsLabel.text=Select notable hash sets to use:
AddContentToHashDbAction.addFilesToHashSet.files=files AddContentToHashDbAction.addFilesToHashSet.files=files
AddContentToHashDbAction.addFilesToHashSet.file=file AddContentToHashDbAction.addFilesToHashSet.file=file
HashDbManager.errCreatingIndex.title=Error creating index HashDbManager.errCreatingIndex.title=Error creating index
@ -289,3 +297,7 @@ AddHashValuesToDatabaseDialog.okButton.text_2=OK
HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.text=Copy hash set into user configuration folder HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.text=Copy hash set into user configuration folder
HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.toolTipText=In Live Triage situations, this option ensures that path to the hash set will be valid HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.toolTipText=In Live Triage situations, this option ensures that path to the hash set will be valid
HashLookupSettingsPanel.indexPathLabel.text= HashLookupSettingsPanel.indexPathLabel.text=
HashLookupModuleSettingsPanel.hashDbsLabel.text=Select hash sets to use:
HashDbCreateDatabaseDialog.noChangeRadioButton.text=No Change
HashDbImportDatabaseDialog.noChangeRadioButton.toolTipText=
HashDbImportDatabaseDialog.noChangeRadioButton.text=No Change

View File

@ -216,8 +216,6 @@ HashLookupSettingsPanel.jLabel6.text=\u30bf\u30a4\u30d7:
HashLookupSettingsPanel.jLabel4.text=\u5834\u6240: HashLookupSettingsPanel.jLabel4.text=\u5834\u6240:
HashLookupSettingsPanel.jLabel2.text=\u540d\u524d: HashLookupSettingsPanel.jLabel2.text=\u540d\u524d:
HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u3067\u3082MD5\u3092\u8a08\u7b97 HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u3067\u3082MD5\u3092\u8a08\u7b97
HashLookupModuleSettingsPanel.knownHashDbsLabel.text=\u4f7f\u7528\u3059\u308b\u65e2\u77e5\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u9078\u629e:
HashLookupModuleSettingsPanel.knownBadHashDbsLabel.text=\u4f7f\u7528\u3059\u308b\u9855\u8457\u306a\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u9078\u629e:
AddContentToHashDbAction.addFilesToHashSet.files=\u30d5\u30a1\u30a4\u30eb AddContentToHashDbAction.addFilesToHashSet.files=\u30d5\u30a1\u30a4\u30eb
AddContentToHashDbAction.addFilesToHashSet.file=\u30d5\u30a1\u30a4\u30eb AddContentToHashDbAction.addFilesToHashSet.file=\u30d5\u30a1\u30a4\u30eb
HashDbManager.errCreatingIndex.title=\u7d22\u5f15\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f HashDbManager.errCreatingIndex.title=\u7d22\u5f15\u306e\u4f5c\u6210\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
@ -295,3 +293,6 @@ AddHashValuesToDatabaseDialog.okButton.text_2=OK
HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u30e6\u30fc\u30b6\u30fc\u69cb\u6210\u30d5\u30a1\u30a4\u30eb\u306b\u30b3\u30d4\u30fc HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u30e6\u30fc\u30b6\u30fc\u69cb\u6210\u30d5\u30a1\u30a4\u30eb\u306b\u30b3\u30d4\u30fc
HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.toolTipText=\u30e9\u30a4\u30d6\u30c8\u30ea\u30a2\u30fc\u30b8\u306e\u72b6\u6cc1\u3067\u306f\u3001\u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u306b\u3088\u3063\u3066\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3078\u306e\u30d1\u30b9\u304c\u6709\u52b9\u3067\u3042\u308b\u3053\u3068\u304c\u4fdd\u8a3c\u3055\u308c\u307e\u3059\u3002 HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.toolTipText=\u30e9\u30a4\u30d6\u30c8\u30ea\u30a2\u30fc\u30b8\u306e\u72b6\u6cc1\u3067\u306f\u3001\u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u306b\u3088\u3063\u3066\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3078\u306e\u30d1\u30b9\u304c\u6709\u52b9\u3067\u3042\u308b\u3053\u3068\u304c\u4fdd\u8a3c\u3055\u308c\u307e\u3059\u3002
HashLookupSettingsPanel.indexPathLabel.text= HashLookupSettingsPanel.indexPathLabel.text=
HashLookupModuleSettingsPanel.hashDbsLabel.text=\u4f7f\u7528\u3059\u308b\u65e2\u77e5\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c8\u3092\u9078\u629e:
HashDbCreateDatabaseDialog.noChangeRadioButton.text=\u9855\u8457
HashDbImportDatabaseDialog.noChangeRadioButton.text=\u9855\u8457

View File

@ -78,6 +78,7 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="knownRadioButton" min="-2" max="-2" attributes="0"/> <Component id="knownRadioButton" min="-2" max="-2" attributes="0"/>
<Component id="knownBadRadioButton" min="-2" max="-2" attributes="0"/> <Component id="knownBadRadioButton" min="-2" max="-2" attributes="0"/>
<Component id="noChangeRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
@ -125,19 +126,21 @@
<Component id="knownRadioButton" min="-2" max="-2" attributes="0"/> <Component id="knownRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="knownBadRadioButton" min="-2" max="-2" attributes="0"/> <Component id="knownBadRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" 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" attributes="0"> <Group type="102" attributes="0">
<Component id="sendIngestMessagesCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
<Group type="102" alignment="0" attributes="0">
<Component id="noChangeRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="24" max="-2" attributes="0"/>
<Component id="sendIngestMessagesCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -313,5 +316,18 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="orgButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="orgButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="noChangeRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup1"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="HashDbCreateDatabaseDialog.noChangeRadioButton.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="noChangeRadioButtonActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -198,6 +198,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
lbOrg = new javax.swing.JLabel(); lbOrg = new javax.swing.JLabel();
orgComboBox = new javax.swing.JComboBox<>(); orgComboBox = new javax.swing.JComboBox<>();
orgButton = new javax.swing.JButton(); orgButton = new javax.swing.JButton();
noChangeRadioButton = new javax.swing.JRadioButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
@ -292,6 +293,14 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
} }
}); });
buttonGroup1.add(noChangeRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(noChangeRadioButton, org.openide.util.NbBundle.getMessage(HashDbCreateDatabaseDialog.class, "HashDbCreateDatabaseDialog.noChangeRadioButton.text")); // NOI18N
noChangeRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
noChangeRadioButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout); getContentPane().setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
@ -334,7 +343,8 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
.addGap(32, 32, 32) .addGap(32, 32, 32)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(knownRadioButton) .addComponent(knownRadioButton)
.addComponent(knownBadRadioButton))) .addComponent(knownBadRadioButton)
.addComponent(noChangeRadioButton)))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(12, 12, 12) .addGap(12, 12, 12)
.addComponent(jLabel2)) .addComponent(jLabel2))
@ -374,16 +384,18 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
.addComponent(knownRadioButton) .addComponent(knownRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(knownBadRadioButton) .addComponent(knownBadRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(sendIngestMessagesCheckbox)
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE) .addGap(0, 0, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cancelButton) .addComponent(cancelButton)
.addComponent(okButton)))) .addComponent(okButton)))
.addGroup(layout.createSequentialGroup()
.addComponent(noChangeRadioButton)
.addGap(24, 24, 24)
.addComponent(sendIngestMessagesCheckbox)
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap()) .addContainerGap())
); );
@ -391,13 +403,13 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void knownRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownRadioButtonActionPerformed private void knownRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownRadioButtonActionPerformed
sendIngestMessagesCheckbox.setSelected(false); sendIngestMessagesCheckbox.setSelected(KnownFilesType.KNOWN.isDefaultInboxMessages());
sendIngestMessagesCheckbox.setEnabled(false); sendIngestMessagesCheckbox.setEnabled(KnownFilesType.KNOWN.isInboxMessagesAllowed());
}//GEN-LAST:event_knownRadioButtonActionPerformed }//GEN-LAST:event_knownRadioButtonActionPerformed
private void knownBadRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownBadRadioButtonActionPerformed private void knownBadRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownBadRadioButtonActionPerformed
sendIngestMessagesCheckbox.setSelected(true); sendIngestMessagesCheckbox.setSelected(KnownFilesType.KNOWN_BAD.isDefaultInboxMessages());
sendIngestMessagesCheckbox.setEnabled(true); sendIngestMessagesCheckbox.setEnabled(KnownFilesType.KNOWN_BAD.isInboxMessagesAllowed());
}//GEN-LAST:event_knownBadRadioButtonActionPerformed }//GEN-LAST:event_knownBadRadioButtonActionPerformed
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
@ -476,15 +488,17 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
} }
KnownFilesType type; KnownFilesType type;
TskData.FileKnown fileKnown;
if (knownRadioButton.isSelected()) { if (knownRadioButton.isSelected()) {
type = KnownFilesType.KNOWN; type = KnownFilesType.KNOWN;
fileKnown = TskData.FileKnown.KNOWN; } else if (noChangeRadioButton.isSelected()) {
type = KnownFilesType.NO_CHANGE;
} else { } else {
type = KnownFilesType.KNOWN_BAD; type = KnownFilesType.KNOWN_BAD;
fileKnown = TskData.FileKnown.BAD;
} }
TskData.FileKnown fileKnown = type.getFileKnown();
String errorMessage = NbBundle String errorMessage = NbBundle
.getMessage(this.getClass(), "HashDbCreateDatabaseDialog.errMsg.hashDbCreationErr"); .getMessage(this.getClass(), "HashDbCreateDatabaseDialog.errMsg.hashDbCreationErr");
@ -586,6 +600,11 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
enableComponents(); enableComponents();
}//GEN-LAST:event_centralRepoRadioButtonActionPerformed }//GEN-LAST:event_centralRepoRadioButtonActionPerformed
private void noChangeRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_noChangeRadioButtonActionPerformed
sendIngestMessagesCheckbox.setSelected(KnownFilesType.NO_CHANGE.isDefaultInboxMessages());
sendIngestMessagesCheckbox.setEnabled(KnownFilesType.NO_CHANGE.isInboxMessagesAllowed());
}//GEN-LAST:event_noChangeRadioButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.ButtonGroup buttonGroup1; private javax.swing.ButtonGroup buttonGroup1;
private javax.swing.JButton cancelButton; private javax.swing.JButton cancelButton;
@ -600,6 +619,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
private javax.swing.JRadioButton knownBadRadioButton; private javax.swing.JRadioButton knownBadRadioButton;
private javax.swing.JRadioButton knownRadioButton; private javax.swing.JRadioButton knownRadioButton;
private javax.swing.JLabel lbOrg; private javax.swing.JLabel lbOrg;
private javax.swing.JRadioButton noChangeRadioButton;
private javax.swing.JButton okButton; private javax.swing.JButton okButton;
private javax.swing.JButton orgButton; private javax.swing.JButton orgButton;
private javax.swing.JComboBox<String> orgComboBox; private javax.swing.JComboBox<String> orgComboBox;

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">
<Group type="102" alignment="0" 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" attributes="0"> <Group type="102" attributes="0">
@ -54,10 +54,6 @@
</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">
<Group type="102" alignment="1" attributes="0">
<Component id="sendIngestMessagesCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<Component id="lbOrg" min="-2" max="-2" attributes="0"/> <Component id="lbOrg" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
@ -76,7 +72,16 @@
<Component id="hashSetNameTextField" max="32767" attributes="0"/> <Component id="hashSetNameTextField" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
<Group type="102" attributes="0"> </Group>
<EmptySpace min="-2" pref="81" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="sendIngestMessagesCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="okButton" linkSize="1" min="-2" max="-2" attributes="0"/> <Component id="okButton" linkSize="1" min="-2" max="-2" attributes="0"/>
</Group> </Group>
@ -86,16 +91,17 @@
</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="saveInUserConfigFolderCheckbox" min="-2" max="-2" attributes="0"/>
<Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="readOnlyCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace min="-2" pref="19" max="-2" attributes="0"/> <EmptySpace min="-2" pref="19" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="knownRadioButton" min="-2" max="-2" attributes="0"/> <Component id="knownRadioButton" min="-2" max="-2" attributes="0"/>
<Component id="knownBadRadioButton" min="-2" max="-2" attributes="0"/> <Component id="knownBadRadioButton" min="-2" max="-2" attributes="0"/>
<Component id="noChangeRadioButton" min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
<Component id="saveInUserConfigFolderCheckbox" min="-2" max="-2" attributes="0"/>
<Component id="readOnlyCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
@ -113,52 +119,54 @@
<Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="openButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="openButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="fileTypeRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="centralRepoRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jLabel4" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="hashSetNameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbVersion" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="versionTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="orgButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="orgComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbOrg" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="jLabel2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="knownRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="knownBadRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="noChangeRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="5" max="-2" attributes="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">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="fileTypeRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="centralRepoRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jLabel4" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="hashSetNameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbVersion" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="versionTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="orgButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="orgComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbOrg" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="jLabel2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="knownRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="knownBadRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="readOnlyCheckbox" min="-2" max="-2" attributes="0"/> <Component id="readOnlyCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="sendIngestMessagesCheckbox" min="-2" max="-2" attributes="0"/> <Component id="sendIngestMessagesCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="saveInUserConfigFolderCheckbox" min="-2" max="-2" attributes="0"/> <Component id="saveInUserConfigFolderCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="29" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="-2" pref="81" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -367,5 +375,21 @@
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="noChangeRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup1"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="HashDbImportDatabaseDialog.noChangeRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="HashDbImportDatabaseDialog.noChangeRadioButton.toolTipText" 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="noChangeRadioButtonActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -182,6 +182,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
centralRepoRadioButton = new javax.swing.JRadioButton(); centralRepoRadioButton = new javax.swing.JRadioButton();
jLabel4 = new javax.swing.JLabel(); jLabel4 = new javax.swing.JLabel();
saveInUserConfigFolderCheckbox = new javax.swing.JCheckBox(); saveInUserConfigFolderCheckbox = new javax.swing.JCheckBox();
noChangeRadioButton = new javax.swing.JRadioButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
@ -291,6 +292,15 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
org.openide.awt.Mnemonics.setLocalizedText(saveInUserConfigFolderCheckbox, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(saveInUserConfigFolderCheckbox, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.text")); // NOI18N
saveInUserConfigFolderCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.toolTipText")); // NOI18N saveInUserConfigFolderCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.saveInUserConfigFolderCheckbox.toolTipText")); // NOI18N
buttonGroup1.add(noChangeRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(noChangeRadioButton, org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.noChangeRadioButton.text")); // NOI18N
noChangeRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(HashDbImportDatabaseDialog.class, "HashDbImportDatabaseDialog.noChangeRadioButton.toolTipText")); // NOI18N
noChangeRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
noChangeRadioButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout); getContentPane().setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
@ -315,9 +325,6 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
.addComponent(openButton)))) .addComponent(openButton))))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(sendIngestMessagesCheckbox)
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(lbOrg) .addComponent(lbOrg)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
@ -331,7 +338,13 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
.addGap(40, 40, 40) .addGap(40, 40, 40)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(versionTextField) .addComponent(versionTextField)
.addComponent(hashSetNameTextField))) .addComponent(hashSetNameTextField))))
.addGap(81, 81, 81))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(sendIngestMessagesCheckbox)
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE) .addGap(0, 0, Short.MAX_VALUE)
.addComponent(okButton))) .addComponent(okButton)))
@ -339,14 +352,15 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
.addComponent(cancelButton)) .addComponent(cancelButton))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(saveInUserConfigFolderCheckbox)
.addComponent(jLabel2) .addComponent(jLabel2)
.addComponent(readOnlyCheckbox)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(19, 19, 19) .addGap(19, 19, 19)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(knownRadioButton) .addComponent(knownRadioButton)
.addComponent(knownBadRadioButton)))) .addComponent(knownBadRadioButton)
.addComponent(noChangeRadioButton)))
.addComponent(saveInUserConfigFolderCheckbox)
.addComponent(readOnlyCheckbox))
.addGap(0, 0, Short.MAX_VALUE))) .addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap()) .addContainerGap())
); );
@ -361,44 +375,46 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
.addComponent(databasePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(databasePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel3) .addComponent(jLabel3)
.addComponent(openButton)) .addComponent(openButton))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(fileTypeRadioButton)
.addComponent(centralRepoRadioButton)
.addComponent(jLabel4))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(hashSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbVersion)
.addComponent(versionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(5, 5, 5)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(orgButton)
.addComponent(orgComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbOrg))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(knownRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(knownBadRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(noChangeRadioButton)
.addGap(5, 5, 5)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(fileTypeRadioButton)
.addComponent(centralRepoRadioButton)
.addComponent(jLabel4))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(hashSetNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbVersion)
.addComponent(versionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(5, 5, 5)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(orgButton)
.addComponent(orgComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbOrg))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(knownRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(knownBadRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(readOnlyCheckbox) .addComponent(readOnlyCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(sendIngestMessagesCheckbox) .addComponent(sendIngestMessagesCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(saveInUserConfigFolderCheckbox) .addComponent(saveInUserConfigFolderCheckbox)
.addGap(0, 29, Short.MAX_VALUE)) .addGap(0, 0, Short.MAX_VALUE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE) .addGap(81, 81, 81)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cancelButton) .addComponent(cancelButton)
.addComponent(okButton)))) .addComponent(okButton))))
.addContainerGap()) .addGap(18, 18, 18))
); );
pack(); pack();
@ -436,13 +452,13 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
}//GEN-LAST:event_openButtonActionPerformed }//GEN-LAST:event_openButtonActionPerformed
private void knownRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownRadioButtonActionPerformed private void knownRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownRadioButtonActionPerformed
sendIngestMessagesCheckbox.setSelected(false); sendIngestMessagesCheckbox.setSelected(KnownFilesType.KNOWN.isDefaultInboxMessages());
sendIngestMessagesCheckbox.setEnabled(false); sendIngestMessagesCheckbox.setEnabled(KnownFilesType.KNOWN.isInboxMessagesAllowed());
}//GEN-LAST:event_knownRadioButtonActionPerformed }//GEN-LAST:event_knownRadioButtonActionPerformed
private void knownBadRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownBadRadioButtonActionPerformed private void knownBadRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownBadRadioButtonActionPerformed
sendIngestMessagesCheckbox.setSelected(true); sendIngestMessagesCheckbox.setSelected(KnownFilesType.KNOWN_BAD.isDefaultInboxMessages());
sendIngestMessagesCheckbox.setEnabled(true); sendIngestMessagesCheckbox.setEnabled(KnownFilesType.KNOWN_BAD.isInboxMessagesAllowed());
}//GEN-LAST:event_knownBadRadioButtonActionPerformed }//GEN-LAST:event_knownBadRadioButtonActionPerformed
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
@ -531,6 +547,8 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
KnownFilesType type; KnownFilesType type;
if (knownRadioButton.isSelected()) { if (knownRadioButton.isSelected()) {
type = KnownFilesType.KNOWN; type = KnownFilesType.KNOWN;
} else if (noChangeRadioButton.isSelected()) {
type = KnownFilesType.NO_CHANGE;
} else { } else {
type = KnownFilesType.KNOWN_BAD; type = KnownFilesType.KNOWN_BAD;
} }
@ -629,6 +647,11 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
enableComponents(); enableComponents();
}//GEN-LAST:event_readOnlyCheckboxActionPerformed }//GEN-LAST:event_readOnlyCheckboxActionPerformed
private void noChangeRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_noChangeRadioButtonActionPerformed
sendIngestMessagesCheckbox.setSelected(KnownFilesType.NO_CHANGE.isDefaultInboxMessages());
sendIngestMessagesCheckbox.setEnabled(KnownFilesType.NO_CHANGE.isInboxMessagesAllowed());
}//GEN-LAST:event_noChangeRadioButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.ButtonGroup buttonGroup1; private javax.swing.ButtonGroup buttonGroup1;
private javax.swing.JButton cancelButton; private javax.swing.JButton cancelButton;
@ -644,6 +667,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
private javax.swing.JRadioButton knownRadioButton; private javax.swing.JRadioButton knownRadioButton;
private javax.swing.JLabel lbOrg; private javax.swing.JLabel lbOrg;
private javax.swing.JLabel lbVersion; private javax.swing.JLabel lbVersion;
private javax.swing.JRadioButton noChangeRadioButton;
private javax.swing.JButton okButton; private javax.swing.JButton okButton;
private javax.swing.JButton openButton; private javax.swing.JButton openButton;
private javax.swing.JButton orgButton; private javax.swing.JButton orgButton;

View File

@ -24,7 +24,9 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Stream;
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.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
@ -57,12 +59,26 @@ import org.sleuthkit.datamodel.TskException;
@Messages({ @Messages({
"HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.", "HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.",
"HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable file search will not be executed.", "HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable file search will not be executed.",
"HashDbIngestModule.noChangeHashDbSetMsg=No 'No Change' hash set.",
"HashDbIngestModule.noChangeFileSearchWillNotExecuteWarn='No Change' file search will not be executed.",
"HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.", "HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.",
"HashDbIngestModule.knownFileSearchWillNotExecuteWarn=Known file search will not be executed." "HashDbIngestModule.knownFileSearchWillNotExecuteWarn=Known file search will not be executed.",
}) "# {0} - fileName", "HashDbIngestModule.lookingUpKnownBadHashValueErr=Error encountered while looking up notable hash value for {0}.",
"# {0} - fileName", "HashDbIngestModule.lookingUpNoChangeHashValueErr=Error encountered while looking up no change hash value for {0}.",
"# {0} - fileName", "HashDbIngestModule.lookingUpKnownHashValueErr=Error encountered while looking up known hash value for {0}.",})
public class HashDbIngestModule implements FileIngestModule { public class HashDbIngestModule implements FileIngestModule {
private static final Logger logger = Logger.getLogger(HashDbIngestModule.class.getName()); private static final Logger logger = Logger.getLogger(HashDbIngestModule.class.getName());
private final Function<AbstractFile, String> knownBadLookupError
= (file) -> Bundle.HashDbIngestModule_lookingUpKnownBadHashValueErr(file.getName());
private final Function<AbstractFile, String> noChangeLookupError
= (file) -> Bundle.HashDbIngestModule_lookingUpNoChangeHashValueErr(file.getName());
private final Function<AbstractFile, String> knownLookupError
= (file) -> Bundle.HashDbIngestModule_lookingUpKnownHashValueErr(file.getName());
private static final int MAX_COMMENT_SIZE = 500; private static final int MAX_COMMENT_SIZE = 500;
private final IngestServices services = IngestServices.getInstance(); private final IngestServices services = IngestServices.getInstance();
private final SleuthkitCase skCase; private final SleuthkitCase skCase;
@ -70,6 +86,7 @@ public class HashDbIngestModule implements FileIngestModule {
private final HashLookupModuleSettings settings; private final HashLookupModuleSettings settings;
private final List<HashDb> knownBadHashSets = new ArrayList<>(); private final List<HashDb> knownBadHashSets = new ArrayList<>();
private final List<HashDb> knownHashSets = new ArrayList<>(); private final List<HashDb> knownHashSets = new ArrayList<>();
private final List<HashDb> noChangeHashSets = new ArrayList<>();
private long jobId; private long jobId;
private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>(); private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>();
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter(); private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
@ -81,6 +98,7 @@ public class HashDbIngestModule implements FileIngestModule {
private static class IngestJobTotals { private static class IngestJobTotals {
private final AtomicLong totalKnownBadCount = new AtomicLong(0); private final AtomicLong totalKnownBadCount = new AtomicLong(0);
private final AtomicLong totalNoChangeCount = new AtomicLong(0);
private final AtomicLong totalCalctime = new AtomicLong(0); private final AtomicLong totalCalctime = new AtomicLong(0);
private final AtomicLong totalLookuptime = new AtomicLong(0); private final AtomicLong totalLookuptime = new AtomicLong(0);
} }
@ -114,8 +132,8 @@ public class HashDbIngestModule implements FileIngestModule {
if (!hashDbManager.verifyAllDatabasesLoadedCorrectly()) { if (!hashDbManager.verifyAllDatabasesLoadedCorrectly()) {
throw new IngestModuleException("Could not load all hash sets"); throw new IngestModuleException("Could not load all hash sets");
} }
updateEnabledHashSets(hashDbManager.getKnownBadFileHashSets(), knownBadHashSets);
updateEnabledHashSets(hashDbManager.getKnownFileHashSets(), knownHashSets); initializeHashsets(hashDbManager.getAllHashSets());
if (refCounter.incrementAndGet(jobId) == 1) { if (refCounter.incrementAndGet(jobId) == 1) {
// initialize job totals // initialize job totals
@ -128,6 +146,13 @@ public class HashDbIngestModule implements FileIngestModule {
Bundle.HashDbIngestModule_noKnownBadHashDbSetMsg(), Bundle.HashDbIngestModule_noKnownBadHashDbSetMsg(),
Bundle.HashDbIngestModule_knownBadFileSearchWillNotExecuteWarn())); Bundle.HashDbIngestModule_knownBadFileSearchWillNotExecuteWarn()));
} }
if (noChangeHashSets.isEmpty()) {
services.postMessage(IngestMessage.createWarningMessage(
HashLookupModuleFactory.getModuleName(),
Bundle.HashDbIngestModule_noChangeHashDbSetMsg(),
Bundle.HashDbIngestModule_noChangeFileSearchWillNotExecuteWarn()));
}
if (knownHashSets.isEmpty()) { if (knownHashSets.isEmpty()) {
services.postMessage(IngestMessage.createWarningMessage( services.postMessage(IngestMessage.createWarningMessage(
@ -139,18 +164,29 @@ public class HashDbIngestModule implements FileIngestModule {
} }
/** /**
* Cycle through list of hashsets and return the subset that is enabled. * Cycle through list of hashsets and place each HashDB in the appropriate
* list based on KnownFilesType.
* *
* @param allHashSets List of all hashsets from DB manager * @param allHashSets List of all hashsets from DB manager
* @param enabledHashSets List of enabled ones to return.
*/ */
private void updateEnabledHashSets(List<HashDb> allHashSets, List<HashDb> enabledHashSets) { private void initializeHashsets(List<HashDb> allHashSets) {
enabledHashSets.clear();
for (HashDb db : allHashSets) { for (HashDb db : allHashSets) {
if (settings.isHashSetEnabled(db)) { if (settings.isHashSetEnabled(db)) {
try { try {
if (db.isValid()) { if (db.isValid()) {
enabledHashSets.add(db); switch (db.getKnownFilesType()) {
case KNOWN:
knownHashSets.add(db);
break;
case KNOWN_BAD:
knownBadHashSets.add(db);
break;
case NO_CHANGE:
noChangeHashSets.add(db);
break;
default:
throw new TskCoreException("Unknown KnownFilesType: " + db.getKnownFilesType());
}
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting index status for " + db.getDisplayName() + " hash set", ex); //NON-NLS logger.log(Level.WARNING, "Error getting index status for " + db.getDisplayName() + " hash set", ex); //NON-NLS
@ -174,128 +210,37 @@ public class HashDbIngestModule implements FileIngestModule {
return ProcessResult.ERROR; return ProcessResult.ERROR;
} }
// Skip unallocated space files. if (shouldSkip(file)) {
if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
|| file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
return ProcessResult.OK;
}
/*
* Skip directories. One reason for this is because we won't accurately
* calculate hashes of NTFS directories that have content that spans the
* IDX_ROOT and IDX_ALLOC artifacts. So we disable that until a solution
* for it is developed.
*/
if (file.isDir()) {
return ProcessResult.OK;
}
// bail out if we have no hashes set
if ((knownHashSets.isEmpty()) && (knownBadHashSets.isEmpty()) && (!settings.shouldCalculateHashes())) {
return ProcessResult.OK; return ProcessResult.OK;
} }
// Safely get a reference to the totalsForIngestJobs object // Safely get a reference to the totalsForIngestJobs object
IngestJobTotals totals = getTotalsForIngestJobs(jobId); IngestJobTotals totals = getTotalsForIngestJobs(jobId);
// calc hash value // calc hash value
String name = file.getName(); String md5Hash = getHash(file, totals);
long fileId = file.getId(); if (md5Hash == null) {
String md5Hash = file.getMd5Hash(); return ProcessResult.ERROR;
if (md5Hash == null || md5Hash.isEmpty()) {
try {
TimingMetric metric = HealthMonitor.getTimingMetric("Disk Reads: Hash calculation");
long calcstart = System.currentTimeMillis();
md5Hash = HashUtility.calculateMd5Hash(file);
if (file.getSize() > 0) {
// Surprisingly, the hash calculation does not seem to be correlated that
// strongly with file size until the files get large.
// Only normalize if the file size is greater than ~1MB.
if (file.getSize() < 1000000) {
HealthMonitor.submitTimingMetric(metric);
} else {
// In testing, this normalization gave reasonable resuls
HealthMonitor.submitNormalizedTimingMetric(metric, file.getSize() / 500000);
}
}
file.setMd5Hash(md5Hash);
long delta = (System.currentTimeMillis() - calcstart);
totals.totalCalctime.addAndGet(delta);
} catch (IOException ex) {
logger.log(Level.WARNING, String.format("Error calculating hash of file '%s' (id=%d).", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", 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;
}
} }
// look up in notable first // the processing result of handling this file
boolean foundBad = false;
ProcessResult ret = ProcessResult.OK; ProcessResult ret = ProcessResult.OK;
for (HashDb db : knownBadHashSets) {
try {
long lookupstart = System.currentTimeMillis();
HashHitInfo hashInfo = db.lookupMD5(file);
if (null != hashInfo) {
foundBad = true;
totals.totalKnownBadCount.incrementAndGet();
file.setKnown(TskData.FileKnown.BAD); // look up in notable first
FindInHashsetsResult knownBadResult = findInHashsets(file, totals.totalKnownBadCount,
totals.totalLookuptime, knownBadHashSets, TskData.FileKnown.BAD, knownBadLookupError);
String hashSetName = db.getDisplayName(); boolean foundBad = knownBadResult.isFound();
if (knownBadResult.isError()) {
ret = ProcessResult.ERROR;
}
String comment = ""; // look up no change items next
ArrayList<String> comments = hashInfo.getComments(); FindInHashsetsResult noChangeResult = findInHashsets(file, totals.totalNoChangeCount,
int i = 0; totals.totalLookuptime, noChangeHashSets, TskData.FileKnown.UNKNOWN, noChangeLookupError);
for (String c : comments) {
if (++i > 1) {
comment += " ";
}
comment += c;
if (comment.length() > MAX_COMMENT_SIZE) {
comment = comment.substring(0, MAX_COMMENT_SIZE) + "...";
break;
}
}
/* if (noChangeResult.isError()) {
* We have a match. Now create an artifact if it is ret = ProcessResult.ERROR;
* determined that one hasn't been created yet.
*/
List<BlackboardAttribute> attributesList = new ArrayList<>();
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, HashLookupModuleFactory.getModuleName(), hashSetName));
try {
org.sleuthkit.datamodel.Blackboard tskBlackboard = skCase.getBlackboard();
if (tskBlackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, attributesList) == false) {
postHashSetHitToBlackboard(file, md5Hash, hashSetName, comment, db.getSendIngestMessages());
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format(
"A problem occurred while checking for existing artifacts for file '%s' (id=%d).", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
Bundle.HashDbIngestModule_dialogTitle_errorFindingArtifacts(name),
Bundle.HashDbIngestModule_errorMessage_lookingForFileArtifacts(name)));
ret = ProcessResult.ERROR;
}
}
long delta = (System.currentTimeMillis() - lookupstart);
totals.totalLookuptime.addAndGet(delta);
} catch (TskException ex) {
logger.log(Level.WARNING, String.format(
"Couldn't lookup notable hash for file '%s' (id=%d) - see sleuthkit log for details", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.hashLookupErrorMsg", name),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.lookingUpKnownBadHashValueErr", name)));
ret = ProcessResult.ERROR;
}
} }
// If the file is not in the notable sets, search for it in the known sets. // If the file is not in the notable sets, search for it in the known sets.
@ -313,12 +258,7 @@ public class HashDbIngestModule implements FileIngestModule {
totals.totalLookuptime.addAndGet(delta); totals.totalLookuptime.addAndGet(delta);
} catch (TskException ex) { } catch (TskException ex) {
logger.log(Level.WARNING, String.format( reportLookupError(ex, file, knownLookupError);
"Couldn't lookup known hash for file '%s' (id=%d) - see sleuthkit log for details", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.hashLookupErrorMsg", name),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.lookingUpKnownHashValueErr", name)));
ret = ProcessResult.ERROR; ret = ProcessResult.ERROR;
} }
} }
@ -327,6 +267,245 @@ public class HashDbIngestModule implements FileIngestModule {
return ret; return ret;
} }
/**
* Returns true if this file should be skipped for processing.
*
* @param file The file to potentially skip.
*
* @return True if this file should be skipped.
*/
private boolean shouldSkip(AbstractFile file) {
// Skip unallocated space files.
if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
|| file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
return true;
}
/*
* Skip directories. One reason for this is because we won't accurately
* calculate hashes of NTFS directories that have content that spans the
* IDX_ROOT and IDX_ALLOC artifacts. So we disable that until a solution
* for it is developed.
*/
if (file.isDir()) {
return true;
}
// bail out if we have no hashes set
if ((knownHashSets.isEmpty()) && (knownBadHashSets.isEmpty()) && (!settings.shouldCalculateHashes())) {
return true;
}
return false;
}
/**
* Reports an error when an issue is encountered looking up a file.
*
* @param ex The exception thrown in the error.
* @param file The file for which this error applies.
* @param lookupErrorMessage The function that generates an error message
* specific to which piece of the ingest
* processing failed.
*/
private void reportLookupError(TskException ex, AbstractFile file, Function<AbstractFile, String> lookupErrorMessage) {
logger.log(Level.WARNING, String.format(
"Couldn't lookup notable hash for file '%s' (id=%d) - see sleuthkit log for details", file.getName(), file.getId()), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.hashLookupErrorMsg", file.getName()),
lookupErrorMessage.apply(file)));
}
/**
* The result of attempting to find a file in a list of HashDB objects.
*/
private static class FindInHashsetsResult {
private final boolean found;
private final boolean error;
FindInHashsetsResult(boolean found, boolean error) {
this.found = found;
this.error = error;
}
/**
* Returns true if the file was found in the HashDB.
*
* @return True if the file was found in the HashDB.
*/
boolean isFound() {
return found;
}
/**
* Returns true if there was an error in the process of finding a file
* in a HashDB.
*
* @return True if there was an error in the process of finding a file
* in a HashDB.
*/
boolean isError() {
return error;
}
}
/**
* Attempts to find an abstract file in a list of HashDB objects.
*
* @param file The file to find.
* @param totalCount The total cound of files found in this type
* @param totalLookupTime The counter tracking the total amount of run
* time for this operation.
* @param hashSets The HashDB objects to cycle through looking for
* a hash hit.
* @param statusIfFound The FileKnown status to set on the file if the
* file is found in the hashSets.
* @param lookupErrorMessage The function that generates a message should
* there be an error in looking up the file in the
* hashSets.
*
* @return Whether or not the file was found and whether or not there was an
* error during the operation.
*/
private FindInHashsetsResult findInHashsets(AbstractFile file, AtomicLong totalCount, AtomicLong totalLookupTime,
List<HashDb> hashSets, TskData.FileKnown statusIfFound, Function<AbstractFile, String> lookupErrorMessage) {
boolean found = false;
boolean wasError = false;
for (HashDb db : hashSets) {
try {
long lookupstart = System.currentTimeMillis();
HashHitInfo hashInfo = db.lookupMD5(file);
if (null != hashInfo) {
found = true;
totalCount.incrementAndGet();
file.setKnown(statusIfFound);
String hashSetName = db.getDisplayName();
String comment = generateComment(hashInfo);
if (!createArtifactIfNotExists(hashSetName, file, comment, db)) {
wasError = true;
}
}
long delta = (System.currentTimeMillis() - lookupstart);
totalLookupTime.addAndGet(delta);
} catch (TskException ex) {
reportLookupError(ex, file, lookupErrorMessage);
wasError = true;
}
}
return new FindInHashsetsResult(found, wasError);
}
/**
* Generates a formatted comment.
*
* @param hashInfo The HashHitInfo.
*
* @return The formatted comment.
*/
private String generateComment(HashHitInfo hashInfo) {
String comment = "";
ArrayList<String> comments = hashInfo.getComments();
int i = 0;
for (String c : comments) {
if (++i > 1) {
comment += " ";
}
comment += c;
if (comment.length() > MAX_COMMENT_SIZE) {
comment = comment.substring(0, MAX_COMMENT_SIZE) + "...";
break;
}
}
return comment;
}
/**
* Creates a BlackboardArtifact if artifact does not already exist.
*
* @param hashSetName The name of the hashset found.
* @param file The file that had a hash hit.
* @param comment The comment to associate with this artifact.
* @param db the database in which this file was found.
*
* @return True if the operation occurred successfully and without error.
*/
private boolean createArtifactIfNotExists(String hashSetName, AbstractFile file, String comment, HashDb db) {
/*
* We have a match. Now create an artifact if it is determined that one
* hasn't been created yet.
*/
List<BlackboardAttribute> attributesList = new ArrayList<>();
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, HashLookupModuleFactory.getModuleName(), hashSetName));
try {
Blackboard tskBlackboard = skCase.getBlackboard();
if (tskBlackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, attributesList) == false) {
postHashSetHitToBlackboard(file, file.getMd5Hash(), hashSetName, comment, db.getSendIngestMessages());
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format(
"A problem occurred while checking for existing artifacts for file '%s' (id=%d).", file.getName(), file.getId()), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
Bundle.HashDbIngestModule_dialogTitle_errorFindingArtifacts(file.getName()),
Bundle.HashDbIngestModule_errorMessage_lookingForFileArtifacts(file.getName())));
return false;
}
return true;
}
/**
* Retrieves the md5 hash for a file or generates one if no one exists on
* the file.
*
* @param file The file in order to determine the hash.
* @param totals The timing metrics for this process.
*
* @return The found or determined md5 hash or null if none could be
* determined.
*/
private String getHash(AbstractFile file, IngestJobTotals totals) {
String md5Hash = file.getMd5Hash();
if (md5Hash != null && md5Hash.isEmpty()) {
return md5Hash;
}
try {
TimingMetric metric = HealthMonitor.getTimingMetric("Disk Reads: Hash calculation");
long calcstart = System.currentTimeMillis();
md5Hash = HashUtility.calculateMd5Hash(file);
if (file.getSize() > 0) {
// Surprisingly, the hash calculation does not seem to be correlated that
// strongly with file size until the files get large.
// Only normalize if the file size is greater than ~1MB.
if (file.getSize() < 1000000) {
HealthMonitor.submitTimingMetric(metric);
} else {
// In testing, this normalization gave reasonable resuls
HealthMonitor.submitNormalizedTimingMetric(metric, file.getSize() / 500000);
}
}
file.setMd5Hash(md5Hash);
long delta = (System.currentTimeMillis() - calcstart);
totals.totalCalctime.addAndGet(delta);
return md5Hash;
} catch (IOException ex) {
logger.log(Level.WARNING, String.format("Error calculating hash of file '%s' (id=%d).", file.getName(), file.getId()), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", file.getName()),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr",
file.getParentPath() + file.getName(),
file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) ? "Allocated File" : "Deleted File")));
return null;
}
}
/** /**
* Post a hash set hit to the blackboard. * Post a hash set hit to the blackboard.
* *
@ -413,35 +592,35 @@ public class HashDbIngestModule implements FileIngestModule {
* @param knownBadHashSets The list of hash sets for "known bad" files. * @param knownBadHashSets The list of hash sets for "known bad" files.
* @param knownHashSets The list of hash sets for "known" files. * @param knownHashSets The list of hash sets for "known" files.
*/ */
private static synchronized void postSummary(long jobId, @Messages("HashDbIngestModule.complete.noChangesFound=No Change items found:")
List<HashDb> knownBadHashSets, List<HashDb> knownHashSets) { private static synchronized void postSummary(long jobId, List<HashDb> knownBadHashSets,
List<HashDb> noChangeHashSets, List<HashDb> knownHashSets) {
IngestJobTotals jobTotals = getTotalsForIngestJobs(jobId); IngestJobTotals jobTotals = getTotalsForIngestJobs(jobId);
totalsForIngestJobs.remove(jobId); totalsForIngestJobs.remove(jobId);
if ((!knownBadHashSets.isEmpty()) || (!knownHashSets.isEmpty())) { if ((!knownBadHashSets.isEmpty()) || (!knownHashSets.isEmpty()) || (!noChangeHashSets.isEmpty())) {
StringBuilder detailsSb = new StringBuilder(); StringBuilder detailsSb = new StringBuilder();
//details //details
detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS detailsSb.append(
"<table border='0' cellpadding='4' width='280'>" +
"<tr><td>" + NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.knownBadsFound") + "</td>" +
"<td>" + jobTotals.totalKnownBadCount.get() + "</td></tr>" +
"<tr><td>" + Bundle.HashDbIngestModule_complete_noChangesFound() + "</td>" +
"<td>" + jobTotals.totalNoChangeCount.get() + "</td></tr>" +
"<tr><td>" + NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.totalCalcTime") +
"</td><td>" + jobTotals.totalCalctime.get() + "</td></tr>\n" +
"<tr><td>" + NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.totalLookupTime") +
"</td><td>" + jobTotals.totalLookuptime.get() + "</td></tr>\n</table>" +
detailsSb.append("<tr><td>") //NON-NLS "<p>" + NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.databasesUsed") + "</p>\n<ul>"); //NON-NLS
.append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.knownBadsFound"))
.append("</td>"); //NON-NLS Stream.concat(knownBadHashSets.stream(), noChangeHashSets.stream()).forEach((db) -> {
detailsSb.append("<td>").append(jobTotals.totalKnownBadCount.get()).append("</td></tr>"); //NON-NLS detailsSb.append("<li>" + db.getHashSetName() + "</li>\n"); //NON-NLS
});
detailsSb.append("<tr><td>") //NON-NLS
.append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.totalCalcTime"))
.append("</td><td>").append(jobTotals.totalCalctime.get()).append("</td></tr>\n"); //NON-NLS
detailsSb.append("<tr><td>") //NON-NLS
.append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.totalLookupTime"))
.append("</td><td>").append(jobTotals.totalLookuptime.get()).append("</td></tr>\n"); //NON-NLS
detailsSb.append("</table>"); //NON-NLS
detailsSb.append("<p>") //NON-NLS
.append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.databasesUsed"))
.append("</p>\n<ul>"); //NON-NLS
for (HashDb db : knownBadHashSets) {
detailsSb.append("<li>").append(db.getHashSetName()).append("</li>\n"); //NON-NLS
}
detailsSb.append("</ul>"); //NON-NLS detailsSb.append("</ul>"); //NON-NLS
@ -456,7 +635,7 @@ public class HashDbIngestModule implements FileIngestModule {
@Override @Override
public void shutDown() { public void shutDown() {
if (refCounter.decrementAndGet(jobId) == 0) { if (refCounter.decrementAndGet(jobId) == 0) {
postSummary(jobId, knownBadHashSets, knownHashSets); postSummary(jobId, knownBadHashSets, noChangeHashSets, knownHashSets);
} }
} }
} }

View File

@ -1,15 +1,15 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 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");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -23,7 +23,6 @@ import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -31,6 +30,7 @@ import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Stream;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
@ -58,6 +58,7 @@ import org.sleuthkit.datamodel.SleuthkitJNI;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb.KnownFilesType;
/** /**
* This class implements a singleton that manages the set of hash databases used * This class implements a singleton that manages the set of hash databases used
@ -103,8 +104,8 @@ public class HashDbManager implements PropertyChangeListener {
public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener); changeSupport.removePropertyChangeListener(listener);
} }
synchronized boolean verifyAllDatabasesLoadedCorrectly(){ synchronized boolean verifyAllDatabasesLoadedCorrectly() {
return allDatabasesLoadedCorrectly; return allDatabasesLoadedCorrectly;
} }
@ -238,7 +239,7 @@ public class HashDbManager implements PropertyChangeListener {
} }
return hashDb; return hashDb;
} }
private SleuthkitHashSet addHashDatabase(int handle, String hashSetName, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws TskCoreException { private SleuthkitHashSet addHashDatabase(int handle, String hashSetName, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws TskCoreException {
// Wrap an object around the handle. // Wrap an object around the handle.
SleuthkitHashSet hashDb = new SleuthkitHashSet(handle, hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType); SleuthkitHashSet hashDb = new SleuthkitHashSet(handle, hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType);
@ -273,22 +274,22 @@ public class HashDbManager implements PropertyChangeListener {
} }
return hashDb; return hashDb;
} }
CentralRepoHashSet addExistingCentralRepoHashSet(String hashSetName, String version, int referenceSetID, CentralRepoHashSet addExistingCentralRepoHashSet(String hashSetName, String version, int referenceSetID,
boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType,
boolean readOnly) throws TskCoreException{ boolean readOnly) throws TskCoreException {
if(! CentralRepository.isEnabled()){ if (!CentralRepository.isEnabled()) {
throw new TskCoreException("Could not load central repository hash set " + hashSetName + " - central repository is not enabled"); throw new TskCoreException("Could not load central repository hash set " + hashSetName + " - central repository is not enabled");
} }
CentralRepoHashSet db = new CentralRepoHashSet(hashSetName, version, referenceSetID, searchDuringIngest, CentralRepoHashSet db = new CentralRepoHashSet(hashSetName, version, referenceSetID, searchDuringIngest,
sendIngestMessages, knownFilesType, readOnly); sendIngestMessages, knownFilesType, readOnly);
if(! db.isValid()){ if (!db.isValid()) {
throw new TskCoreException("Error finding hash set " + hashSetName + " in central repository"); throw new TskCoreException("Error finding hash set " + hashSetName + " in central repository");
} }
// Add the hash database to the collection // Add the hash database to the collection
hashSets.add(db); hashSets.add(db);
@ -302,8 +303,8 @@ public class HashDbManager implements PropertyChangeListener {
NbBundle.getMessage(this.getClass(), "HashDbManager.moduleErrorListeningToUpdatesMsg"), NbBundle.getMessage(this.getClass(), "HashDbManager.moduleErrorListeningToUpdatesMsg"),
MessageNotifyUtil.MessageType.ERROR); MessageNotifyUtil.MessageType.ERROR);
} }
return db; return db;
} }
synchronized void indexHashDatabase(SleuthkitHashSet hashDb) { synchronized void indexHashDatabase(SleuthkitHashSet hashDb) {
@ -341,7 +342,7 @@ public class HashDbManager implements PropertyChangeListener {
this.removeHashDatabaseNoSave(hashDb); this.removeHashDatabaseNoSave(hashDb);
this.save(); this.save();
} }
public synchronized void removeHashDatabaseNoSave(HashDb hashDb) throws HashDbManagerException { public synchronized void removeHashDatabaseNoSave(HashDb hashDb) throws HashDbManagerException {
// Don't remove a database if ingest is running // Don't remove a database if ingest is running
boolean ingestIsRunning = IngestManager.getInstance().isIngestRunning(); boolean ingestIsRunning = IngestManager.getInstance().isIngestRunning();
@ -357,17 +358,16 @@ public class HashDbManager implements PropertyChangeListener {
hashSets.remove(hashDb); hashSets.remove(hashDb);
// Now undertake the operations that could throw. // Now undertake the operations that could throw.
// Indexing is only relevanet for sleuthkit hashsets // Indexing is only relevanet for sleuthkit hashsets
if(hashDb instanceof SleuthkitHashSet){ if (hashDb instanceof SleuthkitHashSet) {
SleuthkitHashSet hashDatabase = (SleuthkitHashSet)hashDb; SleuthkitHashSet hashDatabase = (SleuthkitHashSet) hashDb;
try { try {
if(hashDatabase.hasIndex()){ if (hashDatabase.hasIndex()) {
hashSetPaths.remove(hashDatabase.getIndexPath()); hashSetPaths.remove(hashDatabase.getIndexPath());
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error getting index path of " + hashDatabase.getHashSetName() + " hash set when removing the hash set", ex); //NON-NLS Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error getting index path of " + hashDatabase.getHashSetName() + " hash set when removing the hash set", ex); //NON-NLS
} }
try { try {
if (!hashDatabase.hasIndexOnly()) { if (!hashDatabase.hasIndexOnly()) {
@ -376,7 +376,7 @@ public class HashDbManager implements PropertyChangeListener {
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error getting hash set path of " + hashDatabase.getHashSetName() + " hash set when removing the hash set", ex); //NON-NLS Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error getting hash set path of " + hashDatabase.getHashSetName() + " hash set when removing the hash set", ex); //NON-NLS
} }
try { try {
hashDatabase.close(); hashDatabase.close();
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
@ -405,7 +405,7 @@ public class HashDbManager implements PropertyChangeListener {
throw new HashDbManagerException(NbBundle.getMessage(this.getClass(), "HashDbManager.saveErrorExceptionMsg")); throw new HashDbManagerException(NbBundle.getMessage(this.getClass(), "HashDbManager.saveErrorExceptionMsg"));
} }
} }
/** /**
* Gets all of the hash databases used to classify files as known or known * Gets all of the hash databases used to classify files as known or known
* bad. Will add any new central repository databases to the list before * bad. Will add any new central repository databases to the list before
@ -414,12 +414,12 @@ public class HashDbManager implements PropertyChangeListener {
* @return A list, possibly empty, of hash databases. * @return A list, possibly empty, of hash databases.
*/ */
public synchronized List<HashDb> getAllHashSets() { public synchronized List<HashDb> getAllHashSets() {
try{ try {
updateHashSetsFromCentralRepository(); updateHashSetsFromCentralRepository();
} catch (TskCoreException ex){ } catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
} }
List<HashDb> hashDbs = new ArrayList<>(); List<HashDb> hashDbs = new ArrayList<>();
hashDbs.addAll(this.hashSets); hashDbs.addAll(this.hashSets);
return hashDbs; return hashDbs;
@ -432,9 +432,9 @@ public class HashDbManager implements PropertyChangeListener {
*/ */
public synchronized List<HashDb> getKnownFileHashSets() { public synchronized List<HashDb> getKnownFileHashSets() {
List<HashDb> hashDbs = new ArrayList<>(); List<HashDb> hashDbs = new ArrayList<>();
try{ try {
updateHashSetsFromCentralRepository(); updateHashSetsFromCentralRepository();
} catch (TskCoreException ex){ } catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
} }
this.hashSets.stream().filter((db) -> (db.getKnownFilesType() == HashDb.KnownFilesType.KNOWN)).forEach((db) -> { this.hashSets.stream().filter((db) -> (db.getKnownFilesType() == HashDb.KnownFilesType.KNOWN)).forEach((db) -> {
@ -450,9 +450,9 @@ public class HashDbManager implements PropertyChangeListener {
*/ */
public synchronized List<HashDb> getKnownBadFileHashSets() { public synchronized List<HashDb> getKnownBadFileHashSets() {
List<HashDb> hashDbs = new ArrayList<>(); List<HashDb> hashDbs = new ArrayList<>();
try{ try {
updateHashSetsFromCentralRepository(); updateHashSetsFromCentralRepository();
} catch (TskCoreException ex){ } catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
} }
this.hashSets.stream().filter((db) -> (db.getKnownFilesType() == HashDb.KnownFilesType.KNOWN_BAD)).forEach((db) -> { this.hashSets.stream().filter((db) -> (db.getKnownFilesType() == HashDb.KnownFilesType.KNOWN_BAD)).forEach((db) -> {
@ -472,9 +472,9 @@ public class HashDbManager implements PropertyChangeListener {
private List<HashDb> getUpdateableHashSets(List<HashDb> hashDbs) { private List<HashDb> getUpdateableHashSets(List<HashDb> hashDbs) {
ArrayList<HashDb> updateableDbs = new ArrayList<>(); ArrayList<HashDb> updateableDbs = new ArrayList<>();
try{ try {
updateHashSetsFromCentralRepository(); updateHashSetsFromCentralRepository();
} catch (TskCoreException ex){ } catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
} }
for (HashDb db : hashDbs) { for (HashDb db : hashDbs) {
@ -488,34 +488,27 @@ public class HashDbManager implements PropertyChangeListener {
} }
return updateableDbs; return updateableDbs;
} }
private List<HashDbInfo> getCentralRepoHashSetsFromDatabase(){ private List<HashDbInfo> getCentralRepoHashSetsFromDatabase() {
List<HashDbInfo> crHashSets = new ArrayList<>(); List<HashDbInfo> crHashSets = new ArrayList<>();
if(CentralRepository.isEnabled()){ if (CentralRepository.isEnabled()) {
try{ try {
List<CentralRepoFileSet> crSets = CentralRepository.getInstance().getAllReferenceSets(CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID)); List<CentralRepoFileSet> crSets = CentralRepository.getInstance().getAllReferenceSets(CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID));
for(CentralRepoFileSet globalSet:crSets){ for (CentralRepoFileSet globalSet : crSets) {
// Defaults for fields not stored in the central repository: // Defaults for fields not stored in the central repository:
// searchDuringIngest: false // searchDuringIngest: false
// sendIngestMessages: true if the hash set is notable // sendIngestMessages: true if the hash set is notable
boolean sendIngestMessages = convertFileKnown(globalSet.getFileKnownStatus()).equals(HashDb.KnownFilesType.KNOWN_BAD); boolean sendIngestMessages = KnownFilesType.fromFileKnown(globalSet.getFileKnownStatus()).equals(HashDb.KnownFilesType.KNOWN_BAD);
crHashSets.add(new HashDbInfo(globalSet.getSetName(), globalSet.getVersion(), crHashSets.add(new HashDbInfo(globalSet.getSetName(), globalSet.getVersion(),
globalSet.getGlobalSetID(), convertFileKnown(globalSet.getFileKnownStatus()), globalSet.isReadOnly(), false, sendIngestMessages)); globalSet.getGlobalSetID(), KnownFilesType.fromFileKnown(globalSet.getFileKnownStatus()), globalSet.isReadOnly(), false, sendIngestMessages));
} }
} catch (CentralRepoException ex){ } catch (CentralRepoException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
} }
} }
return crHashSets; return crHashSets;
} }
private static HashDb.KnownFilesType convertFileKnown(TskData.FileKnown fileKnown){
if(fileKnown.equals(TskData.FileKnown.BAD)){
return HashDb.KnownFilesType.KNOWN_BAD;
}
return HashDb.KnownFilesType.KNOWN;
}
/** /**
* Restores the last saved hash sets configuration. This supports * Restores the last saved hash sets configuration. This supports
@ -531,9 +524,9 @@ public class HashDbManager implements PropertyChangeListener {
private void closeHashDatabases(List<HashDb> hashDatabases) { private void closeHashDatabases(List<HashDb> hashDatabases) {
for (HashDb database : hashDatabases) { for (HashDb database : hashDatabases) {
if(database instanceof SleuthkitHashSet){ if (database instanceof SleuthkitHashSet) {
try { try {
((SleuthkitHashSet)database).close(); ((SleuthkitHashSet) database).close();
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error closing " + database.getHashSetName() + " hash set", ex); //NON-NLS Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error closing " + database.getHashSetName() + " hash set", ex); //NON-NLS
} }
@ -558,13 +551,13 @@ public class HashDbManager implements PropertyChangeListener {
* @param settings The settings to configure. * @param settings The settings to configure.
*/ */
@Messages({"# {0} - hash set name", "HashDbManager.noDbPath.message=Couldn't get valid hash set path for: {0}", @Messages({"# {0} - hash set name", "HashDbManager.noDbPath.message=Couldn't get valid hash set path for: {0}",
"HashDbManager.centralRepoLoadError.message=Error loading central repository hash sets"}) "HashDbManager.centralRepoLoadError.message=Error loading central repository hash sets"})
private void configureSettings(HashLookupSettings settings) { private void configureSettings(HashLookupSettings settings) {
allDatabasesLoadedCorrectly = true; allDatabasesLoadedCorrectly = true;
List<HashDbInfo> hashDbInfoList = settings.getHashDbInfo(); List<HashDbInfo> hashDbInfoList = settings.getHashDbInfo();
for (HashDbInfo hashDbInfo : hashDbInfoList) { for (HashDbInfo hashDbInfo : hashDbInfoList) {
try { try {
if(hashDbInfo.isFileDatabaseType()){ if (hashDbInfo.isFileDatabaseType()) {
String dbPath = this.getValidFilePath(hashDbInfo.getHashSetName(), hashDbInfo.getPath()); String dbPath = this.getValidFilePath(hashDbInfo.getHashSetName(), hashDbInfo.getPath());
if (dbPath != null) { if (dbPath != null) {
addHashDatabase(SleuthkitJNI.openHashDatabase(dbPath), hashDbInfo.getHashSetName(), hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(), hashDbInfo.getKnownFilesType()); addHashDatabase(SleuthkitJNI.openHashDatabase(dbPath), hashDbInfo.getHashSetName(), hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(), hashDbInfo.getKnownFilesType());
@ -573,10 +566,10 @@ public class HashDbManager implements PropertyChangeListener {
allDatabasesLoadedCorrectly = false; allDatabasesLoadedCorrectly = false;
} }
} else { } else {
if(CentralRepository.isEnabled()){ if (CentralRepository.isEnabled()) {
addExistingCentralRepoHashSet(hashDbInfo.getHashSetName(), hashDbInfo.getVersion(), addExistingCentralRepoHashSet(hashDbInfo.getHashSetName(), hashDbInfo.getVersion(),
hashDbInfo.getReferenceSetID(), hashDbInfo.getReferenceSetID(),
hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(), hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(),
hashDbInfo.getKnownFilesType(), hashDbInfo.isReadOnly()); hashDbInfo.getKnownFilesType(), hashDbInfo.isReadOnly());
} }
} }
@ -590,13 +583,13 @@ public class HashDbManager implements PropertyChangeListener {
allDatabasesLoadedCorrectly = false; allDatabasesLoadedCorrectly = false;
} }
} }
if(CentralRepository.isEnabled()){ if (CentralRepository.isEnabled()) {
try{ try {
updateHashSetsFromCentralRepository(); updateHashSetsFromCentralRepository();
} catch (TskCoreException ex){ } catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash set", ex); //NON-NLS Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash set", ex); //NON-NLS
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
Bundle.HashDbManager_centralRepoLoadError_message(), Bundle.HashDbManager_centralRepoLoadError_message(),
NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"), NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"),
@ -604,14 +597,17 @@ public class HashDbManager implements PropertyChangeListener {
allDatabasesLoadedCorrectly = false; allDatabasesLoadedCorrectly = false;
} }
} }
/* NOTE: When RuntimeProperties.coreComponentsAreActive() is "false", /*
I don't think we should overwrite hash db settings file because we * NOTE: When RuntimeProperties.coreComponentsAreActive() is "false", I
were unable to load a database. The user should have to fix the issue or * don't think we should overwrite hash db settings file because we were
remove the database from settings. Overwiting the settings effectively removes * unable to load a database. The user should have to fix the issue or
the database from HashLookupSettings and the user may not know about this * remove the database from settings. Overwiting the settings
because the dialogs are not being displayed. The next time user starts Autopsy, HashDB * effectively removes the database from HashLookupSettings and the user
will load without errors and the user may think that the problem was solved.*/ * may not know about this because the dialogs are not being displayed.
* The next time user starts Autopsy, HashDB will load without errors
* and the user may think that the problem was solved.
*/
if (!allDatabasesLoadedCorrectly && RuntimeProperties.runningWithGUI()) { if (!allDatabasesLoadedCorrectly && RuntimeProperties.runningWithGUI()) {
try { try {
HashLookupSettings.writeSettings(new HashLookupSettings(HashLookupSettings.convertHashSetList(this.hashSets))); HashLookupSettings.writeSettings(new HashLookupSettings(HashLookupSettings.convertHashSetList(this.hashSets)));
@ -622,31 +618,31 @@ public class HashDbManager implements PropertyChangeListener {
} }
} }
} }
private void updateHashSetsFromCentralRepository() throws TskCoreException { private void updateHashSetsFromCentralRepository() throws TskCoreException {
if(CentralRepository.isEnabled()){ if (CentralRepository.isEnabled()) {
List<HashDbInfo> crHashDbInfoList = getCentralRepoHashSetsFromDatabase(); List<HashDbInfo> crHashDbInfoList = getCentralRepoHashSetsFromDatabase();
for(HashDbInfo hashDbInfo : crHashDbInfoList) { for (HashDbInfo hashDbInfo : crHashDbInfoList) {
if(hashDbInfoIsNew(hashDbInfo)){ if (hashDbInfoIsNew(hashDbInfo)) {
addExistingCentralRepoHashSet(hashDbInfo.getHashSetName(), hashDbInfo.getVersion(), addExistingCentralRepoHashSet(hashDbInfo.getHashSetName(), hashDbInfo.getVersion(),
hashDbInfo.getReferenceSetID(), hashDbInfo.getReferenceSetID(),
hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(), hashDbInfo.getKnownFilesType(), hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(), hashDbInfo.getKnownFilesType(),
hashDbInfo.isReadOnly()); hashDbInfo.isReadOnly());
} }
} }
} }
} }
private boolean hashDbInfoIsNew(HashDbInfo dbInfo){ private boolean hashDbInfoIsNew(HashDbInfo dbInfo) {
for(HashDb db:this.hashSets){ for (HashDb db : this.hashSets) {
if(dbInfo.matches(db)){ if (dbInfo.matches(db)) {
return false; return false;
} }
} }
return true; return true;
} }
private String getValidFilePath(String hashSetName, String configuredPath) { private String getValidFilePath(String hashSetName, String configuredPath) {
// Check the configured path. // Check the configured path.
File database = new File(configuredPath); File database = new File(configuredPath);
if (database.exists()) { if (database.exists()) {
@ -655,12 +651,12 @@ public class HashDbManager implements PropertyChangeListener {
// Give the user an opportunity to find the desired file. // Give the user an opportunity to find the desired file.
String newPath = null; String newPath = null;
if (RuntimeProperties.runningWithGUI() && if (RuntimeProperties.runningWithGUI()
JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(), && JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
NbBundle.getMessage(this.getClass(), "HashDbManager.dlgMsg.dbNotFoundAtLoc", NbBundle.getMessage(this.getClass(), "HashDbManager.dlgMsg.dbNotFoundAtLoc",
hashSetName, configuredPath), hashSetName, configuredPath),
NbBundle.getMessage(this.getClass(), "HashDbManager.dlgTitle.MissingDb"), NbBundle.getMessage(this.getClass(), "HashDbManager.dlgTitle.MissingDb"),
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
newPath = searchForFile(); newPath = searchForFile();
if (null != newPath && !newPath.isEmpty()) { if (null != newPath && !newPath.isEmpty()) {
database = new File(newPath); database = new File(newPath);
@ -692,26 +688,89 @@ public class HashDbManager implements PropertyChangeListener {
} }
return filePath; return filePath;
} }
public static abstract class HashDb { public static abstract class HashDb {
/** /**
* Indicates how files with hashes stored in a particular hash database * Indicates how files with hashes stored in a particular hash database
* object should be classified. * object should be classified.
*/ */
@Messages({
"HashDbManager.noChange.text=No Change",
"HashDbManager.known.text=Known",
"HashDbManager.knownBad.text=Notable"
})
public enum KnownFilesType { public enum KnownFilesType {
KNOWN(NbBundle.getMessage(HashDbManager.class, "HashDbManager.known.text")), KNOWN(Bundle.HashDbManager_known_text(), TskData.FileKnown.KNOWN, false, false),
KNOWN_BAD(NbBundle.getMessage(HashDbManager.class, "HashDbManager.knownBad.text")); KNOWN_BAD(Bundle.HashDbManager_knownBad_text(), TskData.FileKnown.BAD, true, true),
private final String displayName; NO_CHANGE(Bundle.HashDbManager_noChange_text(), TskData.FileKnown.UNKNOWN, true, false);
private KnownFilesType(String displayName) { private final String displayName;
private final TskData.FileKnown fileKnown;
private final boolean allowSendInboxMessages;
private final boolean defaultSendInboxMessages;
KnownFilesType(String displayName, TskData.FileKnown fileKnown, boolean allowSendInboxMessages, boolean defaultSendInboxMessages) {
this.displayName = displayName; this.displayName = displayName;
this.fileKnown = fileKnown;
this.allowSendInboxMessages = allowSendInboxMessages;
this.defaultSendInboxMessages = defaultSendInboxMessages;
}
/**
* Returns whether or not it is allowable to send inbox messages
* with this known files type.
*
* @return Whether or not it is allowable to send inbox messages
* with this known files type.
*/
boolean isInboxMessagesAllowed() {
return allowSendInboxMessages;
}
/**
* Returns whether or not by default for this type is to send inbox
* messages.
*
* @return Whether or not by default for this type is to send inbox
* messages.
*/
boolean isDefaultInboxMessages() {
return defaultSendInboxMessages;
} }
public String getDisplayName() { public String getDisplayName() {
return this.displayName; return this.displayName;
} }
/**
* Retrieves the corresponding TskData.FileKnown enum type that
* relates to this.
*
* @return The corresponding TskData.FileKnown.
*/
TskData.FileKnown getFileKnown() {
return this.fileKnown;
}
/**
* Converts a TskData.FileKnown to the corresponding KnownFilesType.
*
* @param fileKnown The TskData.FileKnown type.
*
* @return The corresponding KnownFilesType.
*/
static KnownFilesType fromFileKnown(TskData.FileKnown fileKnown) {
if (fileKnown == null) {
return null;
}
return Stream.of(KnownFilesType.values())
.filter((type) -> type.getFileKnown() == fileKnown)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unknown TskData.FileKnown type: " + fileKnown));
}
} }
/** /**
@ -721,9 +780,9 @@ public class HashDbManager implements PropertyChangeListener {
INDEXING_DONE INDEXING_DONE
} }
public abstract String getHashSetName(); public abstract String getHashSetName();
abstract String getDisplayName(); abstract String getDisplayName();
public abstract String getDatabasePath() throws TskCoreException; public abstract String getDatabasePath() throws TskCoreException;
@ -731,7 +790,7 @@ public class HashDbManager implements PropertyChangeListener {
public abstract HashDb.KnownFilesType getKnownFilesType(); public abstract HashDb.KnownFilesType getKnownFilesType();
public abstract boolean getSearchDuringIngest(); public abstract boolean getSearchDuringIngest();
abstract void setSearchDuringIngest(boolean useForIngest); abstract void setSearchDuringIngest(boolean useForIngest);
public abstract boolean getSendIngestMessages(); public abstract boolean getSendIngestMessages();
@ -764,28 +823,30 @@ public class HashDbManager implements PropertyChangeListener {
public abstract boolean lookupMD5Quick(Content content) throws TskCoreException; public abstract boolean lookupMD5Quick(Content content) throws TskCoreException;
public abstract HashHitInfo lookupMD5(Content content) throws TskCoreException; public abstract HashHitInfo lookupMD5(Content content) throws TskCoreException;
/** /**
* Returns whether this database can be enabled. * Returns whether this database can be enabled. For file type, this is
* For file type, this is the same as checking that it has an index * the same as checking that it has an index
*
* @return true if is valid, false otherwise * @return true if is valid, false otherwise
* @throws TskCoreException *
* @throws TskCoreException
*/ */
abstract boolean isValid() throws TskCoreException; abstract boolean isValid() throws TskCoreException;
public abstract String getIndexPath() throws TskCoreException; public abstract String getIndexPath() throws TskCoreException;
public abstract boolean hasIndexOnly() throws TskCoreException; public abstract boolean hasIndexOnly() throws TskCoreException;
public abstract void firePropertyChange(String propertyName, Object oldValue, Object newValue); public abstract void firePropertyChange(String propertyName, Object oldValue, Object newValue);
public abstract void addPropertyChangeListener(PropertyChangeListener pcl); public abstract void addPropertyChangeListener(PropertyChangeListener pcl);
public abstract void removePropertyChangeListener(PropertyChangeListener pcl); public abstract void removePropertyChangeListener(PropertyChangeListener pcl);
@Override @Override
public abstract String toString(); public abstract String toString();
} }
/** /**
@ -793,13 +854,13 @@ public class HashDbManager implements PropertyChangeListener {
* as known or know bad. * as known or know bad.
*/ */
class SleuthkitHashSet extends HashDb { class SleuthkitHashSet extends HashDb {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final int handle; private final int handle;
private final String hashSetName; private final String hashSetName;
private boolean searchDuringIngest; private boolean searchDuringIngest;
private boolean sendIngestMessages; private boolean sendIngestMessages;
private final HashDb.KnownFilesType knownFilesType; private final HashDb.KnownFilesType knownFilesType;
private boolean indexing; private boolean indexing;
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
@ -813,8 +874,8 @@ public class HashDbManager implements PropertyChangeListener {
} }
/** /**
* Adds a listener for the events defined in HashDb.Event. * Adds a listener for the events defined in HashDb.Event. Listeners are
* Listeners are used during indexing. * used during indexing.
* *
* @param pcl * @param pcl
*/ */
@ -832,8 +893,8 @@ public class HashDbManager implements PropertyChangeListener {
public void removePropertyChangeListener(PropertyChangeListener pcl) { public void removePropertyChangeListener(PropertyChangeListener pcl) {
propertyChangeSupport.removePropertyChangeListener(pcl); propertyChangeSupport.removePropertyChangeListener(pcl);
} }
int getHandle(){ int getHandle() {
return handle; return handle;
} }
@ -841,9 +902,9 @@ public class HashDbManager implements PropertyChangeListener {
public String getHashSetName() { public String getHashSetName() {
return hashSetName; return hashSetName;
} }
@Override @Override
String getDisplayName(){ String getDisplayName() {
return getHashSetName(); return getHashSetName();
} }
@ -851,9 +912,9 @@ public class HashDbManager implements PropertyChangeListener {
public String getDatabasePath() throws TskCoreException { public String getDatabasePath() throws TskCoreException {
return SleuthkitJNI.getHashDatabasePath(handle); return SleuthkitJNI.getHashDatabasePath(handle);
} }
public void setIndexing(boolean indexing){ public void setIndexing(boolean indexing) {
this.indexing = indexing; this.indexing = indexing;
} }
@Override @Override
@ -989,12 +1050,14 @@ public class HashDbManager implements PropertyChangeListener {
} }
return result; return result;
} }
/** /**
* Returns whether this database can be enabled. * Returns whether this database can be enabled. For file type, this is
* For file type, this is the same as checking that it has an index * the same as checking that it has an index
*
* @return true if is valid, false otherwise * @return true if is valid, false otherwise
* @throws TskCoreException *
* @throws TskCoreException
*/ */
@Override @Override
boolean isValid() throws TskCoreException { boolean isValid() throws TskCoreException {
@ -1017,21 +1080,20 @@ public class HashDbManager implements PropertyChangeListener {
boolean isIndexing() { boolean isIndexing() {
return indexing; return indexing;
} }
@Override @Override
public void firePropertyChange(String propertyName, Object oldValue, Object newValue){ public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue); this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
} }
private void close() throws TskCoreException { private void close() throws TskCoreException {
SleuthkitJNI.closeHashDatabase(handle); SleuthkitJNI.closeHashDatabase(handle);
} }
@Override @Override
public String toString(){ public String toString() {
return getHashSetName(); return getHashSetName();
} }
@Override @Override
public int hashCode() { public int hashCode() {
@ -1066,13 +1128,13 @@ public class HashDbManager implements PropertyChangeListener {
* Instances of this class represent hash databases used to classify files * Instances of this class represent hash databases used to classify files
* as known or know bad. * as known or know bad.
*/ */
class CentralRepoHashSet extends HashDb{ class CentralRepoHashSet extends HashDb {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final String hashSetName; private final String hashSetName;
private boolean searchDuringIngest; private boolean searchDuringIngest;
private boolean sendIngestMessages; private boolean sendIngestMessages;
private final HashDb.KnownFilesType knownFilesType; private final HashDb.KnownFilesType knownFilesType;
private final int referenceSetID; private final int referenceSetID;
private final String version; private final String version;
private String orgName; private String orgName;
@ -1080,10 +1142,10 @@ public class HashDbManager implements PropertyChangeListener {
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
@Messages({"HashDbManager.CentralRepoHashDb.orgError=Error loading organization"}) @Messages({"HashDbManager.CentralRepoHashDb.orgError=Error loading organization"})
private CentralRepoHashSet(String hashSetName, String version, int referenceSetID, private CentralRepoHashSet(String hashSetName, String version, int referenceSetID,
boolean useForIngest, boolean sendHitMessages, HashDb.KnownFilesType knownFilesType, boolean useForIngest, boolean sendHitMessages, HashDb.KnownFilesType knownFilesType,
boolean readOnly) boolean readOnly)
throws TskCoreException{ throws TskCoreException {
this.hashSetName = hashSetName; this.hashSetName = hashSetName;
this.version = version; this.version = version;
this.referenceSetID = referenceSetID; this.referenceSetID = referenceSetID;
@ -1091,18 +1153,18 @@ public class HashDbManager implements PropertyChangeListener {
this.sendIngestMessages = sendHitMessages; this.sendIngestMessages = sendHitMessages;
this.knownFilesType = knownFilesType; this.knownFilesType = knownFilesType;
this.readOnly = readOnly; this.readOnly = readOnly;
try{ try {
orgName = CentralRepository.getInstance().getReferenceSetOrganization(referenceSetID).getName(); orgName = CentralRepository.getInstance().getReferenceSetOrganization(referenceSetID).getName();
} catch (CentralRepoException ex){ } catch (CentralRepoException ex) {
Logger.getLogger(SleuthkitHashSet.class.getName()).log(Level.SEVERE, "Error looking up central repository organization for reference set " + referenceSetID, ex); //NON-NLS Logger.getLogger(SleuthkitHashSet.class.getName()).log(Level.SEVERE, "Error looking up central repository organization for reference set " + referenceSetID, ex); //NON-NLS
orgName = Bundle.HashDbManager_CentralRepoHashDb_orgError(); orgName = Bundle.HashDbManager_CentralRepoHashDb_orgError();
} }
} }
/** /**
* Adds a listener for the events defined in HashDb.Event. * Adds a listener for the events defined in HashDb.Event. Listeners are
* Listeners are used during indexing. * used during indexing.
* *
* @param pcl * @param pcl
*/ */
@ -1120,9 +1182,9 @@ public class HashDbManager implements PropertyChangeListener {
public void removePropertyChangeListener(PropertyChangeListener pcl) { public void removePropertyChangeListener(PropertyChangeListener pcl) {
propertyChangeSupport.removePropertyChangeListener(pcl); propertyChangeSupport.removePropertyChangeListener(pcl);
} }
@Override @Override
public boolean hasIndexOnly() throws TskCoreException{ public boolean hasIndexOnly() throws TskCoreException {
return true; return true;
} }
@ -1130,25 +1192,25 @@ public class HashDbManager implements PropertyChangeListener {
public String getHashSetName() { public String getHashSetName() {
return hashSetName; return hashSetName;
} }
@Override @Override
public String getDisplayName(){ public String getDisplayName() {
if(! getVersion().isEmpty()){ if (!getVersion().isEmpty()) {
return getHashSetName() + " " + getVersion() + " (remote)"; return getHashSetName() + " " + getVersion() + " (remote)";
} else { } else {
return getHashSetName() + " (remote)"; return getHashSetName() + " (remote)";
} }
} }
String getVersion(){ String getVersion() {
return version; return version;
} }
String getOrgName(){ String getOrgName() {
return orgName; return orgName;
} }
int getReferenceSetID(){ int getReferenceSetID() {
return referenceSetID; return referenceSetID;
} }
@ -1196,7 +1258,7 @@ public class HashDbManager implements PropertyChangeListener {
*/ */
@Override @Override
public boolean isUpdateable() throws TskCoreException { public boolean isUpdateable() throws TskCoreException {
return (! readOnly); return (!readOnly);
} }
/** /**
@ -1229,18 +1291,13 @@ public class HashDbManager implements PropertyChangeListener {
if (content instanceof AbstractFile) { if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile) content; AbstractFile file = (AbstractFile) content;
if (null != file.getMd5Hash()) { if (null != file.getMd5Hash()) {
TskData.FileKnown type; TskData.FileKnown type = knownFilesType.getFileKnown();
if(knownFilesType.equals(HashDb.KnownFilesType.KNOWN_BAD)){
type = TskData.FileKnown.BAD; try {
} else {
type = TskData.FileKnown.KNOWN;
}
try{
CentralRepoFileInstance fileInstance = new CentralRepoFileInstance(referenceSetID, file.getMd5Hash(), CentralRepoFileInstance fileInstance = new CentralRepoFileInstance(referenceSetID, file.getMd5Hash(),
type, comment); type, comment);
CentralRepository.getInstance().addReferenceInstance(fileInstance,CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID)); CentralRepository.getInstance().addReferenceInstance(fileInstance, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID));
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex){ } catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex); //NON-NLS throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex); //NON-NLS
} }
} }
@ -1257,24 +1314,20 @@ public class HashDbManager implements PropertyChangeListener {
@Override @Override
public void addHashes(List<HashEntry> hashes) throws TskCoreException { public void addHashes(List<HashEntry> hashes) throws TskCoreException {
Set<CentralRepoFileInstance> globalFileInstances = new HashSet<>(); Set<CentralRepoFileInstance> globalFileInstances = new HashSet<>();
for(HashEntry hashEntry:hashes){ for (HashEntry hashEntry : hashes) {
TskData.FileKnown type; TskData.FileKnown type = knownFilesType.getFileKnown();
if(knownFilesType.equals(HashDb.KnownFilesType.KNOWN_BAD)){
type = TskData.FileKnown.BAD;
} else {
type = TskData.FileKnown.KNOWN;
}
try { try {
globalFileInstances.add(new CentralRepoFileInstance(referenceSetID, hashEntry.getMd5Hash(), type, hashEntry.getComment())); globalFileInstances.add(new CentralRepoFileInstance(referenceSetID, hashEntry.getMd5Hash(), type, hashEntry.getComment()));
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex){ } catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex); throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex);
} }
} }
try{ try {
CentralRepository.getInstance().bulkInsertReferenceTypeEntries(globalFileInstances, CentralRepository.getInstance().bulkInsertReferenceTypeEntries(globalFileInstances,
CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID)); CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID));
} catch (CentralRepoException ex){ } catch (CentralRepoException ex) {
throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex); throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex);
} }
} }
@ -1295,9 +1348,9 @@ public class HashDbManager implements PropertyChangeListener {
if (content instanceof AbstractFile) { if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile) content; AbstractFile file = (AbstractFile) content;
if (null != file.getMd5Hash()) { if (null != file.getMd5Hash()) {
try{ try {
return CentralRepository.getInstance().isFileHashInReferenceSet(file.getMd5Hash(), this.referenceSetID); return CentralRepository.getInstance().isFileHashInReferenceSet(file.getMd5Hash(), this.referenceSetID);
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex){ } catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
Logger.getLogger(SleuthkitHashSet.class.getName()).log(Level.SEVERE, "Error performing central reposiotry hash lookup for hash " Logger.getLogger(SleuthkitHashSet.class.getName()).log(Level.SEVERE, "Error performing central reposiotry hash lookup for hash "
+ file.getMd5Hash() + " in reference set " + referenceSetID, ex); //NON-NLS + file.getMd5Hash() + " in reference set " + referenceSetID, ex); //NON-NLS
throw new TskCoreException("Error performing central reposiotry hash lookup", ex); throw new TskCoreException("Error performing central reposiotry hash lookup", ex);
@ -1324,12 +1377,9 @@ public class HashDbManager implements PropertyChangeListener {
if (content instanceof AbstractFile) { if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile) content; AbstractFile file = (AbstractFile) content;
if (null != file.getMd5Hash()) { if (null != file.getMd5Hash()) {
try{ try {
if(CentralRepository.getInstance().isFileHashInReferenceSet(file.getMd5Hash(), this.referenceSetID)){ return CentralRepository.getInstance().lookupHash(file.getMd5Hash(), referenceSetID);
// Make a bare-bones HashHitInfo for now } catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
result = new HashHitInfo(file.getMd5Hash(), "", "");
}
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex){
Logger.getLogger(SleuthkitHashSet.class.getName()).log(Level.SEVERE, "Error performing central reposiotry hash lookup for hash " Logger.getLogger(SleuthkitHashSet.class.getName()).log(Level.SEVERE, "Error performing central reposiotry hash lookup for hash "
+ file.getMd5Hash() + " in reference set " + referenceSetID, ex); //NON-NLS + file.getMd5Hash() + " in reference set " + referenceSetID, ex); //NON-NLS
throw new TskCoreException("Error performing central reposiotry hash lookup", ex); throw new TskCoreException("Error performing central reposiotry hash lookup", ex);
@ -1338,35 +1388,34 @@ public class HashDbManager implements PropertyChangeListener {
} }
return result; return result;
} }
/** /**
* Returns whether this database can be enabled. * Returns whether this database can be enabled.
* *
* @return true if is valid, false otherwise * @return true if is valid, false otherwise
*/ */
@Override @Override
boolean isValid() { boolean isValid() {
if(! CentralRepository.isEnabled()) { if (!CentralRepository.isEnabled()) {
return false; return false;
} }
try{ try {
return CentralRepository.getInstance().referenceSetIsValid(this.referenceSetID, this.hashSetName, this.version); return CentralRepository.getInstance().referenceSetIsValid(this.referenceSetID, this.hashSetName, this.version);
} catch (CentralRepoException ex){ } catch (CentralRepoException ex) {
Logger.getLogger(CentralRepoHashSet.class.getName()).log(Level.SEVERE, "Error validating hash set " + hashSetName, ex); //NON-NLS Logger.getLogger(CentralRepoHashSet.class.getName()).log(Level.SEVERE, "Error validating hash set " + hashSetName, ex); //NON-NLS
return false; return false;
} }
} }
@Override @Override
public void firePropertyChange(String propertyName, Object oldValue, Object newValue){ public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue); this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
} }
@Override @Override
public String toString(){ public String toString() {
return getDisplayName(); return getDisplayName();
} }
@Override @Override
public int hashCode() { public int hashCode() {
@ -1398,8 +1447,8 @@ public class HashDbManager implements PropertyChangeListener {
} }
return true; return true;
} }
} }
/** /**
* Worker thread to make an index of a database * Worker thread to make an index of a database
*/ */

View File

@ -25,16 +25,12 @@
<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" attributes="0"> <Group type="102" attributes="0">
<Component id="knownHashDbsLabel" min="-2" max="-2" attributes="0"/> <Component id="hashDbsLabel" 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="knownBadHashDbsLabel" pref="290" max="32767" 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"> <Component id="hashDbsScrollPane" pref="494" max="32767" attributes="1"/>
<Component id="jScrollPane1" pref="0" max="32767" attributes="1"/>
<Component id="jScrollPane2" pref="0" max="32767" attributes="1"/>
</Group>
</Group> </Group>
<Component id="alwaysCalcHashesCheckbox" alignment="0" max="32767" attributes="0"/> <Component id="alwaysCalcHashesCheckbox" alignment="0" max="32767" attributes="0"/>
</Group> </Group>
@ -46,22 +42,25 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/> <EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Component id="knownHashDbsLabel" min="-2" max="-2" attributes="0"/> <Component id="hashDbsLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="29" max="32767" attributes="0"/> <Component id="hashDbsScrollPane" pref="207" max="32767" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/> <EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Component id="knownBadHashDbsLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane2" pref="29" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="alwaysCalcHashesCheckbox" min="-2" max="-2" attributes="0"/> <Component id="alwaysCalcHashesCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
</Layout> </Layout>
<SubComponents> <SubComponents>
<Container class="javax.swing.JScrollPane" name="jScrollPane1"> <Component class="javax.swing.JLabel" name="hashDbsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="HashLookupModuleSettingsPanel.hashDbsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="hashDbsScrollPane">
<Properties> <Properties>
<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="org.netbeans.modules.form.compat2.border.EtchedBorderInfo"> <Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
@ -75,7 +74,7 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Component class="javax.swing.JTable" name="knownHashTable"> <Component class="javax.swing.JTable" name="hashTable">
<Properties> <Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor"> <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="f0" green="f0" red="f0" type="rgb"/> <Color blue="f0" green="f0" red="f0" type="rgb"/>
@ -86,20 +85,6 @@
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="javax.swing.JLabel" name="knownBadHashDbsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="HashLookupModuleSettingsPanel.knownBadHashDbsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="knownHashDbsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="HashLookupModuleSettingsPanel.knownHashDbsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="alwaysCalcHashesCheckbox"> <Component class="javax.swing.JCheckBox" name="alwaysCalcHashesCheckbox">
<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">
@ -121,39 +106,5 @@
<Property name="verticalTextPosition" type="int" value="1"/> <Property name="verticalTextPosition" type="int" value="1"/>
</Properties> </Properties>
</Component> </Component>
<Container class="javax.swing.JScrollPane" name="jScrollPane2">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</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.JTable" name="knownBadHashTable">
<Properties>
<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="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
<Table columnCount="0" rowCount="0"/>
</Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0"/>
</Property>
<Property name="showHorizontalLines" type="boolean" value="false"/>
<Property name="showVerticalLines" type="boolean" value="false"/>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="true" resizingAllowed="true"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -1,15 +1,15 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 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");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -27,13 +27,13 @@ import javax.swing.JScrollPane;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn; import javax.swing.table.TableColumn;
import org.apache.commons.lang.StringUtils;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
/** /**
* Ingest job settings panel for hash lookup file ingest modules. * Ingest job settings panel for hash lookup file ingest modules.
*/ */
@ -42,10 +42,8 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final HashDbManager hashDbManager = HashDbManager.getInstance(); private final HashDbManager hashDbManager = HashDbManager.getInstance();
private final List<HashSetModel> knownHashSetModels = new ArrayList<>(); private final List<HashSetModel> hashSetModels = new ArrayList<>();
private final HashSetsTableModel knownHashSetsTableModel = new HashSetsTableModel(knownHashSetModels); private final HashSetsTableModel hashSetsTableModel = new HashSetsTableModel(hashSetModels);
private final List<HashSetModel> knownBadHashSetModels = new ArrayList<>();
private final HashSetsTableModel knownBadHashSetsTableModel = new HashSetsTableModel(knownBadHashSetModels);
HashLookupModuleSettingsPanel(HashLookupModuleSettings settings) { HashLookupModuleSettingsPanel(HashLookupModuleSettings settings) {
initializeHashSetModels(settings); initializeHashSetModels(settings);
@ -54,11 +52,7 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
} }
private void initializeHashSetModels(HashLookupModuleSettings settings) { private void initializeHashSetModels(HashLookupModuleSettings settings) {
initializeHashSetModels(settings, validSetsOnly(hashDbManager.getKnownFileHashSets()), knownHashSetModels); List<HashDb> hashDbs = validSetsOnly(hashDbManager.getAllHashSets());
initializeHashSetModels(settings, validSetsOnly(hashDbManager.getKnownBadFileHashSets()), knownBadHashSetModels);
}
private void initializeHashSetModels(HashLookupModuleSettings settings, List<HashDb> hashDbs, List<HashSetModel> hashSetModels) {
hashSetModels.clear(); hashSetModels.clear();
for (HashDb db : hashDbs) { for (HashDb db : hashDbs) {
hashSetModels.add(new HashSetModel(db, settings.isHashSetEnabled(db), isHashDbValid(db))); hashSetModels.add(new HashSetModel(db, settings.isHashSetEnabled(db), isHashDbValid(db)));
@ -66,8 +60,7 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
} }
private void customizeComponents(HashLookupModuleSettings settings) { private void customizeComponents(HashLookupModuleSettings settings) {
customizeHashSetsTable(jScrollPane1, knownHashTable, knownHashSetsTableModel); customizeHashSetsTable(hashDbsScrollPane, hashTable, hashSetsTableModel);
customizeHashSetsTable(jScrollPane2, knownBadHashTable, knownBadHashSetsTableModel);
alwaysCalcHashesCheckbox.setSelected(settings.shouldCalculateHashes()); alwaysCalcHashesCheckbox.setSelected(settings.shouldCalculateHashes());
hashDbManager.addPropertyChangeListener(this); hashDbManager.addPropertyChangeListener(this);
alwaysCalcHashesCheckbox.setText("<html>" + org.openide.util.NbBundle.getMessage(HashLookupModuleSettingsPanel.class, "HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.text") + "</html>"); // NOI18N NON-NLS alwaysCalcHashesCheckbox.setText("<html>" + org.openide.util.NbBundle.getMessage(HashLookupModuleSettingsPanel.class, "HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.text") + "</html>"); // NOI18N NON-NLS
@ -78,7 +71,7 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
table.setTableHeader(null); table.setTableHeader(null);
table.setRowSelectionAllowed(false); table.setRowSelectionAllowed(false);
final int width1 = scrollPane.getPreferredSize().width; final int width1 = scrollPane.getPreferredSize().width;
knownHashTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN); hashTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
TableColumn column; TableColumn column;
for (int i = 0; i < table.getColumnCount(); i++) { for (int i = 0; i < table.getColumnCount(); i++) {
column = table.getColumnModel().getColumn(i); column = table.getColumnModel().getColumn(i);
@ -103,8 +96,7 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
public IngestModuleIngestJobSettings getSettings() { public IngestModuleIngestJobSettings getSettings() {
List<HashDb> enabledHashSets = new ArrayList<>(); List<HashDb> enabledHashSets = new ArrayList<>();
List<HashDb> disabledHashSets = new ArrayList<>(); List<HashDb> disabledHashSets = new ArrayList<>();
addHashSets(knownHashSetModels, enabledHashSets, disabledHashSets); addHashSets(hashSetModels, enabledHashSets, disabledHashSets);
addHashSets(knownBadHashSetModels, enabledHashSets, disabledHashSets);
return new HashLookupModuleSettings(alwaysCalcHashesCheckbox.isSelected(), return new HashLookupModuleSettings(alwaysCalcHashesCheckbox.isSelected(),
enabledHashSets, disabledHashSets); enabledHashSets, disabledHashSets);
} }
@ -121,46 +113,41 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
void update() { void update() {
updateHashSetModels(); updateHashSetModels();
knownHashSetsTableModel.fireTableDataChanged(); hashSetsTableModel.fireTableDataChanged();
knownBadHashSetsTableModel.fireTableDataChanged();
} }
private void updateHashSetModels() { private List<HashDb> validSetsOnly(List<HashDb> hashDbs) {
updateHashSetModels(validSetsOnly(hashDbManager.getKnownFileHashSets()), knownHashSetModels);
updateHashSetModels(validSetsOnly(hashDbManager.getKnownBadFileHashSets()), knownBadHashSetModels);
}
private List<HashDb> validSetsOnly(List<HashDb> hashDbs){
List<HashDb> validDbs = new ArrayList<>(); List<HashDb> validDbs = new ArrayList<>();
for(HashDb db:hashDbs){ for (HashDb db : hashDbs) {
try{ try {
if(db.isValid()){ if (db.isValid()) {
validDbs.add(db); validDbs.add(db);
} }
} catch (TskCoreException ex){ } catch (TskCoreException ex) {
Logger.getLogger(HashLookupModuleSettingsPanel.class.getName()).log(Level.SEVERE, "Error checking validity for hash set (name = " + db.getHashSetName() + ")", ex); //NON-NLS Logger.getLogger(HashLookupModuleSettingsPanel.class.getName()).log(Level.SEVERE, "Error checking validity for hash set (name = " + db.getHashSetName() + ")", ex); //NON-NLS
} }
} }
return validDbs; return validDbs;
} }
void updateHashSetModels(List<HashDb> hashDbs, List<HashSetModel> hashSetModels) { void updateHashSetModels() {
List<HashDb> hashDbs = validSetsOnly(hashDbManager.getAllHashSets());
List<HashDb> hashDatabases = new ArrayList<>(hashDbs); List<HashDb> hashDatabases = new ArrayList<>(hashDbs);
// Update the hash sets and detect deletions. // Update the hash sets and detect deletions.
List<HashSetModel> deletedHashSetModels = new ArrayList<>(); List<HashSetModel> deletedHashSetModels = new ArrayList<>();
for (HashSetModel model : hashSetModels) { for (HashSetModel model : hashSetModels) {
boolean foundDatabase = false; boolean foundDatabase = false;
for(HashDb db : hashDatabases){ for (HashDb db : hashDatabases) {
if(model.getDatabase().equals(db)){ if (model.getDatabase().equals(db)) {
model.setValid(isHashDbValid(db)); model.setValid(isHashDbValid(db));
hashDatabases.remove(db); hashDatabases.remove(db);
foundDatabase = true; foundDatabase = true;
break; break;
} }
} }
if(! foundDatabase){ if (!foundDatabase) {
deletedHashSetModels.add(model); deletedHashSetModels.add(model);
} }
} }
@ -179,8 +166,7 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
void reset(HashLookupModuleSettings newSettings) { void reset(HashLookupModuleSettings newSettings) {
initializeHashSetModels(newSettings); initializeHashSetModels(newSettings);
alwaysCalcHashesCheckbox.setSelected(newSettings.shouldCalculateHashes()); alwaysCalcHashesCheckbox.setSelected(newSettings.shouldCalculateHashes());
knownHashSetsTableModel.fireTableDataChanged(); hashSetsTableModel.fireTableDataChanged();
knownBadHashSetsTableModel.fireTableDataChanged();
} }
private boolean isHashDbValid(HashDb hashDb) { private boolean isHashDbValid(HashDb hashDb) {
@ -204,8 +190,8 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
this.enabled = enabled; this.enabled = enabled;
this.valid = valid; this.valid = valid;
} }
HashDb getDatabase(){ HashDb getDatabase() {
return db; return db;
} }
@ -213,6 +199,16 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
return db.getDisplayName(); return db.getDisplayName();
} }
String getFormattedName() {
String knownTypeName = (db != null && db.getKnownFilesType() != null) ? db.getKnownFilesType().getDisplayName() : "";
if (!StringUtils.isBlank(knownTypeName)) {
knownTypeName = String.format(" (%s)", knownTypeName);
}
String displayName = db != null ? db.getDisplayName() : "";
return displayName + knownTypeName;
}
void setEnabled(boolean enabled) { void setEnabled(boolean enabled) {
this.enabled = enabled; this.enabled = enabled;
} }
@ -254,7 +250,7 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
if (columnIndex == 0) { if (columnIndex == 0) {
return hashSets.get(rowIndex).isEnabled(); return hashSets.get(rowIndex).isEnabled();
} else { } else {
return hashSets.get(rowIndex).getName(); return hashSets.get(rowIndex).getFormattedName();
} }
} }
@ -285,26 +281,21 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() { private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane(); hashDbsLabel = new javax.swing.JLabel();
knownHashTable = new javax.swing.JTable(); hashDbsScrollPane = new javax.swing.JScrollPane();
knownBadHashDbsLabel = new javax.swing.JLabel(); hashTable = new javax.swing.JTable();
knownHashDbsLabel = new javax.swing.JLabel();
alwaysCalcHashesCheckbox = new javax.swing.JCheckBox(); alwaysCalcHashesCheckbox = new javax.swing.JCheckBox();
jScrollPane2 = new javax.swing.JScrollPane();
knownBadHashTable = new javax.swing.JTable();
setPreferredSize(new java.awt.Dimension(292, 150)); setPreferredSize(new java.awt.Dimension(292, 150));
jScrollPane1.setBorder(javax.swing.BorderFactory.createEtchedBorder()); hashDbsLabel.setText(org.openide.util.NbBundle.getMessage(HashLookupModuleSettingsPanel.class, "HashLookupModuleSettingsPanel.hashDbsLabel.text")); // NOI18N
knownHashTable.setBackground(new java.awt.Color(240, 240, 240)); hashDbsScrollPane.setBorder(javax.swing.BorderFactory.createEtchedBorder());
knownHashTable.setShowHorizontalLines(false);
knownHashTable.setShowVerticalLines(false);
jScrollPane1.setViewportView(knownHashTable);
knownBadHashDbsLabel.setText(org.openide.util.NbBundle.getMessage(HashLookupModuleSettingsPanel.class, "HashLookupModuleSettingsPanel.knownBadHashDbsLabel.text")); // NOI18N hashTable.setBackground(new java.awt.Color(240, 240, 240));
hashTable.setShowHorizontalLines(false);
knownHashDbsLabel.setText(org.openide.util.NbBundle.getMessage(HashLookupModuleSettingsPanel.class, "HashLookupModuleSettingsPanel.knownHashDbsLabel.text")); // NOI18N hashTable.setShowVerticalLines(false);
hashDbsScrollPane.setViewportView(hashTable);
alwaysCalcHashesCheckbox.setText(org.openide.util.NbBundle.getMessage(HashLookupModuleSettingsPanel.class, "HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.text")); // NOI18N alwaysCalcHashesCheckbox.setText(org.openide.util.NbBundle.getMessage(HashLookupModuleSettingsPanel.class, "HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.text")); // NOI18N
alwaysCalcHashesCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(HashLookupModuleSettingsPanel.class, "HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.toolTipText")); // NOI18N alwaysCalcHashesCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(HashLookupModuleSettingsPanel.class, "HashLookupModuleSettingsPanel.alwaysCalcHashesCheckbox.toolTipText")); // NOI18N
@ -314,21 +305,6 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
alwaysCalcHashesCheckbox.setVerticalAlignment(javax.swing.SwingConstants.TOP); alwaysCalcHashesCheckbox.setVerticalAlignment(javax.swing.SwingConstants.TOP);
alwaysCalcHashesCheckbox.setVerticalTextPosition(javax.swing.SwingConstants.TOP); alwaysCalcHashesCheckbox.setVerticalTextPosition(javax.swing.SwingConstants.TOP);
jScrollPane2.setBorder(javax.swing.BorderFactory.createEtchedBorder());
knownBadHashTable.setBackground(new java.awt.Color(240, 240, 240));
knownBadHashTable.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
},
new String [] {
}
));
knownBadHashTable.setShowHorizontalLines(false);
knownBadHashTable.setShowVerticalLines(false);
jScrollPane2.setViewportView(knownBadHashTable);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
@ -337,14 +313,11 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(knownHashDbsLabel) .addComponent(hashDbsLabel)
.addGap(0, 0, Short.MAX_VALUE)) .addGap(0, 0, Short.MAX_VALUE))
.addComponent(knownBadHashDbsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 290, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(10, 10, 10) .addGap(10, 10, 10)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(hashDbsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 494, Short.MAX_VALUE))
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)))
.addComponent(alwaysCalcHashesCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addComponent(alwaysCalcHashesCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap()) .addContainerGap())
); );
@ -352,26 +325,19 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(2, 2, 2) .addGap(2, 2, 2)
.addComponent(knownHashDbsLabel) .addComponent(hashDbsLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 29, Short.MAX_VALUE) .addComponent(hashDbsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 207, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(knownBadHashDbsLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 29, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(alwaysCalcHashesCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(alwaysCalcHashesCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)) .addContainerGap())
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox alwaysCalcHashesCheckbox; private javax.swing.JCheckBox alwaysCalcHashesCheckbox;
private javax.swing.JScrollPane jScrollPane1; private javax.swing.JLabel hashDbsLabel;
private javax.swing.JScrollPane jScrollPane2; private javax.swing.JScrollPane hashDbsScrollPane;
private javax.swing.JLabel knownBadHashDbsLabel; private javax.swing.JTable hashTable;
private javax.swing.JTable knownBadHashTable;
private javax.swing.JLabel knownHashDbsLabel;
private javax.swing.JTable knownHashTable;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -49,7 +49,6 @@ import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.SleuthkitHashSet; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.SleuthkitHashSet;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.CentralRepoHashSet; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.CentralRepoHashSet;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb.KnownFilesType;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.SetEvt; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.SetEvt;
@ -95,16 +94,16 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
} }
} }
}); });
HashDbManager.getInstance().addPropertyChangeListener(new PropertyChangeListener() { HashDbManager.getInstance().addPropertyChangeListener(new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
String propName = evt.getPropertyName(); String propName = evt.getPropertyName();
if(propName.equals(SetEvt.DB_ADDED.toString()) || if (propName.equals(SetEvt.DB_ADDED.toString())
propName.equals(SetEvt.DB_DELETED.toString())) { || propName.equals(SetEvt.DB_DELETED.toString())) {
hashSetTableModel.refreshModel(); hashSetTableModel.refreshModel();
} }
} }
}); });
} }
@ -282,7 +281,7 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
// Update ingest option components. // Update ingest option components.
sendIngestMessagesCheckBox.setSelected(db.getSendIngestMessages()); sendIngestMessagesCheckBox.setSelected(db.getSendIngestMessages());
sendIngestMessagesCheckBox.setEnabled(!ingestIsRunning && db.getKnownFilesType().equals(KnownFilesType.KNOWN_BAD)); sendIngestMessagesCheckBox.setEnabled(!ingestIsRunning && db.getKnownFilesType().isInboxMessagesAllowed());
// Update database action buttons. // Update database action buttons.
createDatabaseButton.setEnabled(true); createDatabaseButton.setEnabled(true);

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.modules.hashdatabase; package org.sleuthkit.autopsy.modules.hashdatabase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.HashEntry;
interface HashSetParser { interface HashSetParser {
@ -26,7 +27,8 @@ interface HashSetParser {
* Get the next hash to import * Get the next hash to import
* *
* @return The hash as a string, or null if the end of file was reached * @return The hash as a string, or null if the end of file was reached
* without error * without error
*
* @throws TskCoreException * @throws TskCoreException
*/ */
String getNextHash() throws TskCoreException; String getNextHash() throws TskCoreException;
@ -50,4 +52,20 @@ interface HashSetParser {
* Closes the import file * Closes the import file
*/ */
void close(); void close();
/**
* Get the next hash to import as a HashEntry object.
*
* @return A new hash entry for the next item parsed.
*
* @throws TskCoreException
*/
default HashEntry getNextHashEntry() throws TskCoreException {
String next = getNextHash();
if (next == null) {
return null;
}
return new HashEntry(null, next, null, null, null);
}
} }

View File

@ -41,6 +41,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.datamodel.HashEntry;
/** /**
* Imports a hash set into the central repository and updates a progress dialog * Imports a hash set into the central repository and updates a progress dialog
@ -186,7 +187,7 @@ class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements P
* Get the newly created database * Get the newly created database
* *
* @return the imported database. May be null if an error occurred or * @return the imported database. May be null if an error occurred or
* the user canceled * the user canceled
*/ */
synchronized HashDbManager.CentralRepoHashSet getDatabase() { synchronized HashDbManager.CentralRepoHashSet getDatabase() {
return newHashDb; return newHashDb;
@ -205,7 +206,7 @@ class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements P
* Check if the import was successful or if there was an error. * Check if the import was successful or if there was an error.
* *
* @return true if the import process completed without error, false * @return true if the import process completed without error, false
* otherwise * otherwise
*/ */
boolean getImportSuccess() { boolean getImportSuccess() {
return importSuccess.get(); return importSuccess.get();
@ -231,16 +232,11 @@ class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements P
try { try {
// Conver to the FileKnown enum used by EamGlobalSet // Conver to the FileKnown enum used by EamGlobalSet
TskData.FileKnown knownStatus; TskData.FileKnown knownStatus = knownFilesType.getFileKnown();
if (knownFilesType.equals(HashDbManager.HashDb.KnownFilesType.KNOWN)) {
knownStatus = TskData.FileKnown.KNOWN;
} else {
knownStatus = TskData.FileKnown.BAD;
}
// Create an empty hashset in the central repository // Create an empty hashset in the central repository
CentralRepository dbManager = CentralRepository.getInstance(); CentralRepository dbManager = CentralRepository.getInstance();
referenceSetID.set(dbManager.newReferenceSet(new CentralRepoFileSet(orgId, hashSetName, version, knownStatus, referenceSetID.set(dbManager.newReferenceSet(new CentralRepoFileSet(orgId, hashSetName, version, knownStatus,
readOnly, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID)))); readOnly, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID))));
// Get the "FILES" content type. This is a database lookup so we // Get the "FILES" content type. This is a database lookup so we
@ -255,14 +251,14 @@ class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements P
return null; return null;
} }
String newHash = hashSetParser.getNextHash(); HashEntry newHash = hashSetParser.getNextHashEntry();
if (newHash != null) { if (newHash != null) {
CentralRepoFileInstance eamGlobalFileInstance = new CentralRepoFileInstance( CentralRepoFileInstance eamGlobalFileInstance = new CentralRepoFileInstance(
referenceSetID.get(), referenceSetID.get(),
newHash, newHash.getMd5Hash(),
knownStatus, knownStatus,
""); newHash.getComment());
globalInstances.add(eamGlobalFileInstance); globalInstances.add(eamGlobalFileInstance);

View File

@ -25,6 +25,7 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.HashEntry;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -68,7 +69,9 @@ public class KdbHashSetParser implements HashSetParser {
} }
// Get the hashes // Get the hashes
resultSet = statement.executeQuery("SELECT md5 FROM hashes"); resultSet = statement.executeQuery("SELECT h.md5 as md5, " +
" (SELECT group_concat(c.comment, ' ') FROM comments c WHERE h.id = c.hash_id) as comment " +
" from hashes h");
// At this point, getNextHash can read each hash from the result set // At this point, getNextHash can read each hash from the result set
} catch (ClassNotFoundException | SQLException ex) { } catch (ClassNotFoundException | SQLException ex) {
@ -77,15 +80,21 @@ public class KdbHashSetParser implements HashSetParser {
} }
/** /**
* Get the next hash to import * Get the next hash to import
* *
* @return The hash as a string * @return The hash as a string
*
* @throws TskCoreException * @throws TskCoreException
*/ */
@Override @Override
public String getNextHash() throws TskCoreException { public String getNextHash() throws TskCoreException {
return getNextHashEntry().getMd5Hash();
}
@Override
public HashEntry getNextHashEntry() throws TskCoreException {
try { try {
if (resultSet.next()) { if (resultSet.next()) {
byte[] hashBytes = resultSet.getBytes("md5"); byte[] hashBytes = resultSet.getBytes("md5");
@ -98,13 +107,15 @@ public class KdbHashSetParser implements HashSetParser {
throw new TskCoreException("Hash has incorrect length: " + sb.toString()); throw new TskCoreException("Hash has incorrect length: " + sb.toString());
} }
String md5Hash = sb.toString();
String comment = resultSet.getString("comment");
totalHashesRead++; totalHashesRead++;
return sb.toString(); return new HashEntry(null, md5Hash, null, null, comment);
} else { } else {
throw new TskCoreException("Could not read expected number of hashes from hash set " + filename); throw new TskCoreException("Could not read expected number of hashes from hash set " + filename);
} }
} catch (SQLException ex) { } catch (SQLException ex) {
throw new TskCoreException("Error reading hash from result set for hash set " + filename, ex); throw new TskCoreException("Error opening/reading hash set " + filename, ex);
} }
} }

View File

@ -1,14 +1,15 @@
CTL_OpenPersonas=Personas CTL_OpenPersonas=Personas
CTL_PersonasTopComponentAction=PersonasTopComponent CTL_PersonasTopComponentAction=PersonasTopComponent
CTL_PersonasTopComponent=Personas CTL_PersonasTopComponent=Personas
PersonasTopComponent.searchField.text=John Doe PersonaDetailsTopComponent.resultNameLbl.text=Name:
PersonasTopComponent.searchBtn.text=Search PersonaDetailsTopComponent.resultCasesLbl.text=Cases found in:
PersonasTopComponent.searchNameRadio.text=Name PersonaDetailsTopComponent.resultAccountsLbl.text=Accounts:
PersonasTopComponent.searchAccountRadio.text=Account PersonaDetailsTopComponent.resultAliasesLbl.text=Aliases:
PersonasTopComponent.filterResultsTable.columnModel.title1=Name PersonaDetailsTopComponent.resultNameField.text=Johnathan Dough
PersonasTopComponent.filterResultsTable.columnModel.title0=ID PersonaSearchTopComponent.searchAccountRadio.text=Account
PersonasTopComponent.resultAccountsLbl.text=Accounts: PersonaSearchTopComponent.searchNameRadio.text=Name
PersonasTopComponent.resultAliasesLbl.text=Aliases: PersonaSearchTopComponent.searchField.text=
PersonasTopComponent.resultNameLbl.text=Name: PersonaSearchTopComponent.searchBtn.text=Search
PersonasTopComponent.resultCasesLbl.text=Cases found in: PersonaSearchTopComponent.filterResultsTable.columnModel.title1=Name
PersonasTopComponent.resultNameField.text=Johnathan Dough PersonaSearchTopComponent.filterResultsTable.columnModel.title0=ID
PersonaSearchTopComponent.filterResultsTable.toolTipText=

View File

@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<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">
<EmptySpace min="0" pref="505" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="detailsPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="555" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="detailsPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="detailsPanel">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="accountsTablePane" alignment="1" pref="0" max="32767" attributes="0"/>
<Component id="aliasesListPane" alignment="1" max="32767" attributes="0"/>
<Component id="casesListPane" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="resultNameLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="resultNameField" pref="447" max="32767" attributes="0"/>
</Group>
<Component id="resultAliasesLbl" alignment="0" max="32767" attributes="0"/>
<Component id="resultAccountsLbl" alignment="0" max="32767" attributes="0"/>
<Component id="resultCasesLbl" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="resultNameLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="resultNameField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="resultAliasesLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="aliasesListPane" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="resultAccountsLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="accountsTablePane" min="-2" pref="153" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="resultCasesLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="casesListPane" pref="118" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="resultNameLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaDetailsTopComponent.resultNameLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="resultNameField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaDetailsTopComponent.resultNameField.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="resultNameFieldActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="resultAliasesLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaDetailsTopComponent.resultAliasesLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="resultAccountsLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaDetailsTopComponent.resultAccountsLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="accountsTablePane">
<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="accountsTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
<Table columnCount="2" rowCount="4">
<Column editable="false" title="Type" type="java.lang.String">
<Data value="Email"/>
<Data value="Phone"/>
<Data value="Twitter"/>
<Data value="null"/>
</Column>
<Column editable="false" title="Data" type="java.lang.String">
<Data value="jdb@yahoo.com"/>
<Data value="865-555-5555"/>
<Data value="@jd93.bread"/>
<Data value="null"/>
</Column>
</Table>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="resultCasesLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaDetailsTopComponent.resultCasesLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="casesListPane">
<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="casesList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="2">
<StringItem index="0" value="Investigation 13"/>
<StringItem index="1" value="Scene 5"/>
</StringArray>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="aliasesListPane">
<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="aliasesList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="3">
<StringItem index="0" value="J.D."/>
<StringItem index="1" value="Fred Smidge"/>
<StringItem index="2" value="Ethan Roseman"/>
</StringArray>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -18,33 +18,26 @@
*/ */
package org.sleuthkit.autopsy.persona; package org.sleuthkit.autopsy.persona;
import org.openide.windows.TopComponent;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.windows.RetainLocation; import org.openide.windows.RetainLocation;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
/** /**
* Top component for the Personas tool * Top component for persona details
*
*/ */
@TopComponent.Description(preferredID = "PersonasTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER) @TopComponent.Description(preferredID = "PersonasTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER)
@TopComponent.Registration(mode = "personas", openAtStartup = false) @TopComponent.Registration(mode = "personadetails", openAtStartup = false)
@RetainLocation("personas") @RetainLocation("personadetails")
@SuppressWarnings("PMD.SingularField") @SuppressWarnings("PMD.SingularField")
public final class PersonasTopComponent extends TopComponent { public final class PersonaDetailsTopComponent extends TopComponent {
@Messages({ @Messages({
"PTopComponent_Name=Personas" "PDTopComponent_Name=Persona Details"
}) })
public PersonasTopComponent() { public PersonaDetailsTopComponent() {
initComponents(); initComponents();
setName(Bundle.PTopComponent_Name()); setName(Bundle.PDTopComponent_Name());
}
@Override
public void componentOpened() {
super.componentOpened();
WindowManager.getDefault().setTopComponentFloating(this, true);
} }
/** /**
@ -55,15 +48,6 @@ public final class PersonasTopComponent extends TopComponent {
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() { private void initComponents() {
searchButtonGroup = new javax.swing.ButtonGroup();
splitPane = new javax.swing.JSplitPane();
searchPanel = new javax.swing.JPanel();
searchField = new javax.swing.JTextField();
searchNameRadio = new javax.swing.JRadioButton();
searchAccountRadio = new javax.swing.JRadioButton();
filterResultsPane = new javax.swing.JScrollPane();
filterResultsTable = new javax.swing.JTable();
searchBtn = new javax.swing.JButton();
detailsPanel = new javax.swing.JPanel(); detailsPanel = new javax.swing.JPanel();
resultNameLbl = new javax.swing.JLabel(); resultNameLbl = new javax.swing.JLabel();
resultNameField = new javax.swing.JTextField(); resultNameField = new javax.swing.JTextField();
@ -77,102 +61,19 @@ public final class PersonasTopComponent extends TopComponent {
aliasesListPane = new javax.swing.JScrollPane(); aliasesListPane = new javax.swing.JScrollPane();
aliasesList = new javax.swing.JList<>(); aliasesList = new javax.swing.JList<>();
setMinimumSize(new java.awt.Dimension(400, 400)); org.openide.awt.Mnemonics.setLocalizedText(resultNameLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsTopComponent.class, "PersonaDetailsTopComponent.resultNameLbl.text")); // NOI18N
searchField.setText(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchField.text")); // NOI18N
searchButtonGroup.add(searchNameRadio);
searchNameRadio.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(searchNameRadio, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchNameRadio.text")); // NOI18N
searchNameRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
searchNameRadioActionPerformed(evt);
}
});
searchButtonGroup.add(searchAccountRadio);
org.openide.awt.Mnemonics.setLocalizedText(searchAccountRadio, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchAccountRadio.text")); // NOI18N
filterResultsTable.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
{"0", "Johnathn Dough"},
{"3", "Joe Schmoe"},
{"2", "Michael Schmoe"},
{"1", "Ethan Schmoe"}
},
new String [] {
"ID", "Name"
}
) {
Class[] types = new Class [] {
java.lang.String.class, java.lang.String.class
};
public Class getColumnClass(int columnIndex) {
return types [columnIndex];
}
});
filterResultsPane.setViewportView(filterResultsTable);
if (filterResultsTable.getColumnModel().getColumnCount() > 0) {
filterResultsTable.getColumnModel().getColumn(0).setMaxWidth(25);
filterResultsTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.filterResultsTable.columnModel.title0")); // NOI18N
filterResultsTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.filterResultsTable.columnModel.title1")); // NOI18N
}
org.openide.awt.Mnemonics.setLocalizedText(searchBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchBtn.text")); // NOI18N
javax.swing.GroupLayout searchPanelLayout = new javax.swing.GroupLayout(searchPanel);
searchPanel.setLayout(searchPanelLayout);
searchPanelLayout.setHorizontalGroup(
searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(filterResultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addGroup(searchPanelLayout.createSequentialGroup()
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(searchField)
.addGroup(searchPanelLayout.createSequentialGroup()
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.addComponent(searchNameRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(searchAccountRadio))
.addComponent(searchBtn))
.addGap(0, 25, Short.MAX_VALUE)))
.addContainerGap())))
);
searchPanelLayout.setVerticalGroup(
searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(searchField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(searchNameRadio)
.addComponent(searchAccountRadio))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(searchBtn)
.addGap(18, 18, 18)
.addComponent(filterResultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addContainerGap())
);
splitPane.setLeftComponent(searchPanel);
org.openide.awt.Mnemonics.setLocalizedText(resultNameLbl, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultNameLbl.text")); // NOI18N
resultNameField.setEditable(false); resultNameField.setEditable(false);
resultNameField.setText(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultNameField.text")); // NOI18N resultNameField.setText(org.openide.util.NbBundle.getMessage(PersonaDetailsTopComponent.class, "PersonaDetailsTopComponent.resultNameField.text")); // NOI18N
resultNameField.addActionListener(new java.awt.event.ActionListener() { resultNameField.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
resultNameFieldActionPerformed(evt); resultNameFieldActionPerformed(evt);
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(resultAliasesLbl, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultAliasesLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(resultAliasesLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsTopComponent.class, "PersonaDetailsTopComponent.resultAliasesLbl.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(resultAccountsLbl, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultAccountsLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(resultAccountsLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsTopComponent.class, "PersonaDetailsTopComponent.resultAccountsLbl.text")); // NOI18N
accountsTable.setModel(new javax.swing.table.DefaultTableModel( accountsTable.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] { new Object [][] {
@ -202,7 +103,7 @@ public final class PersonasTopComponent extends TopComponent {
}); });
accountsTablePane.setViewportView(accountsTable); accountsTablePane.setViewportView(accountsTable);
org.openide.awt.Mnemonics.setLocalizedText(resultCasesLbl, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultCasesLbl.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(resultCasesLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsTopComponent.class, "PersonaDetailsTopComponent.resultCasesLbl.text")); // NOI18N
casesList.setModel(new javax.swing.AbstractListModel<String>() { casesList.setModel(new javax.swing.AbstractListModel<String>() {
String[] strings = { "Investigation 13", "Scene 5" }; String[] strings = { "Investigation 13", "Scene 5" };
@ -259,26 +160,30 @@ public final class PersonasTopComponent extends TopComponent {
.addContainerGap()) .addContainerGap())
); );
splitPane.setRightComponent(detailsPanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
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(splitPane) .addGap(0, 505, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addComponent(detailsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE)))
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(splitPane) .addGap(0, 555, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addComponent(detailsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE)))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void searchNameRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchNameRadioActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_searchNameRadioActionPerformed
private void resultNameFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_resultNameFieldActionPerformed private void resultNameFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_resultNameFieldActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_resultNameFieldActionPerformed }//GEN-LAST:event_resultNameFieldActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
@ -289,20 +194,16 @@ public final class PersonasTopComponent extends TopComponent {
private javax.swing.JList<String> casesList; private javax.swing.JList<String> casesList;
private javax.swing.JScrollPane casesListPane; private javax.swing.JScrollPane casesListPane;
private javax.swing.JPanel detailsPanel; private javax.swing.JPanel detailsPanel;
private javax.swing.JScrollPane filterResultsPane;
private javax.swing.JTable filterResultsTable;
private javax.swing.JLabel resultAccountsLbl; private javax.swing.JLabel resultAccountsLbl;
private javax.swing.JLabel resultAliasesLbl; private javax.swing.JLabel resultAliasesLbl;
private javax.swing.JLabel resultCasesLbl; private javax.swing.JLabel resultCasesLbl;
private javax.swing.JTextField resultNameField; private javax.swing.JTextField resultNameField;
private javax.swing.JLabel resultNameLbl; private javax.swing.JLabel resultNameLbl;
private javax.swing.JRadioButton searchAccountRadio;
private javax.swing.JButton searchBtn;
private javax.swing.ButtonGroup searchButtonGroup;
private javax.swing.JTextField searchField;
private javax.swing.JRadioButton searchNameRadio;
private javax.swing.JPanel searchPanel;
private javax.swing.JSplitPane splitPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@Override
public void componentOpened() {
super.componentOpened();
WindowManager.getDefault().setTopComponentFloating(this, true);
}
} }

View File

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="searchButtonGroup">
</Component>
</NonVisualComponents>
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 400]"/>
</Property>
</Properties>
<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="searchPanel" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="searchPanel" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="searchPanel">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="filterResultsPane" pref="0" max="32767" attributes="0"/>
<Component id="searchField" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="searchNameRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="searchAccountRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="155" max="32767" attributes="0"/>
<Component id="searchBtn" min="-2" max="-2" 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="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="searchField" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="searchNameRadio" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchAccountRadio" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="filterResultsPane" pref="320" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JTextField" name="searchField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.searchField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="searchNameRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="searchButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.searchNameRadio.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="searchNameRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="searchAccountRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="searchButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.searchAccountRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="filterResultsPane">
<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="filterResultsTable">
<Properties>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.filterResultsTable.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0">
<Column maxWidth="25" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.filterResultsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title>
<Editor/>
<Renderer/>
</Column>
<Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.filterResultsTable.columnModel.title1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title>
<Editor/>
<Renderer/>
</Column>
</TableColumnModel>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="false" resizingAllowed="true"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="searchBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.searchBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,217 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.persona;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.Collections;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.DefaultTableModel;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.RetainLocation;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
/**
* Top component for the Personas tool
*
*/
@TopComponent.Description(preferredID = "PersonasTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER)
@TopComponent.Registration(mode = "personasearch", openAtStartup = false)
@RetainLocation("personasearch")
@SuppressWarnings("PMD.SingularField")
public final class PersonaSearchTopComponent extends TopComponent {
@Messages({
"PSTopComponent_Name=Persona Search"
})
public PersonaSearchTopComponent() {
initComponents();
setName(Bundle.PSTopComponent_Name());
executeSearch();
searchBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
executeSearch();
}
});
}
/**
* Table model for the persona search results
*/
final class PersonaFilterTableModel extends DefaultTableModel {
private static final long serialVersionUID = 1L;
PersonaFilterTableModel(Object[][] rows, String[] colNames) {
super(rows, colNames);
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
}
private void updateResultsTable(Collection<Persona> results) {
Object[][] rows = new Object[results.size()][2];
int i = 0;
for (Persona result : results) {
rows[i] = new String[]{String.valueOf(result.getId()), result.getName()};
i++;
}
DefaultTableModel updatedTableModel = new PersonaFilterTableModel(
rows,
new String[]{"ID", "Name"}
);
filterResultsTable.setModel(updatedTableModel);
// Formatting
filterResultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
filterResultsTable.getColumnModel().getColumn(0).setMaxWidth(100);
filterResultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
}
private void executeSearch() {
Collection<Persona> results = Collections.EMPTY_LIST;
try {
results = Persona.getPersonaByName(searchField.getText());
} catch (CentralRepoException ex) {
Exceptions.printStackTrace(ex);
}
updateResultsTable(results);
}
@Override
public void componentOpened() {
super.componentOpened();
WindowManager.getDefault().setTopComponentFloating(this, 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.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
searchButtonGroup = new javax.swing.ButtonGroup();
searchPanel = new javax.swing.JPanel();
searchField = new javax.swing.JTextField();
searchNameRadio = new javax.swing.JRadioButton();
searchAccountRadio = new javax.swing.JRadioButton();
filterResultsPane = new javax.swing.JScrollPane();
filterResultsTable = new javax.swing.JTable();
searchBtn = new javax.swing.JButton();
setMinimumSize(new java.awt.Dimension(400, 400));
searchField.setText(org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.searchField.text")); // NOI18N
searchButtonGroup.add(searchNameRadio);
searchNameRadio.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(searchNameRadio, org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.searchNameRadio.text")); // NOI18N
searchNameRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
searchNameRadioActionPerformed(evt);
}
});
searchButtonGroup.add(searchAccountRadio);
org.openide.awt.Mnemonics.setLocalizedText(searchAccountRadio, org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.searchAccountRadio.text")); // NOI18N
filterResultsTable.setToolTipText(org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.filterResultsTable.toolTipText")); // NOI18N
filterResultsTable.getTableHeader().setReorderingAllowed(false);
filterResultsPane.setViewportView(filterResultsTable);
if (filterResultsTable.getColumnModel().getColumnCount() > 0) {
filterResultsTable.getColumnModel().getColumn(0).setMaxWidth(25);
filterResultsTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.filterResultsTable.columnModel.title0")); // NOI18N
filterResultsTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.filterResultsTable.columnModel.title1")); // NOI18N
}
org.openide.awt.Mnemonics.setLocalizedText(searchBtn, org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.searchBtn.text")); // NOI18N
javax.swing.GroupLayout searchPanelLayout = new javax.swing.GroupLayout(searchPanel);
searchPanel.setLayout(searchPanelLayout);
searchPanelLayout.setHorizontalGroup(
searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(filterResultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(searchField)
.addGroup(searchPanelLayout.createSequentialGroup()
.addComponent(searchNameRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(searchAccountRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 155, Short.MAX_VALUE)
.addComponent(searchBtn)))
.addContainerGap())
);
searchPanelLayout.setVerticalGroup(
searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(searchField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(searchNameRadio)
.addComponent(searchAccountRadio)
.addComponent(searchBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(filterResultsPane, javax.swing.GroupLayout.DEFAULT_SIZE, 320, Short.MAX_VALUE)
.addContainerGap())
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(searchPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(searchPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
private void searchNameRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchNameRadioActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_searchNameRadioActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane filterResultsPane;
private javax.swing.JTable filterResultsTable;
private javax.swing.JRadioButton searchAccountRadio;
private javax.swing.JButton searchBtn;
private javax.swing.ButtonGroup searchButtonGroup;
private javax.swing.JTextField searchField;
private javax.swing.JRadioButton searchNameRadio;
private javax.swing.JPanel searchPanel;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,381 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="searchButtonGroup">
</Component>
</NonVisualComponents>
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 400]"/>
</Property>
</Properties>
<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="splitPane" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="splitPane" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="splitPane">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="searchPanel">
<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">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="filterResultsPane" pref="0" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="searchField" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="searchNameRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="searchAccountRadio" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="searchBtn" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="25" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="searchField" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="searchNameRadio" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchAccountRadio" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="searchBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="filterResultsPane" pref="0" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JTextField" name="searchField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.searchField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="searchNameRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="searchButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.searchNameRadio.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="searchNameRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="searchAccountRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="searchButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.searchAccountRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="filterResultsPane">
<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="filterResultsTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
<Table columnCount="2" rowCount="4">
<Column editable="true" title="ID" type="java.lang.String">
<Data value="0"/>
<Data value="3"/>
<Data value="2"/>
<Data value="1"/>
</Column>
<Column editable="true" title="Name" type="java.lang.String">
<Data value="Johnathn Dough"/>
<Data value="Joe Schmoe"/>
<Data value="Michael Schmoe"/>
<Data value="Ethan Schmoe"/>
</Column>
</Table>
</Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0">
<Column maxWidth="25" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.filterResultsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title>
<Editor/>
<Renderer/>
</Column>
<Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.filterResultsTable.columnModel.title1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title>
<Editor/>
<Renderer/>
</Column>
</TableColumnModel>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="true" resizingAllowed="true"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="searchBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.searchBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="detailsPanel">
<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" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="accountsTablePane" pref="0" max="32767" attributes="0"/>
<Component id="aliasesListPane" max="32767" attributes="0"/>
<Component id="casesListPane" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="resultNameLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="resultNameField" pref="447" max="32767" attributes="0"/>
</Group>
<Component id="resultAliasesLbl" alignment="0" max="32767" attributes="0"/>
<Component id="resultAccountsLbl" alignment="0" max="32767" attributes="0"/>
<Component id="resultCasesLbl" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="resultNameLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="resultNameField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="resultAliasesLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="aliasesListPane" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="resultAccountsLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="accountsTablePane" min="-2" pref="153" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="resultCasesLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="casesListPane" pref="118" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="resultNameLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.resultNameLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="resultNameField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.resultNameField.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="resultNameFieldActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="resultAliasesLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.resultAliasesLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="resultAccountsLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.resultAccountsLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="accountsTablePane">
<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="accountsTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
<Table columnCount="2" rowCount="4">
<Column editable="false" title="Type" type="java.lang.String">
<Data value="Email"/>
<Data value="Phone"/>
<Data value="Twitter"/>
<Data value="null"/>
</Column>
<Column editable="false" title="Data" type="java.lang.String">
<Data value="jdb@yahoo.com"/>
<Data value="865-555-5555"/>
<Data value="@jd93.bread"/>
<Data value="null"/>
</Column>
</Table>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="resultCasesLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.resultCasesLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="casesListPane">
<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="casesList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="2">
<StringItem index="0" value="Investigation 13"/>
<StringItem index="1" value="Scene 5"/>
</StringArray>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="aliasesListPane">
<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="aliasesList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="3">
<StringItem index="0" value="J.D."/>
<StringItem index="1" value="Fred Smidge"/>
<StringItem index="2" value="Ethan Roseman"/>
</StringArray>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -65,6 +65,7 @@ import org.sleuthkit.datamodel.Pool;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TaggingManager.ContentTagChange;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.TskDataException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -740,13 +741,13 @@ public class PortableCaseReportModule implements ReportModule {
if (! oldTagNameToNewTagName.containsKey(tag.getName())) { if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS
} }
ContentTag newContentTag = portableSkCase.addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset()); ContentTagChange newContentTag = portableSkCase.getTaggingManager().addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
// Get the image tag data associated with this tag (empty string if there is none) // Get the image tag data associated with this tag (empty string if there is none)
// and save it if present // and save it if present
String appData = getImageTagDataForContentTag(tag); String appData = getImageTagDataForContentTag(tag);
if (! appData.isEmpty()) { if (! appData.isEmpty()) {
addImageTagToPortableCase(newContentTag, appData); addImageTagToPortableCase(newContentTag.getAddedTag(), appData);
} }
} }
} }
@ -847,7 +848,7 @@ public class PortableCaseReportModule implements ReportModule {
if (! oldTagNameToNewTagName.containsKey(tag.getName())) { if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS
} }
portableSkCase.addBlackboardArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment()); portableSkCase.getTaggingManager().addArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment());
} }
} }

View File

@ -57,6 +57,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceDeletedEvent;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.coreutils.History; import org.sleuthkit.autopsy.coreutils.History;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
@ -74,6 +75,7 @@ import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisEvent; import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisEvent;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector.FileTypeDetectorInitException;
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;
@ -108,8 +110,6 @@ public final class ImageGalleryController {
Case.Events.CONTENT_TAG_DELETED, Case.Events.CONTENT_TAG_DELETED,
Case.Events.DATA_SOURCE_DELETED Case.Events.DATA_SOURCE_DELETED
); );
private static final String CATEGORY_TAG_SET_PREFIX = "Project VIC";
/* /*
* There is an image gallery controller per case. It is created during the * There is an image gallery controller per case. It is created during the
@ -725,19 +725,19 @@ public final class ImageGalleryController {
private static boolean isDrawableAndNotKnown(AbstractFile abstractFile) throws FileTypeDetector.FileTypeDetectorInitException { private static boolean isDrawableAndNotKnown(AbstractFile abstractFile) throws FileTypeDetector.FileTypeDetectorInitException {
return (abstractFile.getKnown() != TskData.FileKnown.KNOWN) && FileTypeUtils.isDrawable(abstractFile); return (abstractFile.getKnown() != TskData.FileKnown.KNOWN) && FileTypeUtils.isDrawable(abstractFile);
} }
/** /**
* Returns the TagSet with the image gallery categories. * Returns the TagSet with the image gallery categories.
* *
* @return Category TagSet. * @return Category TagSet.
* *
* @throws TskCoreException * @throws TskCoreException
*/ */
private TagSet getCategoryTagSet() throws TskCoreException { private TagSet getCategoryTagSet() throws TskCoreException {
List<TagSet> tagSetList = getCaseDatabase().getTaggingManager().getTagSets(); List<TagSet> tagSetList = getCaseDatabase().getTaggingManager().getTagSets();
if (tagSetList != null && !tagSetList.isEmpty()) { if (tagSetList != null && !tagSetList.isEmpty()) {
for (TagSet set : tagSetList) { for (TagSet set : tagSetList) {
if (set.getName().startsWith(CATEGORY_TAG_SET_PREFIX)) { if (set.getName().equals(TagsManager.getCategoryTagSetName())) {
return set; return set;
} }
} }

View File

@ -82,7 +82,6 @@ public class CategorizeAction extends Action {
this.tagName = tagName; this.tagName = tagName;
setGraphic(getGraphic(tagName)); setGraphic(getGraphic(tagName));
setEventHandler(actionEvent -> addCatToFiles(selectedFileIDs)); setEventHandler(actionEvent -> addCatToFiles(selectedFileIDs));
setAccelerator(new KeyCodeCombination(KeyCode.getKeyCode(getCategoryNumberFromTagName(tagName))));
} }
static public Menu getCategoriesMenu(ImageGalleryController controller) { static public Menu getCategoriesMenu(ImageGalleryController controller) {
@ -94,16 +93,6 @@ public class CategorizeAction extends Action {
controller.queueDBTask(new CategorizeDrawableFileTask(ids, tagName, createUndo)); controller.queueDBTask(new CategorizeDrawableFileTask(ids, tagName, createUndo));
} }
private String getCategoryNumberFromTagName(TagName tagName) {
String displayName = tagName.getDisplayName();
if (displayName.contains("CAT")) {
String[] split = displayName.split(":");
split = split[0].split("-");
return split[1];
}
return "";
}
/** /**
* Instances of this class implement a context menu user interface for * Instances of this class implement a context menu user interface for
* selecting a category * selecting a category

View File

@ -13,6 +13,4 @@ DrawableAttribute.name=Name
DrawableAttribute.path=Path DrawableAttribute.path=Path
DrawableAttribute.tags=Tags DrawableAttribute.tags=Tags
DrawableAttribute.width=Width DrawableAttribute.width=Width
DrawableTagsManager.bookMark=Bookmark
DrawableTagsManager.followUp=Follow Up
VideoFile.getMedia.progress=writing temporary file to disk VideoFile.getMedia.progress=writing temporary file to disk

View File

@ -38,7 +38,6 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent.DeletedContentTagInfo; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent.DeletedContentTagInfo;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TagName;
@ -62,9 +61,7 @@ public class CategoryManager {
private static final Logger LOGGER = Logger.getLogger(CategoryManager.class.getName()); private static final Logger LOGGER = Logger.getLogger(CategoryManager.class.getName());
/** /**
* the DrawableDB that backs the category counts cache. The counts are * the DrawableDB that backs the category counts cache.
* initialized from this, and the counting of CAT-0 is always delegated to
* this db.
*/ */
private final DrawableDB drawableDb; private final DrawableDB drawableDb;

View File

@ -29,7 +29,6 @@ import javafx.scene.Node;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.casemodule.services.TagsManager;
@ -44,8 +43,6 @@ import org.sleuthkit.datamodel.TskCoreException;
* Manages Tags, Tagging, and the relationship between Categories and Tags in * Manages Tags, Tagging, and the relationship between Categories and Tags in
* the autopsy Db. Delegates some work to the backing autopsy TagsManager. * the autopsy Db. Delegates some work to the backing autopsy TagsManager.
*/ */
@NbBundle.Messages({"DrawableTagsManager.followUp=Follow Up",
"DrawableTagsManager.bookMark=Bookmark"})
public final class DrawableTagsManager { public final class DrawableTagsManager {
private static final Logger logger = Logger.getLogger(DrawableTagsManager.class.getName()); private static final Logger logger = Logger.getLogger(DrawableTagsManager.class.getName());
@ -78,8 +75,8 @@ public final class DrawableTagsManager {
public DrawableTagsManager(ImageGalleryController controller) throws TskCoreException { public DrawableTagsManager(ImageGalleryController controller) throws TskCoreException {
this.autopsyTagsManager = controller.getCase().getServices().getTagsManager(); this.autopsyTagsManager = controller.getCase().getServices().getTagsManager();
followUpTagName = getTagName(Bundle.DrawableTagsManager_followUp()); followUpTagName = getTagName(TagsManager.getFollowUpDisplayString());
bookmarkTagName = getTagName(Bundle.DrawableTagsManager_bookMark()); bookmarkTagName = getTagName(TagsManager.getBookmarkDisplayString());
this.controller = controller; this.controller = controller;
compareByDisplayName = new Comparator<TagName>() { compareByDisplayName = new Comparator<TagName>() {

View File

@ -61,7 +61,7 @@
<CheckBox fx:id="seenByOtherExaminersCheckBox" mnemonicParsing="false" text="Don't show groups seen by other examiners" HBox.hgrow="NEVER" /> <CheckBox fx:id="seenByOtherExaminersCheckBox" mnemonicParsing="false" text="Don't show groups seen by other examiners" HBox.hgrow="NEVER" />
<AnchorPane fx:id="nextButtonPane" minWidth="50.0" HBox.hgrow="NEVER"> <AnchorPane fx:id="nextButtonPane" minWidth="50.0" HBox.hgrow="NEVER">
<children> <children>
<Button fx:id="nextButton" contentDisplay="RIGHT" layoutX="44.0" layoutY="-1.0" mnemonicParsing="false" text="All Groups Gave Been Seen" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <Button fx:id="nextButton" contentDisplay="RIGHT" layoutX="44.0" layoutY="-1.0" mnemonicParsing="false" text="All Groups Have Been Seen" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<graphic> <graphic>
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
<image> <image>