mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge branch 'develop' into 1467-add-rank-to-tag-names-table
This commit is contained in:
commit
1ed1e81d29
@ -90,6 +90,7 @@ public interface AutopsyService {
|
||||
private final Case theCase;
|
||||
private final ProgressIndicator progressIndicator;
|
||||
private volatile boolean cancelRequested;
|
||||
private final boolean isNewCase;
|
||||
|
||||
/**
|
||||
* Constructs the context for the creation/opening/upgrading of
|
||||
@ -100,9 +101,23 @@ public interface AutopsyService {
|
||||
* case-level resources
|
||||
*/
|
||||
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.progressIndicator = progressIndicator;
|
||||
this.cancelRequested = false;
|
||||
this.isNewCase = isNewCase;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,6 +160,16 @@ public interface AutopsyService {
|
||||
public boolean 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,7 +132,6 @@ class AddImageTask implements Runnable {
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
List<Content> newDataSources = new ArrayList<>();
|
||||
try {
|
||||
currentCase.getSleuthkitCase().acquireSingleUserCaseWriteLock();
|
||||
synchronized (tskAddImageProcessLock) {
|
||||
if (!tskAddImageProcessStopped) {
|
||||
tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles, imageWriterPath);
|
||||
@ -147,7 +146,6 @@ class AddImageTask implements Runnable {
|
||||
commitOrRevertAddImageProcess(currentCase, errorMessages, newDataSources);
|
||||
progressMonitor.setProgress(100);
|
||||
} finally {
|
||||
currentCase.getSleuthkitCase().releaseSingleUserCaseWriteLock();
|
||||
DataSourceProcessorCallback.DataSourceProcessorResult result;
|
||||
if (criticalErrorOccurred) {
|
||||
result = DataSourceProcessorResult.CRITICAL_ERRORS;
|
||||
|
@ -1958,7 +1958,7 @@ public class Case {
|
||||
checkForCancellation();
|
||||
openCaseLevelServices(progressIndicator);
|
||||
checkForCancellation();
|
||||
openAppServiceCaseResources(progressIndicator);
|
||||
openAppServiceCaseResources(progressIndicator, true);
|
||||
checkForCancellation();
|
||||
openCommunicationChannels(progressIndicator);
|
||||
return null;
|
||||
@ -2007,7 +2007,7 @@ public class Case {
|
||||
checkForCancellation();
|
||||
openCaseLevelServices(progressIndicator);
|
||||
checkForCancellation();
|
||||
openAppServiceCaseResources(progressIndicator);
|
||||
openAppServiceCaseResources(progressIndicator, false);
|
||||
checkForCancellation();
|
||||
openCommunicationChannels(progressIndicator);
|
||||
checkForCancellation();
|
||||
@ -2060,6 +2060,7 @@ public class Case {
|
||||
|
||||
private final SleuthkitCase tskCase;
|
||||
private final String caseName;
|
||||
private final long MAX_IMAGE_THRESHOLD = 100;
|
||||
private final ProgressIndicator progressIndicator;
|
||||
|
||||
/**
|
||||
@ -2117,7 +2118,7 @@ public class Case {
|
||||
* event that the operation is
|
||||
* 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];
|
||||
|
||||
for (Image image : images) {
|
||||
@ -2125,20 +2126,10 @@ public class Case {
|
||||
|
||||
progressIndicator.progress(Bundle.Case_openFileSystems_openingImage(imageStr));
|
||||
|
||||
Collection<FileSystem> fileSystems = this.tskCase.getFileSystems(image);
|
||||
Collection<FileSystem> fileSystems = this.tskCase.getImageFileSystems(image);
|
||||
checkIfCancelled();
|
||||
for (FileSystem fileSystem : fileSystems) {
|
||||
try {
|
||||
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);
|
||||
}
|
||||
|
||||
fileSystem.read(tempBuff, 0, 512);
|
||||
checkIfCancelled();
|
||||
}
|
||||
|
||||
@ -2154,12 +2145,23 @@ public class Case {
|
||||
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();
|
||||
openFileSystems(images);
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(
|
||||
Level.INFO,
|
||||
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.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
|
||||
* task, running in a named thread managed by an executor service, with
|
||||
@ -2548,7 +2550,7 @@ public class Case {
|
||||
appServiceProgressIndicator = new LoggingProgressIndicator();
|
||||
}
|
||||
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
|
||||
threadNameSuffix = threadNameSuffix.toLowerCase();
|
||||
TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_RESOURCES_THREAD_NAME, threadNameSuffix));
|
||||
|
@ -55,11 +55,11 @@ final class TagNameDefinition implements Comparable<TagNameDefinition> {
|
||||
private static final String TAG_SETTING_VERSION_KEY = "CustomTagNameVersion";
|
||||
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_TWO_NAME = "CAT-2: Child Exploitation (Non-Illegal/Age Difficult)";
|
||||
private static final String CATEGORY_THREE_NAME = "CAT-3: CGI/Animation (Child Exploitive)";
|
||||
private static final String CATEGORY_FOUR_NAME = "CAT-4: Exemplar/Comparison (Internal Use Only)";
|
||||
private static final String CATEGORY_FIVE_NAME = "CAT-5: Non-pertinent";
|
||||
private static final String CATEGORY_ONE_NAME = "Child Exploitation (Illegal)";
|
||||
private static final String CATEGORY_TWO_NAME = "Child Exploitation (Non-Illegal/Age Difficult)";
|
||||
private static final String CATEGORY_THREE_NAME = "CGI/Animation (Child Exploitive)";
|
||||
private static final String CATEGORY_FOUR_NAME = "Exemplar/Comparison (Internal Use Only)";
|
||||
private static final String CATEGORY_FIVE_NAME = "Non-pertinent";
|
||||
|
||||
private final String displayName;
|
||||
private final String description;
|
||||
@ -119,6 +119,33 @@ final class TagNameDefinition implements Comparable<TagNameDefinition> {
|
||||
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.
|
||||
*
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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 final SleuthkitCase caseDb;
|
||||
|
||||
static String DEFAULT_TAG_SET_NAME = "Project VIC (United States)";
|
||||
private static String DEFAULT_TAG_SET_NAME = "Project VIC";
|
||||
|
||||
static {
|
||||
|
||||
@ -146,6 +146,12 @@ public class TagsManager implements Closeable {
|
||||
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() {
|
||||
List<String> tagDisplayNames = new ArrayList<>();
|
||||
for (TagNameDefinition tagDef : TagNameDefinition.getTagNameDefinitions()) {
|
||||
@ -153,6 +159,22 @@ public class TagsManager implements Closeable {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -162,7 +184,62 @@ public class TagsManager implements Closeable {
|
||||
* @return list of predefined tag names
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,7 @@ import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||
import org.sleuthkit.datamodel.HashHitInfo;
|
||||
|
||||
/**
|
||||
* Main interface for interacting with the database
|
||||
@ -553,6 +554,22 @@ public interface CentralRepository {
|
||||
*/
|
||||
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
|
||||
*
|
||||
|
@ -41,6 +41,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
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.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
|
||||
import org.sleuthkit.datamodel.HashHitInfo;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
@ -104,7 +106,6 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
|
||||
private static final int QUERY_STR_MAX_LEN = 1000;
|
||||
|
||||
|
||||
/**
|
||||
* Connect to the DB and initialize it.
|
||||
*
|
||||
@ -135,6 +136,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
* Get an ephemeral connection.
|
||||
*/
|
||||
protected abstract Connection getEphemeralConnection();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public final void clearCaches() {
|
||||
synchronized(typeCache) {
|
||||
synchronized (typeCache) {
|
||||
typeCache.invalidateAll();
|
||||
isCRTypeCacheInitialized = false;
|
||||
}
|
||||
@ -1019,22 +1021,21 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
String sql;
|
||||
// _instance table for accounts have an additional account_id column
|
||||
if (artifactHasAnAccount) {
|
||||
sql = "INSERT INTO "
|
||||
+ tableName
|
||||
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
|
||||
+ getConflictClause();
|
||||
}
|
||||
else {
|
||||
sql = "INSERT INTO "
|
||||
+ tableName
|
||||
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?) "
|
||||
+ getConflictClause();
|
||||
sql = "INSERT INTO "
|
||||
+ tableName
|
||||
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
|
||||
+ getConflictClause();
|
||||
} else {
|
||||
sql = "INSERT INTO "
|
||||
+ tableName
|
||||
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?) "
|
||||
+ getConflictClause();
|
||||
}
|
||||
|
||||
try (Connection conn = connect();
|
||||
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
|
||||
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
|
||||
|
||||
if (!eamArtifact.getCorrelationValue().isEmpty()) {
|
||||
preparedStatement.setInt(1, eamArtifact.getCorrelationCase().getID());
|
||||
@ -1067,18 +1068,18 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Central Repo account for the given account type and account ID.
|
||||
* Create a new account first, if one doesn't exist
|
||||
*
|
||||
* @param accountType account type
|
||||
* @param accountUniqueID unique account identifier
|
||||
*
|
||||
* @return A matching account, either existing or newly created.
|
||||
*
|
||||
* @throws TskCoreException exception thrown if a critical error occurs
|
||||
* within TSK core
|
||||
*/
|
||||
/**
|
||||
* Gets the Central Repo account for the given account type and account ID.
|
||||
* Create a new account first, if one doesn't exist
|
||||
*
|
||||
* @param accountType account type
|
||||
* @param accountUniqueID unique account identifier
|
||||
*
|
||||
* @return A matching account, either existing or newly created.
|
||||
*
|
||||
* @throws TskCoreException exception thrown if a critical error occurs
|
||||
* within TSK core
|
||||
*/
|
||||
@Override
|
||||
public CentralRepoAccount getOrCreateAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException {
|
||||
// Get the account fom the accounts table
|
||||
@ -1105,27 +1106,23 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
return account;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException {
|
||||
try {
|
||||
return accountTypesCache.get(accountTypeName, () -> getCRAccountTypeFromDb(accountTypeName));
|
||||
} 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
|
||||
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);) {
|
||||
|
||||
try (Connection conn = connect();
|
||||
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
|
||||
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery();) {
|
||||
while (resultSet.next()) {
|
||||
@ -1145,6 +1142,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
* Gets the CR account type for the specified type name.
|
||||
*
|
||||
* @param accountTypeName account type name to look for
|
||||
*
|
||||
* @return CR account type
|
||||
*
|
||||
* @throws CentralRepoException
|
||||
@ -1152,8 +1150,8 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
private CentralRepoAccountType getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
|
||||
|
||||
String sql = "SELECT * FROM account_types WHERE type_name = ?";
|
||||
try ( Connection conn = connect();
|
||||
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
|
||||
try (Connection conn = connect();
|
||||
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
|
||||
|
||||
preparedStatement.setString(1, accountTypeName);
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery();) {
|
||||
@ -1172,15 +1170,18 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CR account with the given account type and the unique account identifier.
|
||||
* Looks in the cache first.
|
||||
* If not found in cache, reads from the database and saves in cache.
|
||||
* Get the CR account with the given account type and the unique account
|
||||
* identifier. Looks in the cache first. If not found in cache, reads from
|
||||
* 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
|
||||
* @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
|
||||
* found.
|
||||
*
|
||||
* @throws CentralRepoException
|
||||
*/
|
||||
@ -1197,26 +1198,25 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
return crAccount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the Account with the given account type and account identifier,
|
||||
* from the database.
|
||||
* Get the Account with the given account type and account identifier, from
|
||||
* the database.
|
||||
*
|
||||
* @param accountType account type
|
||||
* @param accountType account type
|
||||
* @param accountUniqueID unique account identifier
|
||||
*
|
||||
* @return Account, returns NULL is no matching account found
|
||||
*
|
||||
* @throws TskCoreException exception thrown if a critical error occurs
|
||||
* within TSK core
|
||||
* within TSK core
|
||||
*/
|
||||
private CentralRepoAccount getCRAccountFromDb(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException {
|
||||
|
||||
CentralRepoAccount account = null;
|
||||
|
||||
String sql = "SELECT * FROM accounts WHERE account_type_id = ? AND account_unique_identifier = ?";
|
||||
try ( Connection connection = connect();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql);) {
|
||||
try (Connection connection = connect();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql);) {
|
||||
|
||||
preparedStatement.setInt(1, crAccountType.getAccountTypeId());
|
||||
preparedStatement.setString(2, accountUniqueID);
|
||||
@ -1233,7 +1233,6 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
return account;
|
||||
}
|
||||
|
||||
|
||||
private void checkAddArtifactInstanceNulls(CorrelationAttributeInstance eamArtifact) throws CentralRepoException {
|
||||
if (eamArtifact == null) {
|
||||
throw new CentralRepoException("CorrelationAttribute is null");
|
||||
@ -1574,7 +1573,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
|
||||
synchronized (bulkArtifacts) {
|
||||
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);
|
||||
bulkArtifactsCount++;
|
||||
@ -2001,8 +2000,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
String sqlUpdate
|
||||
= "UPDATE "
|
||||
+ tableName
|
||||
+ " SET known_status=?, comment=? "
|
||||
+ "WHERE id=?";
|
||||
+ " SET known_status=? WHERE id=?";
|
||||
|
||||
try {
|
||||
preparedQuery = conn.prepareStatement(sqlQuery);
|
||||
@ -2016,15 +2014,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
preparedUpdate = conn.prepareStatement(sqlUpdate);
|
||||
|
||||
preparedUpdate.setByte(1, knownStatus.getFileKnownValue());
|
||||
// NOTE: if the user tags the same instance as BAD multiple times,
|
||||
// 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.setInt(2, instance_id);
|
||||
|
||||
preparedUpdate.executeUpdate();
|
||||
} else {
|
||||
@ -2307,6 +2297,42 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
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
|
||||
*
|
||||
@ -2697,15 +2723,16 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the examiner table for the given user name.
|
||||
* Adds a row if the user is not found in the examiner table.
|
||||
* Queries the examiner table for the given user name. Adds a row if the
|
||||
* user is not found in the examiner table.
|
||||
*
|
||||
* @param examinerLoginName user name to look for.
|
||||
*
|
||||
* @return CentralRepoExaminer for the given user name.
|
||||
*
|
||||
* @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
|
||||
public CentralRepoExaminer getOrInsertExaminer(String examinerLoginName) throws CentralRepoException {
|
||||
|
||||
@ -3145,7 +3172,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
typeId = newCorrelationTypeKnownId(newType);
|
||||
}
|
||||
|
||||
synchronized(typeCache) {
|
||||
synchronized (typeCache) {
|
||||
typeCache.put(newType.getId(), newType);
|
||||
}
|
||||
return typeId;
|
||||
@ -3362,7 +3389,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
preparedStatement.setInt(4, aType.isEnabled() ? 1 : 0);
|
||||
preparedStatement.setInt(5, aType.getId());
|
||||
preparedStatement.executeUpdate();
|
||||
synchronized(typeCache) {
|
||||
synchronized (typeCache) {
|
||||
typeCache.put(aType.getId(), aType);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
@ -3386,7 +3413,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
@Override
|
||||
public CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws CentralRepoException {
|
||||
try {
|
||||
synchronized(typeCache) {
|
||||
synchronized (typeCache) {
|
||||
return typeCache.get(typeId, () -> getCorrelationTypeByIdFromCr(typeId));
|
||||
}
|
||||
} 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
|
||||
*
|
||||
@ -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.
|
||||
*/
|
||||
private void getCorrelationTypesFromCr() throws CentralRepoException {
|
||||
|
||||
// clear out the cache
|
||||
synchronized(typeCache) {
|
||||
synchronized (typeCache) {
|
||||
typeCache.invalidateAll();
|
||||
isCRTypeCacheInitialized = false;
|
||||
}
|
||||
|
||||
String sql = "SELECT * FROM correlation_types";
|
||||
try ( Connection conn = connect();
|
||||
PreparedStatement preparedStatement = conn.prepareStatement(sql);
|
||||
ResultSet resultSet = preparedStatement.executeQuery();) {
|
||||
try (Connection conn = connect();
|
||||
PreparedStatement preparedStatement = conn.prepareStatement(sql);
|
||||
ResultSet resultSet = preparedStatement.executeQuery();) {
|
||||
|
||||
synchronized(typeCache) {
|
||||
synchronized (typeCache) {
|
||||
while (resultSet.next()) {
|
||||
CorrelationAttributeInstance.Type aType = getCorrelationTypeFromResultSet(resultSet);
|
||||
typeCache.put(aType.getId(), aType);
|
||||
@ -3622,7 +3649,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
case POSTGRESQL:
|
||||
return "INSERT " + sql + " ON CONFLICT DO NOTHING"; //NON-NLS
|
||||
case SQLITE:
|
||||
return "INSERT OR IGNORE " + sql;
|
||||
return "INSERT OR IGNORE " + sql;
|
||||
|
||||
default:
|
||||
throw new CentralRepoException("Unknown Central Repo DB platform" + CentralRepoDbManager.getSavedDbChoice().getDbPlatform());
|
||||
|
@ -27,7 +27,6 @@ import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
@ -55,6 +54,8 @@ import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
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
|
||||
@ -86,6 +87,10 @@ final class CaseEventListener implements PropertyChangeListener {
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (!(evt instanceof AutopsyEvent) || (((AutopsyEvent) evt).getSourceType() != AutopsyEvent.SourceType.LOCAL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CentralRepository dbManager;
|
||||
try {
|
||||
dbManager = CentralRepository.getInstance();
|
||||
@ -142,6 +147,46 @@ final class CaseEventListener implements PropertyChangeListener {
|
||||
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 CentralRepository dbManager;
|
||||
@ -158,72 +203,98 @@ final class CaseEventListener implements PropertyChangeListener {
|
||||
return;
|
||||
}
|
||||
|
||||
AbstractFile af;
|
||||
TskData.FileKnown knownStatus;
|
||||
String comment;
|
||||
if (Case.Events.valueOf(event.getPropertyName()) == Case.Events.CONTENT_TAG_ADDED) {
|
||||
// For added tags, we want to change the known status to BAD if the
|
||||
// tag that was just added is in the list of central repo tags.
|
||||
final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) event;
|
||||
final ContentTag tagAdded = tagAddedEvent.getAddedTag();
|
||||
Case.Events curEventType = Case.Events.valueOf(event.getPropertyName());
|
||||
if (curEventType == Case.Events.CONTENT_TAG_ADDED && event instanceof ContentTagAddedEvent) {
|
||||
handleTagAdded((ContentTagAddedEvent) event);
|
||||
} else if (curEventType == Case.Events.CONTENT_TAG_DELETED && event instanceof ContentTagDeletedEvent) {
|
||||
handleTagDeleted((ContentTagDeletedEvent) event);
|
||||
} else {
|
||||
LOGGER.log(Level.SEVERE,
|
||||
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())) {
|
||||
if (tagAdded.getContent() instanceof AbstractFile) {
|
||||
af = (AbstractFile) tagAdded.getContent();
|
||||
knownStatus = TskData.FileKnown.BAD;
|
||||
comment = tagAdded.getComment();
|
||||
} 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;
|
||||
}
|
||||
private void handleTagDeleted(ContentTagDeletedEvent evt) {
|
||||
// ensure tag deleted event has a valid content id
|
||||
if (evt.getDeletedTagInfo() == null) {
|
||||
LOGGER.log(Level.SEVERE, "ContentTagDeletedEvent did not have valid content to provide a content id.");
|
||||
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);
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
} // CONTENT_TAG_ADDED, CONTENT_TAG_DELETED
|
||||
}
|
||||
}
|
||||
|
||||
private final class BlackboardTagTask implements Runnable {
|
||||
@ -253,87 +324,125 @@ final class CaseEventListener implements PropertyChangeListener {
|
||||
return;
|
||||
}
|
||||
|
||||
Content content;
|
||||
BlackboardArtifact bbArtifact;
|
||||
TskData.FileKnown knownStatus;
|
||||
String comment;
|
||||
if (Case.Events.valueOf(event.getPropertyName()) == Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED) {
|
||||
// For added tags, we want to change the known status to BAD if the
|
||||
// tag that was just added is in the list of central repo tags.
|
||||
final BlackBoardArtifactTagAddedEvent tagAddedEvent = (BlackBoardArtifactTagAddedEvent) event;
|
||||
final BlackboardArtifactTag tagAdded = tagAddedEvent.getAddedTag();
|
||||
|
||||
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;
|
||||
}
|
||||
Case.Events curEventType = Case.Events.valueOf(event.getPropertyName());
|
||||
if (curEventType == Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED && event instanceof BlackBoardArtifactTagAddedEvent) {
|
||||
handleTagAdded((BlackBoardArtifactTagAddedEvent) event);
|
||||
} else if (curEventType == Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED && event instanceof BlackBoardArtifactTagDeletedEvent) {
|
||||
handleTagDeleted((BlackBoardArtifactTagDeletedEvent) event);
|
||||
} else {
|
||||
LOGGER.log(Level.WARNING,
|
||||
String.format("Received an event %s of type %s and was expecting either CONTENT_TAG_ADDED or CONTENT_TAG_DELETED.",
|
||||
event, curEventType));
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
|
||||
eamArtifact.setComment(comment);
|
||||
try {
|
||||
dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus);
|
||||
} catch (CentralRepoException ex) {
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -435,7 +544,7 @@ final class CaseEventListener implements PropertyChangeListener {
|
||||
if (!hasTagWithConflictingKnownStatus) {
|
||||
Content taggedContent = contentTag.getContent();
|
||||
if (taggedContent instanceof AbstractFile) {
|
||||
final CorrelationAttributeInstance eamArtifact = CorrelationAttributeUtil.makeCorrAttrFromFile((AbstractFile)taggedContent);
|
||||
final CorrelationAttributeInstance eamArtifact = CorrelationAttributeUtil.makeCorrAttrFromFile((AbstractFile) taggedContent);
|
||||
if (eamArtifact != null) {
|
||||
CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
|
||||
}
|
||||
|
@ -20,11 +20,19 @@ package org.sleuthkit.autopsy.contentviewers;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
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.openide.nodes.Node;
|
||||
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.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
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.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||
@ -46,26 +52,190 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
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.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 8)
|
||||
@NbBundle.Messages({
|
||||
@Messages({
|
||||
"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 {
|
||||
|
||||
/**
|
||||
* 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 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.
|
||||
*/
|
||||
public AnnotationsContentViewer() {
|
||||
initComponents();
|
||||
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
|
||||
@ -75,7 +245,8 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder html = new StringBuilder();
|
||||
Document html = Jsoup.parse(EMPTY_HTML);
|
||||
Element body = html.getElementsByTag("body").first();
|
||||
|
||||
BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class);
|
||||
Content sourceFile = null;
|
||||
@ -101,279 +272,439 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
|
||||
artifact.getDisplayName(), artifact.getArtifactID()), ex);
|
||||
}
|
||||
|
||||
boolean somethingWasRendered = false;
|
||||
if (artifact != null) {
|
||||
populateTagData(html, artifact, sourceFile);
|
||||
somethingWasRendered = renderArtifact(body, artifact, sourceFile);
|
||||
} else {
|
||||
populateTagData(html, sourceFile);
|
||||
somethingWasRendered = renderContent(body, sourceFile, false);
|
||||
}
|
||||
|
||||
if (sourceFile instanceof AbstractFile) {
|
||||
populateCentralRepositoryData(html, artifact, (AbstractFile) sourceFile);
|
||||
if (!somethingWasRendered) {
|
||||
appendMessage(body, Bundle.AnnotationsContentViewer_onEmpty());
|
||||
}
|
||||
|
||||
setText(html.toString());
|
||||
jTextPane1.setText(html.html());
|
||||
jTextPane1.setCaretPosition(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the "Selected Item" sections with tag data for the supplied
|
||||
* content.
|
||||
* Renders annotations for an artifact.
|
||||
*
|
||||
* @param html The HTML text to update.
|
||||
* @param content Selected content.
|
||||
* @param parent The html element to render content int.
|
||||
* @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 {
|
||||
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
|
||||
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);
|
||||
return tskCase.getContentTagsByContent(sourceContent);
|
||||
} 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 tags from the case database.", ex); //NON-NLS
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the "Selected Item" and "Source File" sections with tag data for
|
||||
* a supplied artifact.
|
||||
* Retrieves tags for blackboard artifact tags.
|
||||
*
|
||||
* @param html The HTML text to update.
|
||||
* @param artifact A selected artifact.
|
||||
* @param sourceFile The source content of the selected artifact.
|
||||
* @param bba The blackboard artifact for which to retrieve tags.
|
||||
*
|
||||
* @return The found tags.
|
||||
*/
|
||||
private void populateTagData(StringBuilder html, BlackboardArtifact artifact, Content sourceFile) {
|
||||
private static List<BlackboardArtifactTag> getTags(BlackboardArtifact bba) {
|
||||
try {
|
||||
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
|
||||
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);
|
||||
}
|
||||
return tskCase.getBlackboardArtifactTagsByArtifact(bba);
|
||||
} 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 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
|
||||
* artifact.
|
||||
*
|
||||
* @return The Correlation Attribute Instances associated with the
|
||||
* sourcefile that have comments.
|
||||
*/
|
||||
private void populateCentralRepositoryData(StringBuilder html, BlackboardArtifact artifact, AbstractFile sourceFile) {
|
||||
if (CentralRepository.isEnabled()) {
|
||||
startSection(html, "Central Repository Comments");
|
||||
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);
|
||||
private static List<CorrelationAttributeInstance> getCentralRepositoryData(AbstractFile sourceFile) {
|
||||
if (sourceFile == null || StringUtils.isEmpty(sourceFile.getMd5Hash())) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
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) {
|
||||
jTextPane1.setText("<html><body>" + text + "</body></html>"); //NON-NLS
|
||||
private static List<CorrelationAttributeInstance> getCorrelationAttributeComments(List<Pair<CorrelationAttributeInstance.Type, String>> lookupKeys) {
|
||||
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 sectionName The name of the section.
|
||||
* @param parent The parent element for which the entries will be
|
||||
* 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) {
|
||||
html.append("<p style=\"font-size:14px;font-weight:bold;\">")
|
||||
.append(sectionName)
|
||||
.append("</p><br>"); //NON-NLS
|
||||
private static <T> boolean appendEntries(Element parent, SectionConfig<T> config, List<? extends T> items,
|
||||
boolean isSubsection) {
|
||||
if (items == null || items.isEmpty()) {
|
||||
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 message The message text.
|
||||
* @param parent The parent to append the table.
|
||||
* @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) {
|
||||
html.append("<p style=\"font-size:11px;font-style:italic;\">")
|
||||
.append(message)
|
||||
.append("</p><br>"); //NON-NLS
|
||||
private static <T> Element appendVerticalEntryTables(Element parent, List<? extends T> items, List<ItemEntry<T>> rowHeaders) {
|
||||
boolean isFirst = true;
|
||||
for (T item : items) {
|
||||
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 tag The tag whose information will be used to populate the table.
|
||||
* @param parent The parent element that will have a table appended
|
||||
* 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({
|
||||
"AnnotationsContentViewer.tagEntryDataLabel.tag=Tag:",
|
||||
"AnnotationsContentViewer.tagEntryDataLabel.tagUser=Tag User:",
|
||||
"AnnotationsContentViewer.tagEntryDataLabel.comment=Comment:"
|
||||
})
|
||||
private void addTagEntry(StringBuilder html, Tag tag) {
|
||||
startTable(html);
|
||||
addRow(html, Bundle.AnnotationsContentViewer_tagEntryDataLabel_tag(), tag.getName().getDisplayName());
|
||||
addRow(html, Bundle.AnnotationsContentViewer_tagEntryDataLabel_tagUser(), tag.getUserName());
|
||||
addRow(html, Bundle.AnnotationsContentViewer_tagEntryDataLabel_comment(), formatHtmlString(tag.getComment()));
|
||||
endTable(html);
|
||||
private static Element appendTable(Element parent, int columnNumber, List<List<String>> content, List<String> columnHeaders) {
|
||||
Element table = parent.appendElement("table");
|
||||
if (columnHeaders != null && !columnHeaders.isEmpty()) {
|
||||
Element header = table.appendElement("thead");
|
||||
appendRow(header, columnHeaders, columnNumber, true);
|
||||
}
|
||||
Element tableBody = table.appendElement("tbody");
|
||||
|
||||
content.forEach((rowData) -> appendRow(tableBody, rowData, columnNumber, false));
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a data table containing information about a correlation attribute
|
||||
* instance in the Central Repository.
|
||||
* Appends a row to the parent element (should be thead or tbody).
|
||||
*
|
||||
* @param html The HTML text to add the table to.
|
||||
* @param attributeInstance The attribute instance whose information will be
|
||||
* used to populate the table.
|
||||
* @param rowParent The parent table element.
|
||||
* @param data The data to place in columns within 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({
|
||||
"AnnotationsContentViewer.centralRepositoryEntryDataLabel.case=Case:",
|
||||
"AnnotationsContentViewer.centralRepositoryEntryDataLabel.type=Type:",
|
||||
"AnnotationsContentViewer.centralRepositoryEntryDataLabel.comment=Comment:",
|
||||
"AnnotationsContentViewer.centralRepositoryEntryDataLabel.path=Path:"
|
||||
})
|
||||
private void addCentralRepositoryEntry(StringBuilder html, CorrelationAttributeInstance attributeInstance) {
|
||||
startTable(html);
|
||||
addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_case(), attributeInstance.getCorrelationCase().getDisplayName());
|
||||
addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_type(), attributeInstance.getCorrelationType().getDisplayName());
|
||||
addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_comment(), formatHtmlString(attributeInstance.getComment()));
|
||||
addRow(html, Bundle.AnnotationsContentViewer_centralRepositoryEntryDataLabel_path(), attributeInstance.getFilePath());
|
||||
endTable(html);
|
||||
private static Element appendRow(Element rowParent, List<String> data, int columnNumber, boolean isHeader) {
|
||||
String cellType = isHeader ? "th" : "td";
|
||||
Element row = rowParent.appendElement("tr");
|
||||
for (int i = 0; i < columnNumber; i++) {
|
||||
Element cell = row.appendElement(cellType);
|
||||
if (data != null && i < data.size()) {
|
||||
cell.text(StringUtils.isEmpty(data.get(i)) ? "" : data.get(i));
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
html.append("<table>"); //NON-NLS
|
||||
private static Element appendSection(Element parent, String headerText) {
|
||||
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 key The key for the left column of the data row.
|
||||
* @param value The value for the right column of the data row.
|
||||
* @param parent The element to append this subsection to.
|
||||
* @param headerText The text for the subsection.
|
||||
*
|
||||
* @return The div for the new subsection.
|
||||
*/
|
||||
private void addRow(StringBuilder html, String key, String value) {
|
||||
html.append("<tr><td valign=\"top\">"); //NON-NLS
|
||||
html.append(key);
|
||||
html.append("</td><td>"); //NON-NLS
|
||||
html.append(value);
|
||||
html.append("</td></tr>"); //NON-NLS
|
||||
private static Element appendSubsection(Element parent, String headerText) {
|
||||
Element subsectionDiv = parent.appendElement("div");
|
||||
subsectionDiv.attr("class", SUBSECTION_CLASSNAME);
|
||||
Element header = subsectionDiv.appendElement("h2");
|
||||
header.text(headerText);
|
||||
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) {
|
||||
html.append("</table><br><br>"); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>");
|
||||
private static Element appendMessage(Element parent, String message) {
|
||||
Element messageEl = parent.appendElement("p");
|
||||
messageEl.text(message);
|
||||
messageEl.attr("class", MESSAGE_CLASSNAME);
|
||||
return messageEl;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -462,6 +793,6 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
|
||||
|
||||
@Override
|
||||
public void resetComponent() {
|
||||
setText("");
|
||||
jTextPane1.setText(EMPTY_HTML);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
@ -954,3 +954,9 @@ manager.properties.lafError =\
|
||||
manager.properties.brokenProperty = Broken 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
|
||||
|
@ -15,13 +15,22 @@
|
||||
# governing permissions and limitations under the License.
|
||||
#
|
||||
|
||||
AnnotationsContentViewer.centralRepositoryEntry.title=Central Repository Comments
|
||||
AnnotationsContentViewer.centralRepositoryEntryDataLabel.case=Case:
|
||||
AnnotationsContentViewer.centralRepositoryEntryDataLabel.comment=Comment:
|
||||
AnnotationsContentViewer.centralRepositoryEntryDataLabel.path=Path:
|
||||
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.tag=Tag:
|
||||
AnnotationsContentViewer.tagEntryDataLabel.tagUser=Tag User:
|
||||
AnnotationsContentViewer.tagEntryDataLabel.tagUser=Examiner:
|
||||
AnnotationsContentViewer.title=Annotations
|
||||
AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content.
|
||||
ApplicationContentViewer.title=Application
|
||||
|
@ -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}
|
||||
# {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}
|
||||
DefaultArtifactContentViewer.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e
|
||||
DefaultArtifactContentViewer.copyMenuItem.text=\u30b3\u30d4\u30fc
|
||||
|
@ -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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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>
|
@ -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
|
||||
}
|
@ -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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -30,8 +30,6 @@ DataContentViewerArtifact.pageLabel2.text=Result
|
||||
DataContentViewerArtifact.nextPageButton.text=
|
||||
DataContentViewerArtifact.currentPageLabel.text=1
|
||||
DataContentViewerArtifact.ofLabel.text=of
|
||||
DataContentViewerArtifact.copyMenuItem.text=Copy
|
||||
DataContentViewerArtifact.selectAllMenuItem.text=Select All
|
||||
DataContentViewerArtifact.pageLabel.text=Result:
|
||||
AdvancedConfigurationDialog.applyButton.text=OK
|
||||
DataContentViewerHex.goToPageTextField.text=
|
||||
|
@ -82,8 +82,6 @@ DataContentViewerArtifact.pageLabel2.text=\u7d50\u679c
|
||||
DataContentViewerArtifact.nextPageButton.text=
|
||||
DataContentViewerArtifact.currentPageLabel.text=1
|
||||
DataContentViewerArtifact.ofLabel.text=/
|
||||
DataContentViewerArtifact.copyMenuItem.text=\u30b3\u30d4\u30fc
|
||||
DataContentViewerArtifact.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e
|
||||
DataContentViewerArtifact.pageLabel.text=\u7d50\u679c:
|
||||
AdvancedConfigurationDialog.applyButton.text=OK
|
||||
DataContentViewerHex.goToPageTextField.text=
|
||||
|
@ -1,30 +1,6 @@
|
||||
<?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/corecomponents/Bundle.properties" key="DataContentViewerArtifact.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</MenuItem>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</NonVisualComponents>
|
||||
<Form version="1.9" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[100, 58]"/>
|
||||
@ -45,16 +21,16 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jScrollPane1" max="32767" attributes="0"/>
|
||||
<Component id="resultsTableScrollPane" max="32767" attributes="0"/>
|
||||
<Component id="jScrollPane1" pref="561" max="32767" attributes="0"/>
|
||||
<Component id="artifactContentPanel" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="jScrollPane1" min="-2" pref="24" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="resultsTableScrollPane" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="artifactContentPanel" pref="397" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -248,19 +224,34 @@
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<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 class="javax.swing.JPanel" name="artifactContentPanel">
|
||||
<LayoutCode>
|
||||
<CodeStatement>
|
||||
<CodeExpression id="1_artifactContentPanel">
|
||||
<CodeVariable name="artifactContentPanel" type="8194" declaredType="javax.swing.JPanel"/>
|
||||
<ExpressionOrigin>
|
||||
<ExpressionProvider type="ComponentRef">
|
||||
<ComponentRef name="artifactContentPanel"/>
|
||||
</ExpressionProvider>
|
||||
</ExpressionOrigin>
|
||||
</CodeExpression>
|
||||
<StatementProvider type="CodeMethod">
|
||||
<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>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -20,178 +20,68 @@ package org.sleuthkit.autopsy.corecomponents;
|
||||
|
||||
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.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JTextArea;
|
||||
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.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskException;
|
||||
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.Map;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import org.sleuthkit.autopsy.contentviewers.ArtifactContentViewer;
|
||||
import org.sleuthkit.autopsy.contentviewers.ContactArtifactViewer;
|
||||
import org.sleuthkit.autopsy.contentviewers.DefaultArtifactContentViewer;
|
||||
|
||||
/**
|
||||
* Instances of this class display the BlackboardArtifacts associated with the
|
||||
* Content represented by a Node. Each BlackboardArtifact is rendered displayed
|
||||
* in a JTable representation of its BlackboardAttributes.
|
||||
* Content represented by a Node.
|
||||
*
|
||||
* 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)
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@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.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 String WAIT_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.waitText");
|
||||
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 int currentPage = 1;
|
||||
private final Object lock = new Object();
|
||||
private List<ResultsTableArtifact> artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents()
|
||||
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 List<BlackboardArtifact> artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents()
|
||||
private SwingWorker<ViewUpdate, Void> currentTask; // Accessed by multiple threads, use startNewTask()
|
||||
|
||||
|
||||
private final Collection<ArtifactContentViewer> KNOWN_ARTIFACT_VIEWERS =
|
||||
Arrays.asList(
|
||||
new ContactArtifactViewer()
|
||||
);
|
||||
|
||||
public DataContentViewerArtifact() {
|
||||
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;
|
||||
|
||||
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() {
|
||||
java.awt.GridBagConstraints gridBagConstraints;
|
||||
|
||||
rightClickMenu = new javax.swing.JPopupMenu();
|
||||
copyMenuItem = new javax.swing.JMenuItem();
|
||||
selectAllMenuItem = new javax.swing.JMenuItem();
|
||||
jScrollPane1 = new javax.swing.JScrollPane();
|
||||
jPanel1 = new javax.swing.JPanel();
|
||||
totalPageLabel = new javax.swing.JLabel();
|
||||
@ -218,13 +105,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
||||
prevPageButton = new javax.swing.JButton();
|
||||
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));
|
||||
resultsTableScrollPane = new javax.swing.JScrollPane();
|
||||
|
||||
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);
|
||||
artifactContentPanel = new javax.swing.JPanel();
|
||||
|
||||
setPreferredSize(new java.awt.Dimension(100, 58));
|
||||
|
||||
@ -341,43 +222,41 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
||||
|
||||
jScrollPane1.setViewportView(jPanel1);
|
||||
|
||||
resultsTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
|
||||
resultsTableScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
resultsTableScrollPane.setPreferredSize(new java.awt.Dimension(620, 34));
|
||||
artifactContentPanel.setLayout(new javax.swing.OverlayLayout(artifactContentPanel));
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jScrollPane1)
|
||||
.addComponent(resultsTableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 561, Short.MAX_VALUE)
|
||||
.addComponent(artifactContentPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.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
|
||||
|
||||
private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
|
||||
currentPage = currentPage + 1;
|
||||
currentPage += 1;
|
||||
currentPageLabel.setText(Integer.toString(currentPage));
|
||||
artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName());
|
||||
artifactLabel.setText(artifactTableContents.get(currentPage - 1).getDisplayName());
|
||||
startNewTask(new SelectedArtifactChangedTask(currentPage));
|
||||
}//GEN-LAST:event_nextPageButtonActionPerformed
|
||||
|
||||
private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
|
||||
currentPage = currentPage - 1;
|
||||
currentPage -= 1;
|
||||
currentPageLabel.setText(Integer.toString(currentPage));
|
||||
artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName());
|
||||
artifactLabel.setText(artifactTableContents.get(currentPage - 1).getDisplayName());
|
||||
startNewTask(new SelectedArtifactChangedTask(currentPage));
|
||||
}//GEN-LAST:event_prevPageButtonActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JPanel artifactContentPanel;
|
||||
private javax.swing.JLabel artifactLabel;
|
||||
private javax.swing.JMenuItem copyMenuItem;
|
||||
private javax.swing.JLabel currentPageLabel;
|
||||
private javax.swing.Box.Filler filler1;
|
||||
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 pageLabel2;
|
||||
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;
|
||||
// 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.
|
||||
@ -431,10 +278,12 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
||||
currentPageLabel.setText("");
|
||||
artifactLabel.setText("");
|
||||
totalPageLabel.setText("");
|
||||
((DefaultTableModel) resultsTable.getModel()).setRowCount(0);
|
||||
|
||||
prevPageButton.setEnabled(false);
|
||||
nextPageButton.setEnabled(false);
|
||||
currentNode = null;
|
||||
|
||||
artifactContentPanel.removeAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -521,182 +370,12 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ArtifactContentViewer getSupportingViewer(BlackboardArtifact artifact) {
|
||||
|
||||
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 = " ";
|
||||
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;
|
||||
}
|
||||
return this.KNOWN_ARTIFACT_VIEWERS.stream()
|
||||
.filter(knownViewer -> knownViewer.isSupported(artifact))
|
||||
.findAny()
|
||||
.orElse(new DefaultArtifactContentViewer());
|
||||
|
||||
}
|
||||
|
||||
@ -708,18 +387,21 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
||||
|
||||
int numberOfPages;
|
||||
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.numberOfPages = numberOfPages;
|
||||
this.tableContents = contents;
|
||||
this.artifact = artifact;
|
||||
this.errorMsg = null;
|
||||
}
|
||||
|
||||
ViewUpdate(int numberOfPages, int currentPage, String errorMsg) {
|
||||
this.currentPage = currentPage;
|
||||
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;
|
||||
totalPageLabel.setText(Integer.toString(viewUpdate.numberOfPages));
|
||||
currentPageLabel.setText(Integer.toString(currentPage));
|
||||
artifactLabel.setText(viewUpdate.tableContents.getArtifactDisplayName());
|
||||
DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel());
|
||||
tModel.setDataVector(viewUpdate.tableContents.getRows(), COLUMN_HEADERS);
|
||||
updateColumnSizes();
|
||||
updateRowHeights();
|
||||
resultsTable.clearSelection();
|
||||
|
||||
|
||||
artifactContentPanel.removeAll();
|
||||
|
||||
if (viewUpdate.artifact != null) {
|
||||
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.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.
|
||||
*/
|
||||
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.
|
||||
if (currentTask != null) {
|
||||
// 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
|
||||
* artifacts.
|
||||
*/
|
||||
private void setArtifactContents(List<ResultsTableArtifact> artifactList) {
|
||||
private void setArtifactContents(List<BlackboardArtifact> artifactList) {
|
||||
synchronized (lock) {
|
||||
this.artifactTableContents = artifactList;
|
||||
}
|
||||
@ -789,11 +477,11 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
||||
/**
|
||||
* 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) {
|
||||
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.
|
||||
ArrayList<ResultsTableArtifact> artifactContents = new ArrayList<>();
|
||||
ArrayList<BlackboardArtifact> artifactContents = new ArrayList<>();
|
||||
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,
|
||||
@ -932,14 +620,14 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
||||
protected ViewUpdate doInBackground() {
|
||||
// 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.
|
||||
List<ResultsTableArtifact> artifactContents = getArtifactContents();
|
||||
ResultsTableArtifact artifactContent = artifactContents.get(pageIndex - 1);
|
||||
List<BlackboardArtifact> artifactContents = getArtifactContents();
|
||||
|
||||
// It may take a considerable amount of time to fetch the attributes of the selected artifact so check for cancellation.
|
||||
if (isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BlackboardArtifact artifactContent = artifactContents.get(pageIndex - 1);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import javax.swing.JOptionPane;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
|
||||
import org.sleuthkit.datamodel.HashEntry;
|
||||
@ -46,7 +47,14 @@ public class AddHashValuesToDatabaseProgressDialog extends javax.swing.JDialog {
|
||||
private final HashDb hashDb;
|
||||
private final List<HashEntry> hashes;
|
||||
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 errorMessage;
|
||||
private final String text;
|
||||
@ -64,7 +72,6 @@ public class AddHashValuesToDatabaseProgressDialog extends javax.swing.JDialog {
|
||||
display(parent);
|
||||
this.hashes = new ArrayList<>();
|
||||
this.invalidHashes = new ArrayList<>();
|
||||
this.md5Pattern = Pattern.compile("^([a-fA-F0-9]{32})"); // NON-NLS
|
||||
this.parentRef = parent;
|
||||
this.hashDb = hashDb;
|
||||
this.text = text;
|
||||
@ -161,17 +168,15 @@ public class AddHashValuesToDatabaseProgressDialog extends javax.swing.JDialog {
|
||||
// These entries may be of <MD5> or <MD5, comment> format
|
||||
for (String hashEntry : linesInTextArea) {
|
||||
hashEntry = hashEntry.trim();
|
||||
Matcher m = md5Pattern.matcher(hashEntry);
|
||||
Matcher m = HASH_LINE_PATTERN.matcher(hashEntry);
|
||||
if (m.find()) {
|
||||
// Is there any text left on this line? If so, treat it as a comment.
|
||||
String comment = hashEntry.substring(m.end()).trim();
|
||||
if (comment.length() > 0) {
|
||||
comment = (comment.charAt(0) == ',') ? comment.substring(1) : comment;
|
||||
hashes.add(new HashEntry(null, m.group(0), null, null, comment));
|
||||
} else {
|
||||
// more information can be added to the HashEntry - sha-1, sha-512, comment
|
||||
hashes.add(new HashEntry(null, m.group(0), null, null, null));
|
||||
}
|
||||
String hash = m.group(HASH_GROUP);
|
||||
|
||||
// if there was a match and the match is not empty, assign to comment
|
||||
String comment = StringUtils.isNotBlank(m.group(COMMENT_GROUP)) ?
|
||||
m.group(COMMENT_GROUP).trim() : null;
|
||||
|
||||
hashes.add(new HashEntry(null, hash, null, null, comment));
|
||||
} else {
|
||||
if (!hashEntry.isEmpty()) {
|
||||
invalidHashes.add(hashEntry);
|
||||
|
@ -94,8 +94,6 @@ HashDbIngestModule.fileReadErrorMsg=Read Error: {0}
|
||||
HashDbIngestModule.calcHashValueErr=Error encountered while calculating the hash value for {0} ({1}).
|
||||
HashDbIngestModule.hashLookupErrorMsg=Hash Lookup Error: {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.md5Hash=MD5 Hash
|
||||
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.illegalHashDbFileNameExtensionMsg=The hash set file name must have a .{0} extension.
|
||||
HashDbManager.moduleErr=Module Error
|
||||
HashDbManager.knownBad.text=Notable
|
||||
HashDbManager.known.text=Known
|
||||
HashDbManager.fileNameExtensionFilter.title=Hash Set File
|
||||
HashDbSearchAction.dlgMsg.title=File Search by MD5 Hash
|
||||
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.saveErrorExceptionMsg=Error saving hash configuration
|
||||
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.file=file
|
||||
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.toolTipText=In Live Triage situations, this option ensures that path to the hash set will be valid
|
||||
HashLookupSettingsPanel.indexPathLabel.text=
|
||||
HashLookupModuleSettingsPanel.hashDbsLabel.text=Select hash sets to use:
|
||||
HashDbCreateDatabaseDialog.noChangeRadioButton.text=No Change
|
||||
HashDbImportDatabaseDialog.noChangeRadioButton.toolTipText=
|
||||
HashDbImportDatabaseDialog.noChangeRadioButton.text=No Change
|
||||
|
@ -9,6 +9,7 @@ HashDbImportDatabaseDialog.missingOrg=An organization must be selected
|
||||
HashDbImportDatabaseDialog.missingVersion=A version must be entered
|
||||
HashDbImportDatabaseDialog.mustEnterHashSetNameMsg=A hash set name must be entered.
|
||||
HashDbImportDatabaseDialog.populateOrgsError.message=Failure loading organizations.
|
||||
HashDbIngestModule.complete.noChangesFound=No Change items found:
|
||||
# {0} - File name
|
||||
HashDbIngestModule.dialogTitle.errorFindingArtifacts=Error Finding Artifacts: {0}
|
||||
# {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.knownBadFileSearchWillNotExecuteWarn=Notable 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.noKnownHashDbSetMsg=No known hash set.
|
||||
HashDbManager.CentralRepoHashDb.orgError=Error loading organization
|
||||
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
|
||||
HashDbManager.noDbPath.message=Couldn't get valid hash set path for: {0}
|
||||
HashDbSearchAction.noOpenCase.errMsg=No open case available.
|
||||
@ -49,7 +61,10 @@ ImportCentralRepoDbProgressDialog.errorParsingFile.message=Error parsing hash se
|
||||
ImportCentralRepoDbProgressDialog.linesProcessed.message=\ hashes processed
|
||||
ImportCentralRepoDbProgressDialog.title.text=Central Repository Import Progress
|
||||
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
|
||||
OptionsCategory_Name_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.hashLookupErrorMsg=Hash Lookup Error: {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.md5Hash=MD5 Hash
|
||||
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.
|
||||
ModalNoButtons.indexingDbsTitle=Indexing hash sets
|
||||
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.indexThis.currentlyIndexing1Db=Currently indexing 1 hash set
|
||||
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.illegalHashDbFileNameExtensionMsg=The hash set file name must have a .{0} extension.
|
||||
HashDbManager.moduleErr=Module Error
|
||||
HashDbManager.knownBad.text=Notable
|
||||
HashDbManager.known.text=Known
|
||||
HashDbManager.fileNameExtensionFilter.title=Hash Set File
|
||||
HashDbSearchAction.dlgMsg.title=File Search by MD5 Hash
|
||||
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)
|
||||
HashDbManager.ingestRunningExceptionMsg=Ingest is ongoing; this service will be unavailable until it finishes.
|
||||
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.knownHashDbsLabel.text=Select known hash sets to use:
|
||||
HashLookupModuleSettingsPanel.knownBadHashDbsLabel.text=Select notable hash sets to use:
|
||||
AddContentToHashDbAction.addFilesToHashSet.files=files
|
||||
AddContentToHashDbAction.addFilesToHashSet.file=file
|
||||
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.toolTipText=In Live Triage situations, this option ensures that path to the hash set will be valid
|
||||
HashLookupSettingsPanel.indexPathLabel.text=
|
||||
HashLookupModuleSettingsPanel.hashDbsLabel.text=Select hash sets to use:
|
||||
HashDbCreateDatabaseDialog.noChangeRadioButton.text=No Change
|
||||
HashDbImportDatabaseDialog.noChangeRadioButton.toolTipText=
|
||||
HashDbImportDatabaseDialog.noChangeRadioButton.text=No Change
|
||||
|
@ -216,8 +216,6 @@ HashLookupSettingsPanel.jLabel6.text=\u30bf\u30a4\u30d7:
|
||||
HashLookupSettingsPanel.jLabel4.text=\u5834\u6240:
|
||||
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.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.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
|
||||
@ -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.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=
|
||||
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
|
||||
|
@ -78,6 +78,7 @@
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="knownRadioButton" 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 type="102" attributes="0">
|
||||
@ -125,19 +126,21 @@
|
||||
<Component id="knownRadioButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace 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="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"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</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>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
@ -313,5 +316,18 @@
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="orgButtonActionPerformed"/>
|
||||
</Events>
|
||||
</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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="noChangeRadioButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -198,6 +198,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
|
||||
lbOrg = new javax.swing.JLabel();
|
||||
orgComboBox = new javax.swing.JComboBox<>();
|
||||
orgButton = new javax.swing.JButton();
|
||||
noChangeRadioButton = new javax.swing.JRadioButton();
|
||||
|
||||
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());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
@ -334,7 +343,8 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
|
||||
.addGap(32, 32, 32)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(knownRadioButton)
|
||||
.addComponent(knownBadRadioButton)))
|
||||
.addComponent(knownBadRadioButton)
|
||||
.addComponent(noChangeRadioButton)))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(12, 12, 12)
|
||||
.addComponent(jLabel2))
|
||||
@ -374,16 +384,18 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
|
||||
.addComponent(knownRadioButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(knownBadRadioButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.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)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(cancelButton)
|
||||
.addComponent(okButton))))
|
||||
.addComponent(okButton)))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(noChangeRadioButton)
|
||||
.addGap(24, 24, 24)
|
||||
.addComponent(sendIngestMessagesCheckbox)
|
||||
.addGap(0, 0, Short.MAX_VALUE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
@ -391,13 +403,13 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void knownRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownRadioButtonActionPerformed
|
||||
sendIngestMessagesCheckbox.setSelected(false);
|
||||
sendIngestMessagesCheckbox.setEnabled(false);
|
||||
sendIngestMessagesCheckbox.setSelected(KnownFilesType.KNOWN.isDefaultInboxMessages());
|
||||
sendIngestMessagesCheckbox.setEnabled(KnownFilesType.KNOWN.isInboxMessagesAllowed());
|
||||
}//GEN-LAST:event_knownRadioButtonActionPerformed
|
||||
|
||||
private void knownBadRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownBadRadioButtonActionPerformed
|
||||
sendIngestMessagesCheckbox.setSelected(true);
|
||||
sendIngestMessagesCheckbox.setEnabled(true);
|
||||
sendIngestMessagesCheckbox.setSelected(KnownFilesType.KNOWN_BAD.isDefaultInboxMessages());
|
||||
sendIngestMessagesCheckbox.setEnabled(KnownFilesType.KNOWN_BAD.isInboxMessagesAllowed());
|
||||
}//GEN-LAST:event_knownBadRadioButtonActionPerformed
|
||||
|
||||
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;
|
||||
TskData.FileKnown fileKnown;
|
||||
|
||||
if (knownRadioButton.isSelected()) {
|
||||
type = KnownFilesType.KNOWN;
|
||||
fileKnown = TskData.FileKnown.KNOWN;
|
||||
} else if (noChangeRadioButton.isSelected()) {
|
||||
type = KnownFilesType.NO_CHANGE;
|
||||
} else {
|
||||
type = KnownFilesType.KNOWN_BAD;
|
||||
fileKnown = TskData.FileKnown.BAD;
|
||||
}
|
||||
|
||||
TskData.FileKnown fileKnown = type.getFileKnown();
|
||||
|
||||
String errorMessage = NbBundle
|
||||
.getMessage(this.getClass(), "HashDbCreateDatabaseDialog.errMsg.hashDbCreationErr");
|
||||
|
||||
@ -586,6 +600,11 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
|
||||
enableComponents();
|
||||
}//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
|
||||
private javax.swing.ButtonGroup buttonGroup1;
|
||||
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 knownRadioButton;
|
||||
private javax.swing.JLabel lbOrg;
|
||||
private javax.swing.JRadioButton noChangeRadioButton;
|
||||
private javax.swing.JButton okButton;
|
||||
private javax.swing.JButton orgButton;
|
||||
private javax.swing.JComboBox<String> orgComboBox;
|
||||
|
@ -29,7 +29,7 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="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"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
@ -54,10 +54,6 @@
|
||||
</Group>
|
||||
<Group type="102" 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">
|
||||
<Component id="lbOrg" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
@ -76,7 +72,16 @@
|
||||
<Component id="hashSetNameTextField" max="32767" attributes="0"/>
|
||||
</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"/>
|
||||
<Component id="okButton" linkSize="1" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
@ -86,16 +91,17 @@
|
||||
</Group>
|
||||
<Group type="102" 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="readOnlyCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" pref="19" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="knownRadioButton" 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>
|
||||
<Component id="saveInUserConfigFolderCheckbox" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="readOnlyCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
@ -113,52 +119,54 @@
|
||||
<Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="openButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</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="102" 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"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="readOnlyCheckbox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="sendIngestMessagesCheckbox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace 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 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">
|
||||
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -367,5 +375,21 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="noChangeRadioButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -182,6 +182,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
|
||||
centralRepoRadioButton = new javax.swing.JRadioButton();
|
||||
jLabel4 = new javax.swing.JLabel();
|
||||
saveInUserConfigFolderCheckbox = new javax.swing.JCheckBox();
|
||||
noChangeRadioButton = new javax.swing.JRadioButton();
|
||||
|
||||
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
|
||||
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());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
@ -315,9 +325,6 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
|
||||
.addComponent(openButton))))
|
||||
.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(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(lbOrg)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
@ -331,7 +338,13 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
|
||||
.addGap(40, 40, 40)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.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()
|
||||
.addGap(0, 0, Short.MAX_VALUE)
|
||||
.addComponent(okButton)))
|
||||
@ -339,14 +352,15 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
|
||||
.addComponent(cancelButton))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(saveInUserConfigFolderCheckbox)
|
||||
.addComponent(jLabel2)
|
||||
.addComponent(readOnlyCheckbox)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(19, 19, 19)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(knownRadioButton)
|
||||
.addComponent(knownBadRadioButton))))
|
||||
.addComponent(knownBadRadioButton)
|
||||
.addComponent(noChangeRadioButton)))
|
||||
.addComponent(saveInUserConfigFolderCheckbox)
|
||||
.addComponent(readOnlyCheckbox))
|
||||
.addGap(0, 0, Short.MAX_VALUE)))
|
||||
.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(jLabel3)
|
||||
.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.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)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(sendIngestMessagesCheckbox)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(saveInUserConfigFolderCheckbox)
|
||||
.addGap(0, 29, Short.MAX_VALUE))
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.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)
|
||||
.addComponent(cancelButton)
|
||||
.addComponent(okButton))))
|
||||
.addContainerGap())
|
||||
.addGap(18, 18, 18))
|
||||
);
|
||||
|
||||
pack();
|
||||
@ -436,13 +452,13 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
|
||||
}//GEN-LAST:event_openButtonActionPerformed
|
||||
|
||||
private void knownRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownRadioButtonActionPerformed
|
||||
sendIngestMessagesCheckbox.setSelected(false);
|
||||
sendIngestMessagesCheckbox.setEnabled(false);
|
||||
sendIngestMessagesCheckbox.setSelected(KnownFilesType.KNOWN.isDefaultInboxMessages());
|
||||
sendIngestMessagesCheckbox.setEnabled(KnownFilesType.KNOWN.isInboxMessagesAllowed());
|
||||
}//GEN-LAST:event_knownRadioButtonActionPerformed
|
||||
|
||||
private void knownBadRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownBadRadioButtonActionPerformed
|
||||
sendIngestMessagesCheckbox.setSelected(true);
|
||||
sendIngestMessagesCheckbox.setEnabled(true);
|
||||
sendIngestMessagesCheckbox.setSelected(KnownFilesType.KNOWN_BAD.isDefaultInboxMessages());
|
||||
sendIngestMessagesCheckbox.setEnabled(KnownFilesType.KNOWN_BAD.isInboxMessagesAllowed());
|
||||
}//GEN-LAST:event_knownBadRadioButtonActionPerformed
|
||||
|
||||
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;
|
||||
if (knownRadioButton.isSelected()) {
|
||||
type = KnownFilesType.KNOWN;
|
||||
} else if (noChangeRadioButton.isSelected()) {
|
||||
type = KnownFilesType.NO_CHANGE;
|
||||
} else {
|
||||
type = KnownFilesType.KNOWN_BAD;
|
||||
}
|
||||
@ -629,6 +647,11 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
|
||||
enableComponents();
|
||||
}//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
|
||||
private javax.swing.ButtonGroup buttonGroup1;
|
||||
private javax.swing.JButton cancelButton;
|
||||
@ -644,6 +667,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
|
||||
private javax.swing.JRadioButton knownRadioButton;
|
||||
private javax.swing.JLabel lbOrg;
|
||||
private javax.swing.JLabel lbVersion;
|
||||
private javax.swing.JRadioButton noChangeRadioButton;
|
||||
private javax.swing.JButton okButton;
|
||||
private javax.swing.JButton openButton;
|
||||
private javax.swing.JButton orgButton;
|
||||
|
@ -24,7 +24,9 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
@ -57,12 +59,26 @@ import org.sleuthkit.datamodel.TskException;
|
||||
@Messages({
|
||||
"HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.",
|
||||
"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.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 {
|
||||
|
||||
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 final IngestServices services = IngestServices.getInstance();
|
||||
private final SleuthkitCase skCase;
|
||||
@ -70,6 +86,7 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
private final HashLookupModuleSettings settings;
|
||||
private final List<HashDb> knownBadHashSets = new ArrayList<>();
|
||||
private final List<HashDb> knownHashSets = new ArrayList<>();
|
||||
private final List<HashDb> noChangeHashSets = new ArrayList<>();
|
||||
private long jobId;
|
||||
private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>();
|
||||
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
|
||||
@ -81,6 +98,7 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
private static class IngestJobTotals {
|
||||
|
||||
private final AtomicLong totalKnownBadCount = new AtomicLong(0);
|
||||
private final AtomicLong totalNoChangeCount = new AtomicLong(0);
|
||||
private final AtomicLong totalCalctime = new AtomicLong(0);
|
||||
private final AtomicLong totalLookuptime = new AtomicLong(0);
|
||||
}
|
||||
@ -114,8 +132,8 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
if (!hashDbManager.verifyAllDatabasesLoadedCorrectly()) {
|
||||
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) {
|
||||
// initialize job totals
|
||||
@ -129,6 +147,13 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
Bundle.HashDbIngestModule_knownBadFileSearchWillNotExecuteWarn()));
|
||||
}
|
||||
|
||||
if (noChangeHashSets.isEmpty()) {
|
||||
services.postMessage(IngestMessage.createWarningMessage(
|
||||
HashLookupModuleFactory.getModuleName(),
|
||||
Bundle.HashDbIngestModule_noChangeHashDbSetMsg(),
|
||||
Bundle.HashDbIngestModule_noChangeFileSearchWillNotExecuteWarn()));
|
||||
}
|
||||
|
||||
if (knownHashSets.isEmpty()) {
|
||||
services.postMessage(IngestMessage.createWarningMessage(
|
||||
HashLookupModuleFactory.getModuleName(),
|
||||
@ -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 enabledHashSets List of enabled ones to return.
|
||||
* @param allHashSets List of all hashsets from DB manager
|
||||
*/
|
||||
private void updateEnabledHashSets(List<HashDb> allHashSets, List<HashDb> enabledHashSets) {
|
||||
enabledHashSets.clear();
|
||||
private void initializeHashsets(List<HashDb> allHashSets) {
|
||||
for (HashDb db : allHashSets) {
|
||||
if (settings.isHashSetEnabled(db)) {
|
||||
try {
|
||||
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) {
|
||||
logger.log(Level.WARNING, "Error getting index status for " + db.getDisplayName() + " hash set", ex); //NON-NLS
|
||||
@ -174,24 +210,7 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
|
||||
// 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 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())) {
|
||||
if (shouldSkip(file)) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
@ -199,103 +218,29 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
IngestJobTotals totals = getTotalsForIngestJobs(jobId);
|
||||
|
||||
// calc hash value
|
||||
String name = file.getName();
|
||||
long fileId = file.getId();
|
||||
String md5Hash = file.getMd5Hash();
|
||||
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;
|
||||
}
|
||||
String md5Hash = getHash(file, totals);
|
||||
if (md5Hash == null) {
|
||||
return ProcessResult.ERROR;
|
||||
}
|
||||
|
||||
// look up in notable first
|
||||
boolean foundBad = false;
|
||||
// the processing result of handling this file
|
||||
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 = "";
|
||||
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;
|
||||
}
|
||||
}
|
||||
// look up no change items next
|
||||
FindInHashsetsResult noChangeResult = findInHashsets(file, totals.totalNoChangeCount,
|
||||
totals.totalLookuptime, noChangeHashSets, TskData.FileKnown.UNKNOWN, noChangeLookupError);
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
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 (noChangeResult.isError()) {
|
||||
ret = ProcessResult.ERROR;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
} catch (TskException ex) {
|
||||
logger.log(Level.WARNING, String.format(
|
||||
"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)));
|
||||
reportLookupError(ex, file, knownLookupError);
|
||||
ret = ProcessResult.ERROR;
|
||||
}
|
||||
}
|
||||
@ -327,6 +267,245 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
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.
|
||||
*
|
||||
@ -413,35 +592,35 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
* @param knownBadHashSets The list of hash sets for "known bad" files.
|
||||
* @param knownHashSets The list of hash sets for "known" files.
|
||||
*/
|
||||
private static synchronized void postSummary(long jobId,
|
||||
List<HashDb> knownBadHashSets, List<HashDb> knownHashSets) {
|
||||
@Messages("HashDbIngestModule.complete.noChangesFound=No Change items found:")
|
||||
private static synchronized void postSummary(long jobId, List<HashDb> knownBadHashSets,
|
||||
List<HashDb> noChangeHashSets, List<HashDb> knownHashSets) {
|
||||
|
||||
IngestJobTotals jobTotals = getTotalsForIngestJobs(jobId);
|
||||
totalsForIngestJobs.remove(jobId);
|
||||
|
||||
if ((!knownBadHashSets.isEmpty()) || (!knownHashSets.isEmpty())) {
|
||||
if ((!knownBadHashSets.isEmpty()) || (!knownHashSets.isEmpty()) || (!noChangeHashSets.isEmpty())) {
|
||||
StringBuilder detailsSb = new StringBuilder();
|
||||
//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>" +
|
||||
|
||||
detailsSb.append("<tr><td>") //NON-NLS
|
||||
.append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.knownBadsFound"))
|
||||
.append("</td>"); //NON-NLS
|
||||
detailsSb.append("<td>").append(jobTotals.totalKnownBadCount.get()).append("</td></tr>"); //NON-NLS
|
||||
"<tr><td>" + Bundle.HashDbIngestModule_complete_noChangesFound() + "</td>" +
|
||||
"<td>" + jobTotals.totalNoChangeCount.get() + "</td></tr>" +
|
||||
|
||||
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
|
||||
"<tr><td>" + NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.totalCalcTime") +
|
||||
"</td><td>" + jobTotals.totalCalctime.get() + "</td></tr>\n" +
|
||||
|
||||
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
|
||||
}
|
||||
"<tr><td>" + NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.totalLookupTime") +
|
||||
"</td><td>" + jobTotals.totalLookuptime.get() + "</td></tr>\n</table>" +
|
||||
|
||||
"<p>" + NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.databasesUsed") + "</p>\n<ul>"); //NON-NLS
|
||||
|
||||
Stream.concat(knownBadHashSets.stream(), noChangeHashSets.stream()).forEach((db) -> {
|
||||
detailsSb.append("<li>" + db.getHashSetName() + "</li>\n"); //NON-NLS
|
||||
});
|
||||
|
||||
detailsSb.append("</ul>"); //NON-NLS
|
||||
|
||||
@ -456,7 +635,7 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
@Override
|
||||
public void shutDown() {
|
||||
if (refCounter.decrementAndGet(jobId) == 0) {
|
||||
postSummary(jobId, knownBadHashSets, knownHashSets);
|
||||
postSummary(jobId, knownBadHashSets, noChangeHashSets, knownHashSets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -31,6 +30,7 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingWorker;
|
||||
@ -58,6 +58,7 @@ import org.sleuthkit.datamodel.SleuthkitJNI;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
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
|
||||
@ -104,7 +105,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
changeSupport.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
synchronized boolean verifyAllDatabasesLoadedCorrectly(){
|
||||
synchronized boolean verifyAllDatabasesLoadedCorrectly() {
|
||||
return allDatabasesLoadedCorrectly;
|
||||
}
|
||||
|
||||
@ -276,16 +277,16 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
|
||||
CentralRepoHashSet addExistingCentralRepoHashSet(String hashSetName, String version, int referenceSetID,
|
||||
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");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
@ -357,12 +358,11 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
hashSets.remove(hashDb);
|
||||
|
||||
// Now undertake the operations that could throw.
|
||||
|
||||
// Indexing is only relevanet for sleuthkit hashsets
|
||||
if(hashDb instanceof SleuthkitHashSet){
|
||||
SleuthkitHashSet hashDatabase = (SleuthkitHashSet)hashDb;
|
||||
if (hashDb instanceof SleuthkitHashSet) {
|
||||
SleuthkitHashSet hashDatabase = (SleuthkitHashSet) hashDb;
|
||||
try {
|
||||
if(hashDatabase.hasIndex()){
|
||||
if (hashDatabase.hasIndex()) {
|
||||
hashSetPaths.remove(hashDatabase.getIndexPath());
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
@ -414,9 +414,9 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
* @return A list, possibly empty, of hash databases.
|
||||
*/
|
||||
public synchronized List<HashDb> getAllHashSets() {
|
||||
try{
|
||||
try {
|
||||
updateHashSetsFromCentralRepository();
|
||||
} catch (TskCoreException ex){
|
||||
} catch (TskCoreException ex) {
|
||||
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
|
||||
}
|
||||
|
||||
@ -432,9 +432,9 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
*/
|
||||
public synchronized List<HashDb> getKnownFileHashSets() {
|
||||
List<HashDb> hashDbs = new ArrayList<>();
|
||||
try{
|
||||
try {
|
||||
updateHashSetsFromCentralRepository();
|
||||
} catch (TskCoreException ex){
|
||||
} catch (TskCoreException ex) {
|
||||
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) -> {
|
||||
@ -450,9 +450,9 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
*/
|
||||
public synchronized List<HashDb> getKnownBadFileHashSets() {
|
||||
List<HashDb> hashDbs = new ArrayList<>();
|
||||
try{
|
||||
try {
|
||||
updateHashSetsFromCentralRepository();
|
||||
} catch (TskCoreException ex){
|
||||
} catch (TskCoreException ex) {
|
||||
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) -> {
|
||||
@ -472,9 +472,9 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
|
||||
private List<HashDb> getUpdateableHashSets(List<HashDb> hashDbs) {
|
||||
ArrayList<HashDb> updateableDbs = new ArrayList<>();
|
||||
try{
|
||||
try {
|
||||
updateHashSetsFromCentralRepository();
|
||||
} catch (TskCoreException ex){
|
||||
} catch (TskCoreException ex) {
|
||||
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
|
||||
}
|
||||
for (HashDb db : hashDbs) {
|
||||
@ -489,34 +489,27 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
return updateableDbs;
|
||||
}
|
||||
|
||||
private List<HashDbInfo> getCentralRepoHashSetsFromDatabase(){
|
||||
private List<HashDbInfo> getCentralRepoHashSetsFromDatabase() {
|
||||
List<HashDbInfo> crHashSets = new ArrayList<>();
|
||||
if(CentralRepository.isEnabled()){
|
||||
try{
|
||||
if (CentralRepository.isEnabled()) {
|
||||
try {
|
||||
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:
|
||||
// searchDuringIngest: false
|
||||
// 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(),
|
||||
globalSet.getGlobalSetID(), convertFileKnown(globalSet.getFileKnownStatus()), globalSet.isReadOnly(), false, sendIngestMessages));
|
||||
}
|
||||
} catch (CentralRepoException ex){
|
||||
globalSet.getGlobalSetID(), KnownFilesType.fromFileKnown(globalSet.getFileKnownStatus()), globalSet.isReadOnly(), false, sendIngestMessages));
|
||||
}
|
||||
} catch (CentralRepoException ex) {
|
||||
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
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
|
||||
* cancellation of configuration panels.
|
||||
@ -531,9 +524,9 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
|
||||
private void closeHashDatabases(List<HashDb> hashDatabases) {
|
||||
for (HashDb database : hashDatabases) {
|
||||
if(database instanceof SleuthkitHashSet){
|
||||
if (database instanceof SleuthkitHashSet) {
|
||||
try {
|
||||
((SleuthkitHashSet)database).close();
|
||||
((SleuthkitHashSet) database).close();
|
||||
} catch (TskCoreException ex) {
|
||||
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.
|
||||
*/
|
||||
@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) {
|
||||
allDatabasesLoadedCorrectly = true;
|
||||
List<HashDbInfo> hashDbInfoList = settings.getHashDbInfo();
|
||||
for (HashDbInfo hashDbInfo : hashDbInfoList) {
|
||||
try {
|
||||
if(hashDbInfo.isFileDatabaseType()){
|
||||
if (hashDbInfo.isFileDatabaseType()) {
|
||||
String dbPath = this.getValidFilePath(hashDbInfo.getHashSetName(), hashDbInfo.getPath());
|
||||
if (dbPath != null) {
|
||||
addHashDatabase(SleuthkitJNI.openHashDatabase(dbPath), hashDbInfo.getHashSetName(), hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(), hashDbInfo.getKnownFilesType());
|
||||
@ -573,7 +566,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
allDatabasesLoadedCorrectly = false;
|
||||
}
|
||||
} else {
|
||||
if(CentralRepository.isEnabled()){
|
||||
if (CentralRepository.isEnabled()) {
|
||||
addExistingCentralRepoHashSet(hashDbInfo.getHashSetName(), hashDbInfo.getVersion(),
|
||||
hashDbInfo.getReferenceSetID(),
|
||||
hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(),
|
||||
@ -591,10 +584,10 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
}
|
||||
}
|
||||
|
||||
if(CentralRepository.isEnabled()){
|
||||
try{
|
||||
if (CentralRepository.isEnabled()) {
|
||||
try {
|
||||
updateHashSetsFromCentralRepository();
|
||||
} catch (TskCoreException ex){
|
||||
} catch (TskCoreException ex) {
|
||||
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash set", ex); //NON-NLS
|
||||
|
||||
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
|
||||
@ -605,13 +598,16 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: When RuntimeProperties.coreComponentsAreActive() is "false",
|
||||
I don't think we should overwrite hash db settings file because we
|
||||
were unable to load a database. The user should have to fix the issue or
|
||||
remove the database from settings. Overwiting the settings effectively removes
|
||||
the database from HashLookupSettings and the user 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.*/
|
||||
/*
|
||||
* NOTE: When RuntimeProperties.coreComponentsAreActive() is "false", I
|
||||
* don't think we should overwrite hash db settings file because we were
|
||||
* unable to load a database. The user should have to fix the issue or
|
||||
* remove the database from settings. Overwiting the settings
|
||||
* effectively removes the database from HashLookupSettings and the user
|
||||
* 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()) {
|
||||
try {
|
||||
HashLookupSettings.writeSettings(new HashLookupSettings(HashLookupSettings.convertHashSetList(this.hashSets)));
|
||||
@ -624,22 +620,22 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
}
|
||||
|
||||
private void updateHashSetsFromCentralRepository() throws TskCoreException {
|
||||
if(CentralRepository.isEnabled()){
|
||||
if (CentralRepository.isEnabled()) {
|
||||
List<HashDbInfo> crHashDbInfoList = getCentralRepoHashSetsFromDatabase();
|
||||
for(HashDbInfo hashDbInfo : crHashDbInfoList) {
|
||||
if(hashDbInfoIsNew(hashDbInfo)){
|
||||
for (HashDbInfo hashDbInfo : crHashDbInfoList) {
|
||||
if (hashDbInfoIsNew(hashDbInfo)) {
|
||||
addExistingCentralRepoHashSet(hashDbInfo.getHashSetName(), hashDbInfo.getVersion(),
|
||||
hashDbInfo.getReferenceSetID(),
|
||||
hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(), hashDbInfo.getKnownFilesType(),
|
||||
hashDbInfo.isReadOnly());
|
||||
hashDbInfo.getReferenceSetID(),
|
||||
hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(), hashDbInfo.getKnownFilesType(),
|
||||
hashDbInfo.isReadOnly());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hashDbInfoIsNew(HashDbInfo dbInfo){
|
||||
for(HashDb db:this.hashSets){
|
||||
if(dbInfo.matches(db)){
|
||||
private boolean hashDbInfoIsNew(HashDbInfo dbInfo) {
|
||||
for (HashDb db : this.hashSets) {
|
||||
if (dbInfo.matches(db)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -655,12 +651,12 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
|
||||
// Give the user an opportunity to find the desired file.
|
||||
String newPath = null;
|
||||
if (RuntimeProperties.runningWithGUI() &&
|
||||
JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
|
||||
NbBundle.getMessage(this.getClass(), "HashDbManager.dlgMsg.dbNotFoundAtLoc",
|
||||
hashSetName, configuredPath),
|
||||
NbBundle.getMessage(this.getClass(), "HashDbManager.dlgTitle.MissingDb"),
|
||||
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
|
||||
if (RuntimeProperties.runningWithGUI()
|
||||
&& JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
|
||||
NbBundle.getMessage(this.getClass(), "HashDbManager.dlgMsg.dbNotFoundAtLoc",
|
||||
hashSetName, configuredPath),
|
||||
NbBundle.getMessage(this.getClass(), "HashDbManager.dlgTitle.MissingDb"),
|
||||
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
|
||||
newPath = searchForFile();
|
||||
if (null != newPath && !newPath.isEmpty()) {
|
||||
database = new File(newPath);
|
||||
@ -699,19 +695,82 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
* Indicates how files with hashes stored in a particular hash database
|
||||
* object should be classified.
|
||||
*/
|
||||
@Messages({
|
||||
"HashDbManager.noChange.text=No Change",
|
||||
"HashDbManager.known.text=Known",
|
||||
"HashDbManager.knownBad.text=Notable"
|
||||
})
|
||||
public enum KnownFilesType {
|
||||
|
||||
KNOWN(NbBundle.getMessage(HashDbManager.class, "HashDbManager.known.text")),
|
||||
KNOWN_BAD(NbBundle.getMessage(HashDbManager.class, "HashDbManager.knownBad.text"));
|
||||
private final String displayName;
|
||||
KNOWN(Bundle.HashDbManager_known_text(), TskData.FileKnown.KNOWN, false, false),
|
||||
KNOWN_BAD(Bundle.HashDbManager_knownBad_text(), TskData.FileKnown.BAD, true, true),
|
||||
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.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() {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -766,9 +825,11 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
public abstract HashHitInfo lookupMD5(Content content) throws TskCoreException;
|
||||
|
||||
/**
|
||||
* Returns whether this database can be enabled.
|
||||
* For file type, this is the same as checking that it has an index
|
||||
* Returns whether this database can be enabled. For file type, this is
|
||||
* the same as checking that it has an index
|
||||
*
|
||||
* @return true if is valid, false otherwise
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
abstract boolean isValid() throws TskCoreException;
|
||||
@ -813,8 +874,8 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for the events defined in HashDb.Event.
|
||||
* Listeners are used during indexing.
|
||||
* Adds a listener for the events defined in HashDb.Event. Listeners are
|
||||
* used during indexing.
|
||||
*
|
||||
* @param pcl
|
||||
*/
|
||||
@ -833,7 +894,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
propertyChangeSupport.removePropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
int getHandle(){
|
||||
int getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
@ -843,7 +904,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
String getDisplayName(){
|
||||
String getDisplayName() {
|
||||
return getHashSetName();
|
||||
}
|
||||
|
||||
@ -852,7 +913,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
return SleuthkitJNI.getHashDatabasePath(handle);
|
||||
}
|
||||
|
||||
public void setIndexing(boolean indexing){
|
||||
public void setIndexing(boolean indexing) {
|
||||
this.indexing = indexing;
|
||||
}
|
||||
|
||||
@ -991,9 +1052,11 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this database can be enabled.
|
||||
* For file type, this is the same as checking that it has an index
|
||||
* Returns whether this database can be enabled. For file type, this is
|
||||
* the same as checking that it has an index
|
||||
*
|
||||
* @return true if is valid, false otherwise
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
@Override
|
||||
@ -1019,7 +1082,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@ -1028,11 +1091,10 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
public String toString() {
|
||||
return getHashSetName();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int code = 23;
|
||||
@ -1066,7 +1128,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
* Instances of this class represent hash databases used to classify files
|
||||
* as known or know bad.
|
||||
*/
|
||||
class CentralRepoHashSet extends HashDb{
|
||||
class CentralRepoHashSet extends HashDb {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final String hashSetName;
|
||||
@ -1083,7 +1145,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
private CentralRepoHashSet(String hashSetName, String version, int referenceSetID,
|
||||
boolean useForIngest, boolean sendHitMessages, HashDb.KnownFilesType knownFilesType,
|
||||
boolean readOnly)
|
||||
throws TskCoreException{
|
||||
throws TskCoreException {
|
||||
this.hashSetName = hashSetName;
|
||||
this.version = version;
|
||||
this.referenceSetID = referenceSetID;
|
||||
@ -1092,17 +1154,17 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
this.knownFilesType = knownFilesType;
|
||||
this.readOnly = readOnly;
|
||||
|
||||
try{
|
||||
try {
|
||||
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
|
||||
orgName = Bundle.HashDbManager_CentralRepoHashDb_orgError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for the events defined in HashDb.Event.
|
||||
* Listeners are used during indexing.
|
||||
* Adds a listener for the events defined in HashDb.Event. Listeners are
|
||||
* used during indexing.
|
||||
*
|
||||
* @param pcl
|
||||
*/
|
||||
@ -1122,7 +1184,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIndexOnly() throws TskCoreException{
|
||||
public boolean hasIndexOnly() throws TskCoreException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1132,23 +1194,23 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName(){
|
||||
if(! getVersion().isEmpty()){
|
||||
public String getDisplayName() {
|
||||
if (!getVersion().isEmpty()) {
|
||||
return getHashSetName() + " " + getVersion() + " (remote)";
|
||||
} else {
|
||||
return getHashSetName() + " (remote)";
|
||||
}
|
||||
}
|
||||
|
||||
String getVersion(){
|
||||
String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
String getOrgName(){
|
||||
String getOrgName() {
|
||||
return orgName;
|
||||
}
|
||||
|
||||
int getReferenceSetID(){
|
||||
int getReferenceSetID() {
|
||||
return referenceSetID;
|
||||
}
|
||||
|
||||
@ -1196,7 +1258,7 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
*/
|
||||
@Override
|
||||
public boolean isUpdateable() throws TskCoreException {
|
||||
return (! readOnly);
|
||||
return (!readOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1229,18 +1291,13 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
if (content instanceof AbstractFile) {
|
||||
AbstractFile file = (AbstractFile) content;
|
||||
if (null != file.getMd5Hash()) {
|
||||
TskData.FileKnown type;
|
||||
if(knownFilesType.equals(HashDb.KnownFilesType.KNOWN_BAD)){
|
||||
type = TskData.FileKnown.BAD;
|
||||
} else {
|
||||
type = TskData.FileKnown.KNOWN;
|
||||
}
|
||||
TskData.FileKnown type = knownFilesType.getFileKnown();
|
||||
|
||||
try{
|
||||
try {
|
||||
CentralRepoFileInstance fileInstance = new CentralRepoFileInstance(referenceSetID, file.getMd5Hash(),
|
||||
type, comment);
|
||||
CentralRepository.getInstance().addReferenceInstance(fileInstance,CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID));
|
||||
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex){
|
||||
type, comment);
|
||||
CentralRepository.getInstance().addReferenceInstance(fileInstance, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID));
|
||||
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
|
||||
throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
@ -1257,24 +1314,20 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
@Override
|
||||
public void addHashes(List<HashEntry> hashes) throws TskCoreException {
|
||||
Set<CentralRepoFileInstance> globalFileInstances = new HashSet<>();
|
||||
for(HashEntry hashEntry:hashes){
|
||||
TskData.FileKnown type;
|
||||
if(knownFilesType.equals(HashDb.KnownFilesType.KNOWN_BAD)){
|
||||
type = TskData.FileKnown.BAD;
|
||||
} else {
|
||||
type = TskData.FileKnown.KNOWN;
|
||||
}
|
||||
for (HashEntry hashEntry : hashes) {
|
||||
TskData.FileKnown type = knownFilesType.getFileKnown();
|
||||
|
||||
try {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
try {
|
||||
CentralRepository.getInstance().bulkInsertReferenceTypeEntries(globalFileInstances,
|
||||
CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID));
|
||||
} catch (CentralRepoException ex){
|
||||
} catch (CentralRepoException ex) {
|
||||
throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex);
|
||||
}
|
||||
}
|
||||
@ -1295,9 +1348,9 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
if (content instanceof AbstractFile) {
|
||||
AbstractFile file = (AbstractFile) content;
|
||||
if (null != file.getMd5Hash()) {
|
||||
try{
|
||||
try {
|
||||
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 "
|
||||
+ file.getMd5Hash() + " in reference set " + referenceSetID, ex); //NON-NLS
|
||||
throw new TskCoreException("Error performing central reposiotry hash lookup", ex);
|
||||
@ -1324,12 +1377,9 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
if (content instanceof AbstractFile) {
|
||||
AbstractFile file = (AbstractFile) content;
|
||||
if (null != file.getMd5Hash()) {
|
||||
try{
|
||||
if(CentralRepository.getInstance().isFileHashInReferenceSet(file.getMd5Hash(), this.referenceSetID)){
|
||||
// Make a bare-bones HashHitInfo for now
|
||||
result = new HashHitInfo(file.getMd5Hash(), "", "");
|
||||
}
|
||||
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex){
|
||||
try {
|
||||
return CentralRepository.getInstance().lookupHash(file.getMd5Hash(), referenceSetID);
|
||||
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
|
||||
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
|
||||
throw new TskCoreException("Error performing central reposiotry hash lookup", ex);
|
||||
@ -1346,28 +1396,27 @@ public class HashDbManager implements PropertyChangeListener {
|
||||
*/
|
||||
@Override
|
||||
boolean isValid() {
|
||||
if(! CentralRepository.isEnabled()) {
|
||||
if (!CentralRepository.isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
try{
|
||||
try {
|
||||
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
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
public String toString() {
|
||||
return getDisplayName();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int code = 23;
|
||||
|
@ -25,16 +25,12 @@
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" 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"/>
|
||||
</Group>
|
||||
<Component id="knownBadHashDbsLabel" pref="290" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jScrollPane1" pref="0" max="32767" attributes="1"/>
|
||||
<Component id="jScrollPane2" pref="0" max="32767" attributes="1"/>
|
||||
</Group>
|
||||
<Component id="hashDbsScrollPane" pref="494" max="32767" attributes="1"/>
|
||||
</Group>
|
||||
<Component id="alwaysCalcHashesCheckbox" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
@ -46,22 +42,25 @@
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" 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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="hashDbsScrollPane">
|
||||
<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">
|
||||
@ -75,7 +74,7 @@
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="knownHashTable">
|
||||
<Component class="javax.swing.JTable" name="hashTable">
|
||||
<Properties>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="f0" green="f0" red="f0" type="rgb"/>
|
||||
@ -86,20 +85,6 @@
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="alwaysCalcHashesCheckbox">
|
||||
<Properties>
|
||||
<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"/>
|
||||
</Properties>
|
||||
</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>
|
||||
</Form>
|
||||
|
@ -27,13 +27,13 @@ import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.TableColumn;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
|
||||
|
||||
|
||||
/**
|
||||
* 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 final HashDbManager hashDbManager = HashDbManager.getInstance();
|
||||
private final List<HashSetModel> knownHashSetModels = new ArrayList<>();
|
||||
private final HashSetsTableModel knownHashSetsTableModel = new HashSetsTableModel(knownHashSetModels);
|
||||
private final List<HashSetModel> knownBadHashSetModels = new ArrayList<>();
|
||||
private final HashSetsTableModel knownBadHashSetsTableModel = new HashSetsTableModel(knownBadHashSetModels);
|
||||
private final List<HashSetModel> hashSetModels = new ArrayList<>();
|
||||
private final HashSetsTableModel hashSetsTableModel = new HashSetsTableModel(hashSetModels);
|
||||
|
||||
HashLookupModuleSettingsPanel(HashLookupModuleSettings settings) {
|
||||
initializeHashSetModels(settings);
|
||||
@ -54,11 +52,7 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
|
||||
}
|
||||
|
||||
private void initializeHashSetModels(HashLookupModuleSettings settings) {
|
||||
initializeHashSetModels(settings, validSetsOnly(hashDbManager.getKnownFileHashSets()), knownHashSetModels);
|
||||
initializeHashSetModels(settings, validSetsOnly(hashDbManager.getKnownBadFileHashSets()), knownBadHashSetModels);
|
||||
}
|
||||
|
||||
private void initializeHashSetModels(HashLookupModuleSettings settings, List<HashDb> hashDbs, List<HashSetModel> hashSetModels) {
|
||||
List<HashDb> hashDbs = validSetsOnly(hashDbManager.getAllHashSets());
|
||||
hashSetModels.clear();
|
||||
for (HashDb db : hashDbs) {
|
||||
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) {
|
||||
customizeHashSetsTable(jScrollPane1, knownHashTable, knownHashSetsTableModel);
|
||||
customizeHashSetsTable(jScrollPane2, knownBadHashTable, knownBadHashSetsTableModel);
|
||||
customizeHashSetsTable(hashDbsScrollPane, hashTable, hashSetsTableModel);
|
||||
alwaysCalcHashesCheckbox.setSelected(settings.shouldCalculateHashes());
|
||||
hashDbManager.addPropertyChangeListener(this);
|
||||
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.setRowSelectionAllowed(false);
|
||||
final int width1 = scrollPane.getPreferredSize().width;
|
||||
knownHashTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
|
||||
hashTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
|
||||
TableColumn column;
|
||||
for (int i = 0; i < table.getColumnCount(); i++) {
|
||||
column = table.getColumnModel().getColumn(i);
|
||||
@ -103,8 +96,7 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
|
||||
public IngestModuleIngestJobSettings getSettings() {
|
||||
List<HashDb> enabledHashSets = new ArrayList<>();
|
||||
List<HashDb> disabledHashSets = new ArrayList<>();
|
||||
addHashSets(knownHashSetModels, enabledHashSets, disabledHashSets);
|
||||
addHashSets(knownBadHashSetModels, enabledHashSets, disabledHashSets);
|
||||
addHashSets(hashSetModels, enabledHashSets, disabledHashSets);
|
||||
return new HashLookupModuleSettings(alwaysCalcHashesCheckbox.isSelected(),
|
||||
enabledHashSets, disabledHashSets);
|
||||
}
|
||||
@ -121,30 +113,25 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
|
||||
|
||||
void update() {
|
||||
updateHashSetModels();
|
||||
knownHashSetsTableModel.fireTableDataChanged();
|
||||
knownBadHashSetsTableModel.fireTableDataChanged();
|
||||
hashSetsTableModel.fireTableDataChanged();
|
||||
}
|
||||
|
||||
private void updateHashSetModels() {
|
||||
updateHashSetModels(validSetsOnly(hashDbManager.getKnownFileHashSets()), knownHashSetModels);
|
||||
updateHashSetModels(validSetsOnly(hashDbManager.getKnownBadFileHashSets()), knownBadHashSetModels);
|
||||
}
|
||||
|
||||
private List<HashDb> validSetsOnly(List<HashDb> hashDbs){
|
||||
private List<HashDb> validSetsOnly(List<HashDb> hashDbs) {
|
||||
List<HashDb> validDbs = new ArrayList<>();
|
||||
for(HashDb db:hashDbs){
|
||||
try{
|
||||
if(db.isValid()){
|
||||
for (HashDb db : hashDbs) {
|
||||
try {
|
||||
if (db.isValid()) {
|
||||
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
|
||||
}
|
||||
}
|
||||
return validDbs;
|
||||
}
|
||||
|
||||
void updateHashSetModels(List<HashDb> hashDbs, List<HashSetModel> hashSetModels) {
|
||||
void updateHashSetModels() {
|
||||
List<HashDb> hashDbs = validSetsOnly(hashDbManager.getAllHashSets());
|
||||
|
||||
List<HashDb> hashDatabases = new ArrayList<>(hashDbs);
|
||||
|
||||
@ -152,15 +139,15 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
|
||||
List<HashSetModel> deletedHashSetModels = new ArrayList<>();
|
||||
for (HashSetModel model : hashSetModels) {
|
||||
boolean foundDatabase = false;
|
||||
for(HashDb db : hashDatabases){
|
||||
if(model.getDatabase().equals(db)){
|
||||
for (HashDb db : hashDatabases) {
|
||||
if (model.getDatabase().equals(db)) {
|
||||
model.setValid(isHashDbValid(db));
|
||||
hashDatabases.remove(db);
|
||||
foundDatabase = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(! foundDatabase){
|
||||
if (!foundDatabase) {
|
||||
deletedHashSetModels.add(model);
|
||||
}
|
||||
}
|
||||
@ -179,8 +166,7 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
|
||||
void reset(HashLookupModuleSettings newSettings) {
|
||||
initializeHashSetModels(newSettings);
|
||||
alwaysCalcHashesCheckbox.setSelected(newSettings.shouldCalculateHashes());
|
||||
knownHashSetsTableModel.fireTableDataChanged();
|
||||
knownBadHashSetsTableModel.fireTableDataChanged();
|
||||
hashSetsTableModel.fireTableDataChanged();
|
||||
}
|
||||
|
||||
private boolean isHashDbValid(HashDb hashDb) {
|
||||
@ -205,7 +191,7 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
|
||||
this.valid = valid;
|
||||
}
|
||||
|
||||
HashDb getDatabase(){
|
||||
HashDb getDatabase() {
|
||||
return db;
|
||||
}
|
||||
|
||||
@ -213,6 +199,16 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
|
||||
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) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
@ -254,7 +250,7 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
|
||||
if (columnIndex == 0) {
|
||||
return hashSets.get(rowIndex).isEnabled();
|
||||
} 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
|
||||
private void initComponents() {
|
||||
|
||||
jScrollPane1 = new javax.swing.JScrollPane();
|
||||
knownHashTable = new javax.swing.JTable();
|
||||
knownBadHashDbsLabel = new javax.swing.JLabel();
|
||||
knownHashDbsLabel = new javax.swing.JLabel();
|
||||
hashDbsLabel = new javax.swing.JLabel();
|
||||
hashDbsScrollPane = new javax.swing.JScrollPane();
|
||||
hashTable = new javax.swing.JTable();
|
||||
alwaysCalcHashesCheckbox = new javax.swing.JCheckBox();
|
||||
jScrollPane2 = new javax.swing.JScrollPane();
|
||||
knownBadHashTable = new javax.swing.JTable();
|
||||
|
||||
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));
|
||||
knownHashTable.setShowHorizontalLines(false);
|
||||
knownHashTable.setShowVerticalLines(false);
|
||||
jScrollPane1.setViewportView(knownHashTable);
|
||||
hashDbsScrollPane.setBorder(javax.swing.BorderFactory.createEtchedBorder());
|
||||
|
||||
knownBadHashDbsLabel.setText(org.openide.util.NbBundle.getMessage(HashLookupModuleSettingsPanel.class, "HashLookupModuleSettingsPanel.knownBadHashDbsLabel.text")); // NOI18N
|
||||
|
||||
knownHashDbsLabel.setText(org.openide.util.NbBundle.getMessage(HashLookupModuleSettingsPanel.class, "HashLookupModuleSettingsPanel.knownHashDbsLabel.text")); // NOI18N
|
||||
hashTable.setBackground(new java.awt.Color(240, 240, 240));
|
||||
hashTable.setShowHorizontalLines(false);
|
||||
hashTable.setShowVerticalLines(false);
|
||||
hashDbsScrollPane.setViewportView(hashTable);
|
||||
|
||||
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
|
||||
@ -314,21 +305,6 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
|
||||
alwaysCalcHashesCheckbox.setVerticalAlignment(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);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
@ -337,14 +313,11 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(knownHashDbsLabel)
|
||||
.addComponent(hashDbsLabel)
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addComponent(knownBadHashDbsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 290, Short.MAX_VALUE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(10, 10, 10)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)))
|
||||
.addComponent(hashDbsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 494, Short.MAX_VALUE))
|
||||
.addComponent(alwaysCalcHashesCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
@ -352,26 +325,19 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(2, 2, 2)
|
||||
.addComponent(knownHashDbsLabel)
|
||||
.addComponent(hashDbsLabel)
|
||||
.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)
|
||||
.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)
|
||||
.addGap(0, 0, 0))
|
||||
.addContainerGap())
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JCheckBox alwaysCalcHashesCheckbox;
|
||||
private javax.swing.JScrollPane jScrollPane1;
|
||||
private javax.swing.JScrollPane jScrollPane2;
|
||||
private javax.swing.JLabel knownBadHashDbsLabel;
|
||||
private javax.swing.JTable knownBadHashTable;
|
||||
private javax.swing.JLabel knownHashDbsLabel;
|
||||
private javax.swing.JTable knownHashTable;
|
||||
private javax.swing.JLabel hashDbsLabel;
|
||||
private javax.swing.JScrollPane hashDbsScrollPane;
|
||||
private javax.swing.JTable hashTable;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
|
@ -49,7 +49,6 @@ import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
|
||||
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.SleuthkitHashSet;
|
||||
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.autopsy.modules.hashdatabase.HashDbManager.HashDb;
|
||||
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.SetEvt;
|
||||
@ -100,8 +99,8 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String propName = evt.getPropertyName();
|
||||
if(propName.equals(SetEvt.DB_ADDED.toString()) ||
|
||||
propName.equals(SetEvt.DB_DELETED.toString())) {
|
||||
if (propName.equals(SetEvt.DB_ADDED.toString())
|
||||
|| propName.equals(SetEvt.DB_DELETED.toString())) {
|
||||
hashSetTableModel.refreshModel();
|
||||
}
|
||||
}
|
||||
@ -282,7 +281,7 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
|
||||
|
||||
// Update ingest option components.
|
||||
sendIngestMessagesCheckBox.setSelected(db.getSendIngestMessages());
|
||||
sendIngestMessagesCheckBox.setEnabled(!ingestIsRunning && db.getKnownFilesType().equals(KnownFilesType.KNOWN_BAD));
|
||||
sendIngestMessagesCheckBox.setEnabled(!ingestIsRunning && db.getKnownFilesType().isInboxMessagesAllowed());
|
||||
|
||||
// Update database action buttons.
|
||||
createDatabaseButton.setEnabled(true);
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.modules.hashdatabase;
|
||||
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.HashEntry;
|
||||
|
||||
interface HashSetParser {
|
||||
|
||||
@ -26,7 +27,8 @@ interface HashSetParser {
|
||||
* Get the next hash to import
|
||||
*
|
||||
* @return The hash as a string, or null if the end of file was reached
|
||||
* without error
|
||||
* without error
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
String getNextHash() throws TskCoreException;
|
||||
@ -50,4 +52,20 @@ interface HashSetParser {
|
||||
* Closes the import file
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
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
|
||||
@ -186,7 +187,7 @@ class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements P
|
||||
* Get the newly created database
|
||||
*
|
||||
* @return the imported database. May be null if an error occurred or
|
||||
* the user canceled
|
||||
* the user canceled
|
||||
*/
|
||||
synchronized HashDbManager.CentralRepoHashSet getDatabase() {
|
||||
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.
|
||||
*
|
||||
* @return true if the import process completed without error, false
|
||||
* otherwise
|
||||
* otherwise
|
||||
*/
|
||||
boolean getImportSuccess() {
|
||||
return importSuccess.get();
|
||||
@ -231,12 +232,7 @@ class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements P
|
||||
|
||||
try {
|
||||
// Conver to the FileKnown enum used by EamGlobalSet
|
||||
TskData.FileKnown knownStatus;
|
||||
if (knownFilesType.equals(HashDbManager.HashDb.KnownFilesType.KNOWN)) {
|
||||
knownStatus = TskData.FileKnown.KNOWN;
|
||||
} else {
|
||||
knownStatus = TskData.FileKnown.BAD;
|
||||
}
|
||||
TskData.FileKnown knownStatus = knownFilesType.getFileKnown();
|
||||
|
||||
// Create an empty hashset in the central repository
|
||||
CentralRepository dbManager = CentralRepository.getInstance();
|
||||
@ -255,14 +251,14 @@ class ImportCentralRepoDbProgressDialog extends javax.swing.JDialog implements P
|
||||
return null;
|
||||
}
|
||||
|
||||
String newHash = hashSetParser.getNextHash();
|
||||
HashEntry newHash = hashSetParser.getNextHashEntry();
|
||||
|
||||
if (newHash != null) {
|
||||
CentralRepoFileInstance eamGlobalFileInstance = new CentralRepoFileInstance(
|
||||
referenceSetID.get(),
|
||||
newHash,
|
||||
newHash.getMd5Hash(),
|
||||
knownStatus,
|
||||
"");
|
||||
newHash.getComment());
|
||||
|
||||
globalInstances.add(eamGlobalFileInstance);
|
||||
|
||||
|
@ -25,6 +25,7 @@ import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.HashEntry;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
@ -68,7 +69,9 @@ public class KdbHashSetParser implements HashSetParser {
|
||||
}
|
||||
|
||||
// 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
|
||||
} catch (ClassNotFoundException | SQLException ex) {
|
||||
@ -77,15 +80,21 @@ public class KdbHashSetParser implements HashSetParser {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next hash to import
|
||||
*
|
||||
* @return The hash as a string
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
@Override
|
||||
public String getNextHash() throws TskCoreException {
|
||||
return getNextHashEntry().getMd5Hash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashEntry getNextHashEntry() throws TskCoreException {
|
||||
try {
|
||||
if (resultSet.next()) {
|
||||
byte[] hashBytes = resultSet.getBytes("md5");
|
||||
@ -98,13 +107,15 @@ public class KdbHashSetParser implements HashSetParser {
|
||||
throw new TskCoreException("Hash has incorrect length: " + sb.toString());
|
||||
}
|
||||
|
||||
String md5Hash = sb.toString();
|
||||
String comment = resultSet.getString("comment");
|
||||
totalHashesRead++;
|
||||
return sb.toString();
|
||||
return new HashEntry(null, md5Hash, null, null, comment);
|
||||
} else {
|
||||
throw new TskCoreException("Could not read expected number of hashes from hash set " + filename);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,15 @@
|
||||
CTL_OpenPersonas=Personas
|
||||
CTL_PersonasTopComponentAction=PersonasTopComponent
|
||||
CTL_PersonasTopComponent=Personas
|
||||
PersonasTopComponent.searchField.text=John Doe
|
||||
PersonasTopComponent.searchBtn.text=Search
|
||||
PersonasTopComponent.searchNameRadio.text=Name
|
||||
PersonasTopComponent.searchAccountRadio.text=Account
|
||||
PersonasTopComponent.filterResultsTable.columnModel.title1=Name
|
||||
PersonasTopComponent.filterResultsTable.columnModel.title0=ID
|
||||
PersonasTopComponent.resultAccountsLbl.text=Accounts:
|
||||
PersonasTopComponent.resultAliasesLbl.text=Aliases:
|
||||
PersonasTopComponent.resultNameLbl.text=Name:
|
||||
PersonasTopComponent.resultCasesLbl.text=Cases found in:
|
||||
PersonasTopComponent.resultNameField.text=Johnathan Dough
|
||||
PersonaDetailsTopComponent.resultNameLbl.text=Name:
|
||||
PersonaDetailsTopComponent.resultCasesLbl.text=Cases found in:
|
||||
PersonaDetailsTopComponent.resultAccountsLbl.text=Accounts:
|
||||
PersonaDetailsTopComponent.resultAliasesLbl.text=Aliases:
|
||||
PersonaDetailsTopComponent.resultNameField.text=Johnathan Dough
|
||||
PersonaSearchTopComponent.searchAccountRadio.text=Account
|
||||
PersonaSearchTopComponent.searchNameRadio.text=Name
|
||||
PersonaSearchTopComponent.searchField.text=
|
||||
PersonaSearchTopComponent.searchBtn.text=Search
|
||||
PersonaSearchTopComponent.filterResultsTable.columnModel.title1=Name
|
||||
PersonaSearchTopComponent.filterResultsTable.columnModel.title0=ID
|
||||
PersonaSearchTopComponent.filterResultsTable.toolTipText=
|
||||
|
@ -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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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="<String>"/>
|
||||
</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="<String>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -18,33 +18,26 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.persona;
|
||||
|
||||
import org.openide.windows.TopComponent;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.windows.RetainLocation;
|
||||
import org.openide.windows.TopComponent;
|
||||
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.Registration(mode = "personas", openAtStartup = false)
|
||||
@RetainLocation("personas")
|
||||
@TopComponent.Registration(mode = "personadetails", openAtStartup = false)
|
||||
@RetainLocation("personadetails")
|
||||
@SuppressWarnings("PMD.SingularField")
|
||||
public final class PersonasTopComponent extends TopComponent {
|
||||
public final class PersonaDetailsTopComponent extends TopComponent {
|
||||
|
||||
@Messages({
|
||||
"PTopComponent_Name=Personas"
|
||||
"PDTopComponent_Name=Persona Details"
|
||||
})
|
||||
public PersonasTopComponent() {
|
||||
public PersonaDetailsTopComponent() {
|
||||
initComponents();
|
||||
setName(Bundle.PTopComponent_Name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentOpened() {
|
||||
super.componentOpened();
|
||||
WindowManager.getDefault().setTopComponentFloating(this, true);
|
||||
setName(Bundle.PDTopComponent_Name());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,15 +48,6 @@ public final class PersonasTopComponent extends TopComponent {
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN: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();
|
||||
resultNameLbl = new javax.swing.JLabel();
|
||||
resultNameField = new javax.swing.JTextField();
|
||||
@ -77,102 +61,19 @@ public final class PersonasTopComponent extends TopComponent {
|
||||
aliasesListPane = new javax.swing.JScrollPane();
|
||||
aliasesList = new javax.swing.JList<>();
|
||||
|
||||
setMinimumSize(new java.awt.Dimension(400, 400));
|
||||
|
||||
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
|
||||
org.openide.awt.Mnemonics.setLocalizedText(resultNameLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsTopComponent.class, "PersonaDetailsTopComponent.resultNameLbl.text")); // NOI18N
|
||||
|
||||
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() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent 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(
|
||||
new Object [][] {
|
||||
@ -202,7 +103,7 @@ public final class PersonasTopComponent extends TopComponent {
|
||||
});
|
||||
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>() {
|
||||
String[] strings = { "Investigation 13", "Scene 5" };
|
||||
@ -259,26 +160,30 @@ public final class PersonasTopComponent extends TopComponent {
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
splitPane.setRightComponent(detailsPanel);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
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.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
|
||||
|
||||
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
|
||||
// TODO add your handling code here:
|
||||
|
||||
}//GEN-LAST:event_resultNameFieldActionPerformed
|
||||
|
||||
// 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.JScrollPane casesListPane;
|
||||
private javax.swing.JPanel detailsPanel;
|
||||
private javax.swing.JScrollPane filterResultsPane;
|
||||
private javax.swing.JTable filterResultsTable;
|
||||
private javax.swing.JLabel resultAccountsLbl;
|
||||
private javax.swing.JLabel resultAliasesLbl;
|
||||
private javax.swing.JLabel resultCasesLbl;
|
||||
private javax.swing.JTextField resultNameField;
|
||||
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
|
||||
|
||||
@Override
|
||||
public void componentOpened() {
|
||||
super.componentOpened();
|
||||
WindowManager.getDefault().setTopComponentFloating(this, true);
|
||||
}
|
||||
}
|
@ -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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -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
|
||||
|
||||
}
|
@ -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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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="<String>"/>
|
||||
</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="<String>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -65,6 +65,7 @@ import org.sleuthkit.datamodel.Pool;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TaggingManager.ContentTagChange;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
@ -740,13 +741,13 @@ public class PortableCaseReportModule implements ReportModule {
|
||||
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
|
||||
}
|
||||
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)
|
||||
// and save it if present
|
||||
String appData = getImageTagDataForContentTag(tag);
|
||||
if (! appData.isEmpty()) {
|
||||
addImageTagToPortableCase(newContentTag, appData);
|
||||
addImageTagToPortableCase(newContentTag.getAddedTag(), appData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -847,7 +848,7 @@ public class PortableCaseReportModule implements ReportModule {
|
||||
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
|
||||
}
|
||||
portableSkCase.addBlackboardArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment());
|
||||
portableSkCase.getTaggingManager().addArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
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.Logger;
|
||||
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.events.DataSourceAnalysisEvent;
|
||||
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
||||
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector.FileTypeDetectorInitException;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
@ -109,8 +111,6 @@ public final class ImageGalleryController {
|
||||
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
|
||||
* opening of case resources and destroyed during the closing of case
|
||||
@ -737,7 +737,7 @@ public final class ImageGalleryController {
|
||||
List<TagSet> tagSetList = getCaseDatabase().getTaggingManager().getTagSets();
|
||||
if (tagSetList != null && !tagSetList.isEmpty()) {
|
||||
for (TagSet set : tagSetList) {
|
||||
if (set.getName().startsWith(CATEGORY_TAG_SET_PREFIX)) {
|
||||
if (set.getName().equals(TagsManager.getCategoryTagSetName())) {
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,6 @@ public class CategorizeAction extends Action {
|
||||
this.tagName = tagName;
|
||||
setGraphic(getGraphic(tagName));
|
||||
setEventHandler(actionEvent -> addCatToFiles(selectedFileIDs));
|
||||
setAccelerator(new KeyCodeCombination(KeyCode.getKeyCode(getCategoryNumberFromTagName(tagName))));
|
||||
}
|
||||
|
||||
static public Menu getCategoriesMenu(ImageGalleryController controller) {
|
||||
@ -94,16 +93,6 @@ public class CategorizeAction extends Action {
|
||||
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
|
||||
* selecting a category
|
||||
|
@ -13,6 +13,4 @@ DrawableAttribute.name=Name
|
||||
DrawableAttribute.path=Path
|
||||
DrawableAttribute.tags=Tags
|
||||
DrawableAttribute.width=Width
|
||||
DrawableTagsManager.bookMark=Bookmark
|
||||
DrawableTagsManager.followUp=Follow Up
|
||||
VideoFile.getMedia.progress=writing temporary file to disk
|
||||
|
@ -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.DeletedContentTagInfo;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.DhsImageCategory;
|
||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
@ -62,9 +61,7 @@ public class CategoryManager {
|
||||
private static final Logger LOGGER = Logger.getLogger(CategoryManager.class.getName());
|
||||
|
||||
/**
|
||||
* the DrawableDB that backs the category counts cache. The counts are
|
||||
* initialized from this, and the counting of CAT-0 is always delegated to
|
||||
* this db.
|
||||
* the DrawableDB that backs the category counts cache.
|
||||
*/
|
||||
private final DrawableDB drawableDb;
|
||||
|
||||
|
@ -29,7 +29,6 @@ import javafx.scene.Node;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
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.ContentTagDeletedEvent;
|
||||
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
|
||||
* the autopsy Db. Delegates some work to the backing autopsy TagsManager.
|
||||
*/
|
||||
@NbBundle.Messages({"DrawableTagsManager.followUp=Follow Up",
|
||||
"DrawableTagsManager.bookMark=Bookmark"})
|
||||
public final class DrawableTagsManager {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DrawableTagsManager.class.getName());
|
||||
@ -78,8 +75,8 @@ public final class DrawableTagsManager {
|
||||
|
||||
public DrawableTagsManager(ImageGalleryController controller) throws TskCoreException {
|
||||
this.autopsyTagsManager = controller.getCase().getServices().getTagsManager();
|
||||
followUpTagName = getTagName(Bundle.DrawableTagsManager_followUp());
|
||||
bookmarkTagName = getTagName(Bundle.DrawableTagsManager_bookMark());
|
||||
followUpTagName = getTagName(TagsManager.getFollowUpDisplayString());
|
||||
bookmarkTagName = getTagName(TagsManager.getBookmarkDisplayString());
|
||||
this.controller = controller;
|
||||
|
||||
compareByDisplayName = new Comparator<TagName>() {
|
||||
|
@ -61,7 +61,7 @@
|
||||
<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">
|
||||
<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>
|
||||
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
||||
<image>
|
||||
|
Loading…
x
Reference in New Issue
Block a user