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

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

View File

@ -90,6 +90,7 @@ public interface AutopsyService {
private final Case theCase;
private final 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;
}
}
/**

View File

@ -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;

View File

@ -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);
}
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));

View File

@ -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.
*

View File

@ -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();
}
/**

View File

@ -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
*

View File

@ -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.
*
@ -1024,8 +1026,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
+ getConflictClause();
}
else {
} else {
sql = "INSERT INTO "
+ tableName
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
@ -1105,7 +1106,6 @@ abstract class RdbmsCentralRepo implements CentralRepository {
return account;
}
@Override
public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException {
try {
@ -1115,8 +1115,6 @@ abstract class RdbmsCentralRepo implements CentralRepository {
}
}
@Override
public Collection<CentralRepoAccountType> getAllAccountTypes() throws CentralRepoException {
@ -1126,7 +1124,6 @@ abstract class RdbmsCentralRepo implements CentralRepository {
try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
try (ResultSet resultSet = preparedStatement.executeQuery();) {
while (resultSet.next()) {
Account.Type acctType = new Account.Type(resultSet.getString("type_name"), resultSet.getString("display_name"));
@ -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
@ -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 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,10 +1198,9 @@ 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 accountUniqueID unique account identifier
@ -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");
@ -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.
*/
@Override
public CentralRepoExaminer getOrInsertExaminer(String examinerLoginName) throws CentralRepoException {
@ -3397,7 +3424,6 @@ abstract class RdbmsCentralRepo implements CentralRepository {
}
}
/**
* Get the EamArtifact.Type that has the given Type.Id from the central repo
*
@ -3436,7 +3462,8 @@ 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.
*/

View File

@ -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();
if (TagsManager.getNotableTagDisplayNames().contains(tagAdded.getName().getDisplayName())) {
if (tagAdded.getContent() instanceof AbstractFile) {
af = (AbstractFile) tagAdded.getContent();
knownStatus = TskData.FileKnown.BAD;
comment = tagAdded.getComment();
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.WARNING, "Error updating non-file object");
return;
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));
}
} 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
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 {
// 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");
// 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;
}
} else {
// There's still at least one bad tag, so leave the known status as is
// 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 find content", ex);
return;
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,26 +324,71 @@ 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();
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 {
// The added tag isn't flagged as bad in central repo, so do nothing
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));
}
}
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;
}
} else { //BLACKBOARD_ARTIFACT_TAG_DELETED
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();
@ -280,60 +396,53 @@ final class CaseEventListener implements PropertyChangeListener {
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);
if (isKnownFile(content)) {
return;
}
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 = "";
if (hasNotableTag(tags)) {
setArtifactKnownStatus(bbArtifact, TskData.FileKnown.BAD);
} else {
// There's still at least one bad tag, so leave the known status as is
return;
setArtifactKnownStatus(bbArtifact, TskData.FileKnown.UNKNOWN);
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Failed to find content", ex);
LOGGER.log(Level.SEVERE, "Failed to obtain tags manager for case.", ex);
return;
}
}
if ((content instanceof AbstractFile) && (((AbstractFile) content).getKnown() == TskData.FileKnown.KNOWN)) {
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
}
}

View File

@ -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));
private static List<CorrelationAttributeInstance> getCentralRepositoryData(AbstractFile sourceFile) {
if (sourceFile == null || StringUtils.isEmpty(sourceFile.getMd5Hash())) {
return new ArrayList<>();
}
List<CorrelationAttributeInstance.Type> artifactTypes = null;
try {
List<CorrelationAttributeInstance.Type> artifactTypes = CentralRepository.getInstance().getDefinedCorrelationTypes();
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();
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;
}
}
// 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);
}
boolean commentDataFound = false;
/**
* Given a type and a value for that type, does a lookup in the Central
* Repository for matching values that have comments.
*
* @param lookupKeys The type and value to lookup.
*
* @return The found correlation attribute instances.
*/
private static List<CorrelationAttributeInstance> getCorrelationAttributeComments(List<Pair<CorrelationAttributeInstance.Type, String>> lookupKeys) {
List<CorrelationAttributeInstance> instancesToRet = new ArrayList<>();
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);
}
}
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()));
}
if (commentDataFound == false) {
addMessage(html, "There is no comment data for the selected content in the Central Repository.");
}
} catch (CentralRepoException | TskCoreException ex) {
} 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
}
endSection(html);
}
return instancesToRet;
}
/**
* Set the text of the text panel.
* 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 text The text to set to the text panel.
* @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 setText(String text) {
jTextPane1.setText("<html><body>" + text + "</body></html>"); //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;
}
/**
* Start a new data section.
* Appends a table where items are displayed in rows of key-value pairs.
*
* @param html The HTML text to add the section to.
* @param sectionName The name of the section.
* @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 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> 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 message.
* Appends a generic table to the parent element.
*
* @param html The HTML text to add the message to.
* @param message The message text.
* @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.
*/
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 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 tag.
* Appends a row to the parent element (should be thead or tbody).
*
* @param html The HTML text to add the table to.
* @param tag The tag 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.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 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;
}
/**
* Add a data table containing information about a correlation attribute
* instance in the Central Repository.
* Appends a new section with a section header to the parent element.
*
* @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 parent The element to append this section to.
* @param headerText The text for the section.
*
* @return The div for the new section.
*/
@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 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;
}
/**
* Start a data table.
* Appends a new subsection with a subsection header to the parent element.
*
* @param html The HTML text to add the table to.
* @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 startTable(StringBuilder html) {
html.append("<table>"); //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;
}
/**
* Add a data row to a 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 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 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 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
}
/**
* End a data table.
*
* @param html The HTML text on which to end a table.
*/
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);
}
}

View File

@ -0,0 +1,56 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
import java.awt.Component;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Common interface implemented by artifact viewers.
*
* An artifact viewer displays the artifact in a custom
* layout panel suitable for the artifact type.
*
*/
public interface ArtifactContentViewer {
/**
* Called to display the contents of the given artifact.
*
* @param artifact the artifact to display.
*/
void setArtifact(BlackboardArtifact artifact);
/**
* Returns the panel.
*
* @return display panel.
*/
Component getComponent();
/**
* Checks whether the given artifact is supported by the viewer.
*
* @param artifact Artifact to check.
*
* @return True if the artifact can be displayed by the viewer, false otherwise.
*/
boolean isSupported(BlackboardArtifact artifact);
}

View File

@ -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

View File

@ -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

View File

@ -164,3 +164,5 @@ MediaViewImagePanel.tagsMenu.text_1=\u30bf\u30b0\u30e1\u30cb\u30e5\u30fc
SQLiteViewer.readTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
# {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

View File

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

View File

@ -0,0 +1,320 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* This class displays TSK_CONTACT artifact.
*/
public class ContactArtifactViewer extends javax.swing.JPanel implements ArtifactContentViewer {
private final static Logger logger = Logger.getLogger(ContactArtifactViewer.class.getName());
private static final long serialVersionUID = 1L;
/**
* Creates new form for ContactArtifactViewer
*/
public ContactArtifactViewer() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
namePanel = new javax.swing.JPanel();
contactNameLabel = new javax.swing.JLabel();
phonesLabel = new javax.swing.JLabel();
phoneNumbersPanel = new javax.swing.JPanel();
emailsLabel = new javax.swing.JLabel();
emailsPanel = new javax.swing.JPanel();
othersLabel = new javax.swing.JLabel();
otherAttrsPanel = new javax.swing.JPanel();
filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
setLayout(new java.awt.GridBagLayout());
contactNameLabel.setFont(contactNameLabel.getFont().deriveFont((contactNameLabel.getFont().getStyle() | java.awt.Font.ITALIC) | java.awt.Font.BOLD, contactNameLabel.getFont().getSize()+6));
org.openide.awt.Mnemonics.setLocalizedText(contactNameLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.contactNameLabel.text")); // NOI18N
javax.swing.GroupLayout namePanelLayout = new javax.swing.GroupLayout(namePanel);
namePanel.setLayout(namePanelLayout);
namePanelLayout.setHorizontalGroup(
namePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(namePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(contactNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 240, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
namePanelLayout.setVerticalGroup(
namePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(namePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(contactNameLabel)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
add(namePanel, gridBagConstraints);
phonesLabel.setFont(phonesLabel.getFont().deriveFont(phonesLabel.getFont().getStyle() | java.awt.Font.BOLD, phonesLabel.getFont().getSize()+2));
org.openide.awt.Mnemonics.setLocalizedText(phonesLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.phonesLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(phonesLabel, gridBagConstraints);
phoneNumbersPanel.setLayout(new java.awt.GridBagLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(phoneNumbersPanel, gridBagConstraints);
emailsLabel.setFont(emailsLabel.getFont().deriveFont(emailsLabel.getFont().getStyle() | java.awt.Font.BOLD, emailsLabel.getFont().getSize()+2));
org.openide.awt.Mnemonics.setLocalizedText(emailsLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.emailsLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(emailsLabel, gridBagConstraints);
emailsPanel.setLayout(new java.awt.GridBagLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 4;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(emailsPanel, gridBagConstraints);
othersLabel.setFont(othersLabel.getFont().deriveFont(othersLabel.getFont().getStyle() | java.awt.Font.BOLD, othersLabel.getFont().getSize()+2));
org.openide.awt.Mnemonics.setLocalizedText(othersLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.othersLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 5;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(othersLabel, gridBagConstraints);
otherAttrsPanel.setLayout(new java.awt.GridBagLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 6;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(otherAttrsPanel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 7;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weighty = 1.0;
add(filler1, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 4;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridheight = 8;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.ipadx = 2;
gridBagConstraints.weightx = 1.0;
add(filler2, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
@Override
public void setArtifact(BlackboardArtifact artifact) {
// wipe the panel clean
this.removeAll();
initComponents();
List<BlackboardAttribute> phoneNumList = new ArrayList<>();
List<BlackboardAttribute> emailList = new ArrayList<>();
List<BlackboardAttribute> nameList = new ArrayList<>();
List<BlackboardAttribute> otherList = new ArrayList<>();
try {
// Get all the attributes and group them by the section panels they go in
for (BlackboardAttribute bba : artifact.getAttributes()) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_PHONE")) {
phoneNumList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_EMAIL")) {
emailList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_NAME")) {
nameList.add(bba);
} else {
otherList.add(bba);
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting attributes for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
}
// update name section
updateNamePanel(nameList);
// update contact attributes sections
updateSection(phoneNumList, this.phonesLabel, this.phoneNumbersPanel);
updateSection(emailList, this.emailsLabel, this.emailsPanel);
updateSection(otherList, this.othersLabel, this.otherAttrsPanel);
// repaint
this.revalidate();
}
@Override
public Component getComponent() {
// Slap a vertical scrollbar on the panel.
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
/**
* Checks if the given artifact is supported by this viewer.
* This viewer supports TSK_CONTACT artifacts.
*
* @param artifact artifact to check.
* @return True if the artifact is supported, false otherwise.
*/
@Override
public boolean isSupported(BlackboardArtifact artifact) {
return artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID();
}
/**
* Updates the contact name in the view.
*
* @param attributesList
*/
private void updateNamePanel(List<BlackboardAttribute> attributesList) {
for (BlackboardAttribute bba : attributesList) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_NAME")) {
contactNameLabel.setText(bba.getDisplayString());
break;
}
}
contactNameLabel.revalidate();
}
/**
* Updates the view by displaying the given list of attributes in the given section panel.
*
* @param sectionAttributesList list of attributes to display.
* @param sectionLabel section name label.
* @param sectionPanel section panel to display the attributes in.
*/
private void updateSection(List<BlackboardAttribute> sectionAttributesList, JLabel sectionLabel, JPanel sectionPanel) {
// If there are no attributes for tis section, hide the section panel and the section label
if (sectionAttributesList.isEmpty()) {
sectionLabel.setVisible(false);
sectionPanel.setVisible(false);
return;
}
// create a gridbag layout to show each attribute on one line
GridBagLayout gridBagLayout = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.FIRST_LINE_START;
constraints.gridy = 0;
constraints.insets = new java.awt.Insets(4, 12, 0, 0);
for (BlackboardAttribute bba : sectionAttributesList) {
constraints.fill = GridBagConstraints.NONE;
constraints.weightx = 0;
constraints.gridx = 0;
// Add a label for attribute type
javax.swing.JLabel attrTypeLabel = new javax.swing.JLabel();
String attrLabel = bba.getAttributeType().getDisplayName();
attrTypeLabel.setText(attrLabel);
// make type label bold - uncomment if needed.
//attrTypeLabel.setFont(attrTypeLabel.getFont().deriveFont(Font.BOLD, attrTypeLabel.getFont().getSize() ));
gridBagLayout.setConstraints(attrTypeLabel, constraints);
sectionPanel.add(attrTypeLabel);
// Add the attribute value
constraints.gridx++;
javax.swing.JLabel attrValueLabel = new javax.swing.JLabel();
attrValueLabel.setText(bba.getValueString());
gridBagLayout.setConstraints(attrValueLabel, constraints);
sectionPanel.add(attrValueLabel);
// add a filler to take up rest of the space
constraints.gridx++;
constraints.weightx = 1.0;
constraints.fill = GridBagConstraints.HORIZONTAL;
sectionPanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0)));
constraints.gridy++;
}
sectionPanel.setLayout(gridBagLayout);
sectionPanel.revalidate();
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel contactNameLabel;
private javax.swing.JLabel emailsLabel;
private javax.swing.JPanel emailsPanel;
private javax.swing.Box.Filler filler1;
private javax.swing.Box.Filler filler2;
private javax.swing.JPanel namePanel;
private javax.swing.JPanel otherAttrsPanel;
private javax.swing.JLabel othersLabel;
private javax.swing.JPanel phoneNumbersPanel;
private javax.swing.JLabel phonesLabel;
// End of variables declaration//GEN-END:variables
}

View File

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

View File

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

View File

@ -30,8 +30,6 @@ DataContentViewerArtifact.pageLabel2.text=Result
DataContentViewerArtifact.nextPageButton.text=
DataContentViewerArtifact.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=

View File

@ -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=

View File

@ -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, &quot;{key}&quot;)"/>
</Property>
</Properties>
</MenuItem>
<MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerArtifact.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</MenuItem>
</SubComponents>
</Container>
</NonVisualComponents>
<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>

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="noChangeRadioButtonActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -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;

View File

@ -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,8 +119,6 @@
<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="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"/>
@ -143,22 +147,26 @@
<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" 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, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="HashDbImportDatabaseDialog.noChangeRadioButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="noChangeRadioButtonActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -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,8 +375,6 @@ 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.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(fileTypeRadioButton)
.addComponent(centralRepoRadioButton)
@ -387,18 +399,22 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
.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()
.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;

View File

@ -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.
*/
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,10 +210,75 @@ public class HashDbIngestModule implements FileIngestModule {
return ProcessResult.ERROR;
}
if (shouldSkip(file)) {
return ProcessResult.OK;
}
// Safely get a reference to the totalsForIngestJobs object
IngestJobTotals totals = getTotalsForIngestJobs(jobId);
// calc hash value
String md5Hash = getHash(file, totals);
if (md5Hash == null) {
return ProcessResult.ERROR;
}
// the processing result of handling this file
ProcessResult ret = ProcessResult.OK;
// look up in notable first
FindInHashsetsResult knownBadResult = findInHashsets(file, totals.totalKnownBadCount,
totals.totalLookuptime, knownBadHashSets, TskData.FileKnown.BAD, knownBadLookupError);
boolean foundBad = knownBadResult.isFound();
if (knownBadResult.isError()) {
ret = ProcessResult.ERROR;
}
// look up no change items next
FindInHashsetsResult noChangeResult = findInHashsets(file, totals.totalNoChangeCount,
totals.totalLookuptime, noChangeHashSets, TskData.FileKnown.UNKNOWN, noChangeLookupError);
if (noChangeResult.isError()) {
ret = ProcessResult.ERROR;
}
// If the file is not in the notable sets, search for it in the known sets.
// Any hit is sufficient to classify it as known, and there is no need to create
// a hit artifact or send a message to the application inbox.
if (!foundBad) {
for (HashDb db : knownHashSets) {
try {
long lookupstart = System.currentTimeMillis();
if (db.lookupMD5Quick(file)) {
file.setKnown(TskData.FileKnown.KNOWN);
break;
}
long delta = (System.currentTimeMillis() - lookupstart);
totals.totalLookuptime.addAndGet(delta);
} catch (TskException ex) {
reportLookupError(ex, file, knownLookupError);
ret = ProcessResult.ERROR;
}
}
}
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 ProcessResult.OK;
return true;
}
/*
@ -187,22 +288,193 @@ public class HashDbIngestModule implements FileIngestModule {
* for it is developed.
*/
if (file.isDir()) {
return ProcessResult.OK;
return true;
}
// bail out if we have no hashes set
if ((knownHashSets.isEmpty()) && (knownBadHashSets.isEmpty()) && (!settings.shouldCalculateHashes())) {
return ProcessResult.OK;
return true;
}
// Safely get a reference to the totalsForIngestJobs object
IngestJobTotals totals = getTotalsForIngestJobs(jobId);
return false;
}
// calc hash value
String name = file.getName();
long fileId = file.getId();
/**
* 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()) {
if (md5Hash != null && md5Hash.isEmpty()) {
return md5Hash;
}
try {
TimingMetric metric = HealthMonitor.getTimingMetric("Disk Reads: Hash calculation");
long calcstart = System.currentTimeMillis();
@ -221,112 +493,19 @@ public class HashDbIngestModule implements FileIngestModule {
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).", name, fileId), ex); //NON-NLS
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", name),
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 ProcessResult.ERROR;
return null;
}
}
// look up in notable first
boolean foundBad = false;
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);
String hashSetName = db.getDisplayName();
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;
}
}
/*
* 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 the file is not in the notable sets, search for it in the known sets.
// Any hit is sufficient to classify it as known, and there is no need to create
// a hit artifact or send a message to the application inbox.
if (!foundBad) {
for (HashDb db : knownHashSets) {
try {
long lookupstart = System.currentTimeMillis();
if (db.lookupMD5Quick(file)) {
file.setKnown(TskData.FileKnown.KNOWN);
break;
}
long delta = (System.currentTimeMillis() - lookupstart);
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)));
ret = ProcessResult.ERROR;
}
}
}
return ret;
}
/**
* 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);
}
}
}

View File

@ -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
@ -357,7 +358,6 @@ 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;
@ -499,9 +499,9 @@ public class HashDbManager implements PropertyChangeListener {
// 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));
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
@ -510,13 +510,6 @@ public class HashDbManager implements PropertyChangeListener {
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.
@ -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)));
@ -655,8 +651,8 @@ 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(),
if (RuntimeProperties.runningWithGUI()
&& JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
NbBundle.getMessage(this.getClass(), "HashDbManager.dlgMsg.dbNotFoundAtLoc",
hashSetName, configuredPath),
NbBundle.getMessage(this.getClass(), "HashDbManager.dlgTitle.MissingDb"),
@ -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
*/
@ -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
@ -1032,7 +1095,6 @@ public class HashDbManager implements PropertyChangeListener {
return getHashSetName();
}
@Override
public int hashCode() {
int code = 23;
@ -1101,8 +1163,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
*/
@ -1229,12 +1291,7 @@ 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 {
CentralRepoFileInstance fileInstance = new CentralRepoFileInstance(referenceSetID, file.getMd5Hash(),
@ -1258,12 +1315,8 @@ public class HashDbManager implements PropertyChangeListener {
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;
}
TskData.FileKnown type = knownFilesType.getFileKnown();
try {
globalFileInstances.add(new CentralRepoFileInstance(referenceSetID, hashEntry.getMd5Hash(), type, hashEntry.getComment()));
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
@ -1325,10 +1378,7 @@ public class HashDbManager implements PropertyChangeListener {
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(), "", "");
}
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
@ -1367,7 +1417,6 @@ public class HashDbManager implements PropertyChangeListener {
return getDisplayName();
}
@Override
public int hashCode() {
int code = 23;

View File

@ -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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="knownHashDbsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="HashLookupModuleSettingsPanel.knownHashDbsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="alwaysCalcHashesCheckbox">
<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>

View File

@ -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,13 +113,7 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
void update() {
updateHashSetModels();
knownHashSetsTableModel.fireTableDataChanged();
knownBadHashSetsTableModel.fireTableDataChanged();
}
private void updateHashSetModels() {
updateHashSetModels(validSetsOnly(hashDbManager.getKnownFileHashSets()), knownHashSetModels);
updateHashSetModels(validSetsOnly(hashDbManager.getKnownBadFileHashSets()), knownBadHashSetModels);
hashSetsTableModel.fireTableDataChanged();
}
private List<HashDb> validSetsOnly(List<HashDb> hashDbs) {
@ -144,7 +130,8 @@ public final class HashLookupModuleSettingsPanel extends IngestModuleIngestJobSe
return validDbs;
}
void updateHashSetModels(List<HashDb> hashDbs, List<HashSetModel> hashSetModels) {
void updateHashSetModels() {
List<HashDb> hashDbs = validSetsOnly(hashDbManager.getAllHashSets());
List<HashDb> hashDatabases = new ArrayList<>(hashDbs);
@ -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) {
@ -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
}

View File

@ -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);

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.modules.hashdatabase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.HashEntry;
interface HashSetParser {
@ -27,6 +28,7 @@ interface HashSetParser {
*
* @return The hash as a string, or null if the end of file was reached
* 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);
}
}

View File

@ -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
@ -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);

View File

@ -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);
}
}

View File

@ -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=

View File

@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="505" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="detailsPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="555" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="detailsPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="detailsPanel">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="accountsTablePane" alignment="1" pref="0" max="32767" attributes="0"/>
<Component id="aliasesListPane" alignment="1" max="32767" attributes="0"/>
<Component id="casesListPane" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="resultNameLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="resultNameField" pref="447" max="32767" attributes="0"/>
</Group>
<Component id="resultAliasesLbl" alignment="0" max="32767" attributes="0"/>
<Component id="resultAccountsLbl" alignment="0" max="32767" attributes="0"/>
<Component id="resultCasesLbl" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="resultNameLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="resultNameField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="resultAliasesLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="aliasesListPane" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="resultAccountsLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="accountsTablePane" min="-2" pref="153" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="resultCasesLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="casesListPane" pref="118" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="resultNameLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaDetailsTopComponent.resultNameLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="resultNameField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaDetailsTopComponent.resultNameField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="resultNameFieldActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="resultAliasesLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaDetailsTopComponent.resultAliasesLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="resultAccountsLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaDetailsTopComponent.resultAccountsLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="accountsTablePane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="accountsTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
<Table columnCount="2" rowCount="4">
<Column editable="false" title="Type" type="java.lang.String">
<Data value="Email"/>
<Data value="Phone"/>
<Data value="Twitter"/>
<Data value="null"/>
</Column>
<Column editable="false" title="Data" type="java.lang.String">
<Data value="jdb@yahoo.com"/>
<Data value="865-555-5555"/>
<Data value="@jd93.bread"/>
<Data value="null"/>
</Column>
</Table>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="resultCasesLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaDetailsTopComponent.resultCasesLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="casesListPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="casesList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="2">
<StringItem index="0" value="Investigation 13"/>
<StringItem index="1" value="Scene 5"/>
</StringArray>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="aliasesListPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="aliasesList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="3">
<StringItem index="0" value="J.D."/>
<StringItem index="1" value="Fred Smidge"/>
<StringItem index="2" value="Ethan Roseman"/>
</StringArray>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -18,33 +18,26 @@
*/
package org.sleuthkit.autopsy.persona;
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);
}
}

View File

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="searchButtonGroup">
</Component>
</NonVisualComponents>
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 400]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="searchPanel" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="searchPanel" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="searchPanel">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="filterResultsPane" pref="0" max="32767" attributes="0"/>
<Component id="searchField" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="searchNameRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="searchAccountRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="155" max="32767" attributes="0"/>
<Component id="searchBtn" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="searchField" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="searchNameRadio" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchAccountRadio" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="filterResultsPane" pref="320" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JTextField" name="searchField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.searchField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="searchNameRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="searchButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.searchNameRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="searchNameRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="searchAccountRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="searchButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.searchAccountRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="filterResultsPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="filterResultsTable">
<Properties>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.filterResultsTable.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0">
<Column maxWidth="25" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.filterResultsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title>
<Editor/>
<Renderer/>
</Column>
<Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.filterResultsTable.columnModel.title1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title>
<Editor/>
<Renderer/>
</Column>
</TableColumnModel>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="false" resizingAllowed="true"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="searchBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonaSearchTopComponent.searchBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,217 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.persona;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.Collections;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.DefaultTableModel;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.RetainLocation;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
/**
* Top component for the Personas tool
*
*/
@TopComponent.Description(preferredID = "PersonasTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER)
@TopComponent.Registration(mode = "personasearch", openAtStartup = false)
@RetainLocation("personasearch")
@SuppressWarnings("PMD.SingularField")
public final class PersonaSearchTopComponent extends TopComponent {
@Messages({
"PSTopComponent_Name=Persona Search"
})
public PersonaSearchTopComponent() {
initComponents();
setName(Bundle.PSTopComponent_Name());
executeSearch();
searchBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
executeSearch();
}
});
}
/**
* Table model for the persona search results
*/
final class PersonaFilterTableModel extends DefaultTableModel {
private static final long serialVersionUID = 1L;
PersonaFilterTableModel(Object[][] rows, String[] colNames) {
super(rows, colNames);
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
}
private void updateResultsTable(Collection<Persona> results) {
Object[][] rows = new Object[results.size()][2];
int i = 0;
for (Persona result : results) {
rows[i] = new String[]{String.valueOf(result.getId()), result.getName()};
i++;
}
DefaultTableModel updatedTableModel = new PersonaFilterTableModel(
rows,
new String[]{"ID", "Name"}
);
filterResultsTable.setModel(updatedTableModel);
// Formatting
filterResultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
filterResultsTable.getColumnModel().getColumn(0).setMaxWidth(100);
filterResultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
}
private void executeSearch() {
Collection<Persona> results = Collections.EMPTY_LIST;
try {
results = Persona.getPersonaByName(searchField.getText());
} catch (CentralRepoException ex) {
Exceptions.printStackTrace(ex);
}
updateResultsTable(results);
}
@Override
public void componentOpened() {
super.componentOpened();
WindowManager.getDefault().setTopComponentFloating(this, true);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
searchButtonGroup = new javax.swing.ButtonGroup();
searchPanel = new javax.swing.JPanel();
searchField = new javax.swing.JTextField();
searchNameRadio = new javax.swing.JRadioButton();
searchAccountRadio = new javax.swing.JRadioButton();
filterResultsPane = new javax.swing.JScrollPane();
filterResultsTable = new javax.swing.JTable();
searchBtn = new javax.swing.JButton();
setMinimumSize(new java.awt.Dimension(400, 400));
searchField.setText(org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.searchField.text")); // NOI18N
searchButtonGroup.add(searchNameRadio);
searchNameRadio.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(searchNameRadio, org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.searchNameRadio.text")); // NOI18N
searchNameRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
searchNameRadioActionPerformed(evt);
}
});
searchButtonGroup.add(searchAccountRadio);
org.openide.awt.Mnemonics.setLocalizedText(searchAccountRadio, org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.searchAccountRadio.text")); // NOI18N
filterResultsTable.setToolTipText(org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.filterResultsTable.toolTipText")); // NOI18N
filterResultsTable.getTableHeader().setReorderingAllowed(false);
filterResultsPane.setViewportView(filterResultsTable);
if (filterResultsTable.getColumnModel().getColumnCount() > 0) {
filterResultsTable.getColumnModel().getColumn(0).setMaxWidth(25);
filterResultsTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.filterResultsTable.columnModel.title0")); // NOI18N
filterResultsTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.filterResultsTable.columnModel.title1")); // NOI18N
}
org.openide.awt.Mnemonics.setLocalizedText(searchBtn, org.openide.util.NbBundle.getMessage(PersonaSearchTopComponent.class, "PersonaSearchTopComponent.searchBtn.text")); // NOI18N
javax.swing.GroupLayout searchPanelLayout = new javax.swing.GroupLayout(searchPanel);
searchPanel.setLayout(searchPanelLayout);
searchPanelLayout.setHorizontalGroup(
searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(filterResultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(searchField)
.addGroup(searchPanelLayout.createSequentialGroup()
.addComponent(searchNameRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(searchAccountRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 155, Short.MAX_VALUE)
.addComponent(searchBtn)))
.addContainerGap())
);
searchPanelLayout.setVerticalGroup(
searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(searchField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(searchNameRadio)
.addComponent(searchAccountRadio)
.addComponent(searchBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(filterResultsPane, javax.swing.GroupLayout.DEFAULT_SIZE, 320, Short.MAX_VALUE)
.addContainerGap())
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(searchPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(searchPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
private void searchNameRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchNameRadioActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_searchNameRadioActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane filterResultsPane;
private javax.swing.JTable filterResultsTable;
private javax.swing.JRadioButton searchAccountRadio;
private javax.swing.JButton searchBtn;
private javax.swing.ButtonGroup searchButtonGroup;
private javax.swing.JTextField searchField;
private javax.swing.JRadioButton searchNameRadio;
private javax.swing.JPanel searchPanel;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,381 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="searchButtonGroup">
</Component>
</NonVisualComponents>
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 400]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="splitPane" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="splitPane" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="splitPane">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="searchPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="filterResultsPane" pref="0" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="searchField" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="searchNameRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="searchAccountRadio" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="searchBtn" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="25" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="searchField" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="searchNameRadio" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchAccountRadio" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="searchBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="filterResultsPane" pref="0" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JTextField" name="searchField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.searchField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="searchNameRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="searchButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.searchNameRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="searchNameRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="searchAccountRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="searchButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.searchAccountRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="filterResultsPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="filterResultsTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
<Table columnCount="2" rowCount="4">
<Column editable="true" title="ID" type="java.lang.String">
<Data value="0"/>
<Data value="3"/>
<Data value="2"/>
<Data value="1"/>
</Column>
<Column editable="true" title="Name" type="java.lang.String">
<Data value="Johnathn Dough"/>
<Data value="Joe Schmoe"/>
<Data value="Michael Schmoe"/>
<Data value="Ethan Schmoe"/>
</Column>
</Table>
</Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0">
<Column maxWidth="25" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.filterResultsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title>
<Editor/>
<Renderer/>
</Column>
<Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.filterResultsTable.columnModel.title1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title>
<Editor/>
<Renderer/>
</Column>
</TableColumnModel>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="true" resizingAllowed="true"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="searchBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.searchBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="detailsPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="accountsTablePane" pref="0" max="32767" attributes="0"/>
<Component id="aliasesListPane" max="32767" attributes="0"/>
<Component id="casesListPane" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="resultNameLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="resultNameField" pref="447" max="32767" attributes="0"/>
</Group>
<Component id="resultAliasesLbl" alignment="0" max="32767" attributes="0"/>
<Component id="resultAccountsLbl" alignment="0" max="32767" attributes="0"/>
<Component id="resultCasesLbl" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="resultNameLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="resultNameField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="resultAliasesLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="aliasesListPane" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="resultAccountsLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="accountsTablePane" min="-2" pref="153" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="resultCasesLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="casesListPane" pref="118" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="resultNameLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.resultNameLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="resultNameField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.resultNameField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="resultNameFieldActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="resultAliasesLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.resultAliasesLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="resultAccountsLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.resultAccountsLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="accountsTablePane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="accountsTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
<Table columnCount="2" rowCount="4">
<Column editable="false" title="Type" type="java.lang.String">
<Data value="Email"/>
<Data value="Phone"/>
<Data value="Twitter"/>
<Data value="null"/>
</Column>
<Column editable="false" title="Data" type="java.lang.String">
<Data value="jdb@yahoo.com"/>
<Data value="865-555-5555"/>
<Data value="@jd93.bread"/>
<Data value="null"/>
</Column>
</Table>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="resultCasesLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/persona/Bundle.properties" key="PersonasTopComponent.resultCasesLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="casesListPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="casesList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="2">
<StringItem index="0" value="Investigation 13"/>
<StringItem index="1" value="Scene 5"/>
</StringArray>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="aliasesListPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="aliasesList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="3">
<StringItem index="0" value="J.D."/>
<StringItem index="1" value="Fred Smidge"/>
<StringItem index="2" value="Ethan Roseman"/>
</StringArray>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -65,6 +65,7 @@ import org.sleuthkit.datamodel.Pool;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.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());
}
}

View File

@ -57,6 +57,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.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;
}
}

View File

@ -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

View File

@ -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

View File

@ -38,7 +38,6 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent.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;

View File

@ -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>() {

View File

@ -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>