Merge branch 'develop' of github.com:sleuthkit/autopsy into 6459-Package-CASE-json-exporter

This commit is contained in:
U-BASIS\dsmyda 2020-07-08 11:09:42 -04:00
commit aa6d70060f
270 changed files with 13346 additions and 3474 deletions

View File

@ -54,6 +54,11 @@
<fileset dir="${thirdparty.dir}/7-Zip"/> <fileset dir="${thirdparty.dir}/7-Zip"/>
</copy> </copy>
<!--Copy InterestingFileSetRules to release-->
<copy todir="${basedir}/release/InterestingFileSetRules" >
<fileset dir="${thirdparty.dir}/InterestingFileSetRules"/>
</copy>
<!-- The 'libgstlibav.dll' file is too big to store on GitHub, so we <!-- The 'libgstlibav.dll' file is too big to store on GitHub, so we
have it stored in a ZIP file. We'll extract it in place and remove have it stored in a ZIP file. We'll extract it in place and remove
the ZIP file afterward. --> the ZIP file afterward. -->

View File

@ -2,7 +2,7 @@ Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.core/10 OpenIDE-Module: org.sleuthkit.autopsy.core/10
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties
OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml
OpenIDE-Module-Implementation-Version: 31 OpenIDE-Module-Implementation-Version: 32
OpenIDE-Module-Requires: org.openide.windows.WindowManager OpenIDE-Module-Requires: org.openide.windows.WindowManager
AutoUpdate-Show-In-Client: true AutoUpdate-Show-In-Client: true
AutoUpdate-Essential-Module: true AutoUpdate-Essential-Module: true

View File

@ -139,5 +139,5 @@ nbm.homepage=http://www.sleuthkit.org/
nbm.module.author=Brian Carrier nbm.module.author=Brian Carrier
nbm.needs.restart=true nbm.needs.restart=true
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
spec.version.base=10.19 spec.version.base=10.20

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2019 Basis Technology Corp. * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -139,7 +139,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
if (!tagNamesMap.isEmpty()) { if (!tagNamesMap.isEmpty()) {
for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) { for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) {
TagName tagName = entry.getValue(); TagName tagName = entry.getValue();
TagSet tagSet = tagName.getTagSet(); TagSet tagSet = tagsManager.getTagSet(tagName);
// Show custom tags before predefined tags in the menu // Show custom tags before predefined tags in the menu
if (tagSet != null) { if (tagSet != null) {

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -23,7 +23,6 @@ import java.awt.Window;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -36,7 +35,6 @@ import javax.swing.JComponent;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
@ -150,7 +148,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
} }
); );
try { try {
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager(); TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
List<String> standardTagNames = TagsManager.getStandardTagNames(); List<String> standardTagNames = TagsManager.getStandardTagNames();
Map<String, TagName> tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap()); Map<String, TagName> tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap());
@ -161,7 +159,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
tagNamesMap.entrySet().stream().map((entry) -> entry.getValue()).forEachOrdered((tagName) -> { tagNamesMap.entrySet().stream().map((entry) -> entry.getValue()).forEachOrdered((tagName) -> {
TagSet tagSet = null; TagSet tagSet = null;
try { try {
tagSet = tagName.getTagSet(); tagSet = tagsManager.getTagSet(tagName);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Logger.getLogger(GetTagNameAndCommentDialog.class Logger.getLogger(GetTagNameAndCommentDialog.class
.getName()).log(Level.SEVERE, "Failed to get tag set", ex); //NON-NLS .getName()).log(Level.SEVERE, "Failed to get tag set", ex); //NON-NLS

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -131,7 +131,7 @@ abstract class ReplaceTagAction<T extends Tag> extends AbstractAction implements
if (!tagNamesMap.isEmpty()) { if (!tagNamesMap.isEmpty()) {
for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) { for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) {
TagName tagName = entry.getValue(); TagName tagName = entry.getValue();
TagSet tagSet = tagName.getTagSet(); TagSet tagSet = tagsManager.getTagSet(tagName);
// Show custom tags before predefined tags in the menu // Show custom tags before predefined tags in the menu
if (tagSet != null) { if (tagSet != null) {

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2020 Basis Technology Corp. * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -56,7 +56,7 @@ public class TagsManager implements Closeable {
private final SleuthkitCase caseDb; private final SleuthkitCase caseDb;
private static String DEFAULT_TAG_SET_NAME = "Project VIC"; private static String DEFAULT_TAG_SET_NAME = "Project VIC";
private static final Object lock = new Object(); private static final Object lock = new Object();
static { static {
@ -235,16 +235,16 @@ public class TagsManager implements Closeable {
public static String getNotableTagDisplayName() { public static String getNotableTagDisplayName() {
return TagNameDefinition.getNotableTagDisplayName(); return TagNameDefinition.getNotableTagDisplayName();
} }
/** /**
* Creates a new TagSetDefinition file. * Creates a new TagSetDefinition file.
* *
* @param tagSetDef The tag set definition. * @param tagSetDef The tag set definition.
* *
* @throws IOException * @throws IOException
*/ */
public static void addTagSetDefinition(TagSetDefinition tagSetDef) throws IOException { public static void addTagSetDefinition(TagSetDefinition tagSetDef) throws IOException {
synchronized(lock) { synchronized (lock) {
TagSetDefinition.writeTagSetDefinition(tagSetDef); TagSetDefinition.writeTagSetDefinition(tagSetDef);
} }
} }
@ -267,20 +267,20 @@ public class TagsManager implements Closeable {
caseDb.addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus()); caseDb.addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus());
} }
//Assume new case and add tag sets //Assume new case and add tag sets
for(TagSetDefinition setDef: TagSetDefinition.readTagSetDefinitions()) { for (TagSetDefinition setDef : TagSetDefinition.readTagSetDefinitions()) {
List<TagName> tagNameList = new ArrayList<>(); List<TagName> tagNameList = new ArrayList<>();
for(TagNameDefinition tagNameDef: setDef.getTagNameDefinitions()) { for (TagNameDefinition tagNameDef : setDef.getTagNameDefinitions()) {
tagNameList.add(caseDb.addOrUpdateTagName(tagNameDef.getDisplayName(), tagNameDef.getDescription(), tagNameDef.getColor(), tagNameDef.getKnownStatus())); tagNameList.add(caseDb.addOrUpdateTagName(tagNameDef.getDisplayName(), tagNameDef.getDescription(), tagNameDef.getColor(), tagNameDef.getKnownStatus()));
} }
if(!tagNameList.isEmpty()) { if (!tagNameList.isEmpty()) {
taggingMgr.addTagSet(setDef.getName(), tagNameList); taggingMgr.addTagSet(setDef.getName(), tagNameList);
} }
} }
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error updating standard tag name and tag set definitions", ex); LOGGER.log(Level.SEVERE, "Error updating standard tag name and tag set definitions", ex);
} catch(IOException ex) { } catch (IOException ex) {
LOGGER.log(Level.SEVERE, "Error loading tag set JSON files", ex); LOGGER.log(Level.SEVERE, "Error loading tag set JSON files", ex);
} }
@ -288,28 +288,41 @@ public class TagsManager implements Closeable {
tagName.saveToCase(caseDb); tagName.saveToCase(caseDb);
} }
} }
/** /**
* Get a list of all tag sets currently in the case database. * Get a list of all tag sets currently in the case database.
* *
* @return A list, possibly empty, of TagSet objects. * @return A list, possibly empty, of TagSet objects.
* *
* @throws TskCoreException * @throws TskCoreException
*/ */
public List<TagSet> getAllTagSets() throws TskCoreException { public List<TagSet> getAllTagSets() throws TskCoreException {
return caseDb.getTaggingManager().getTagSets(); return caseDb.getTaggingManager().getTagSets();
} }
/**
* Gets the tag set a tag name (tag definition) belongs to, if any.
*
* @param tagName The tag name.
*
* @return A TagSet object or null.
*
* @throws TskCoreException If there is an error querying the case database.
*/
public TagSet getTagSet(TagName tagName) throws TskCoreException {
return caseDb.getTaggingManager().getTagSet(tagName);
}
/** /**
* Add a new TagSet to the case database. Tags will be ranked in the order * Add a new TagSet to the case database. Tags will be ranked in the order
* which they are passed to this method. * which they are passed to this method.
* *
* @param name Tag set name. * @param name Tag set name.
* @param tagNameList List of TagName in rank order. * @param tagNameList List of TagName in rank order.
* *
* @return A new TagSet object. * @return A new TagSet object.
* *
* @throws TskCoreException * @throws TskCoreException
*/ */
public TagSet addTagSet(String name, List<TagName> tagNameList) throws TskCoreException { public TagSet addTagSet(String name, List<TagName> tagNameList) throws TskCoreException {
return caseDb.getTaggingManager().addTagSet(name, tagNameList); return caseDb.getTaggingManager().addTagSet(name, tagNameList);
@ -501,7 +514,7 @@ public class TagsManager implements Closeable {
* name to the case database. * name to the case database.
*/ */
public TagName addTagName(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown knownStatus) throws TagNameAlreadyExistsException, TskCoreException { public TagName addTagName(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown knownStatus) throws TagNameAlreadyExistsException, TskCoreException {
synchronized(lock) { synchronized (lock) {
try { try {
TagName tagName = caseDb.addOrUpdateTagName(displayName, description, color, knownStatus); TagName tagName = caseDb.addOrUpdateTagName(displayName, description, color, knownStatus);
Set<TagNameDefinition> customTypes = TagNameDefinition.getTagNameDefinitions(); Set<TagNameDefinition> customTypes = TagNameDefinition.getTagNameDefinitions();

View File

@ -1,9 +1,9 @@
OpenIDE-Module-Name=Central Repository OpenIDE-Module-Name=Central Repository
OpenIDE-Module-Display-Category=Ingest Module OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Short-Description=Correlation Engine Ingest Module OpenIDE-Module-Short-Description=Central Repository Ingest Module
OpenIDE-Module-Long-Description=\ OpenIDE-Module-Long-Description=\
Correlation Engine ingest module and central database. \n\n\ Central Repository ingest module and central database. \n\n\
The Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\ The Central Repository ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\
Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest. Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest.
CentralRepoCommentDialog.commentLabel.text=Comment: CentralRepoCommentDialog.commentLabel.text=Comment:
CentralRepoCommentDialog.okButton.text=&OK CentralRepoCommentDialog.okButton.text=&OK

View File

@ -4,10 +4,10 @@ AddEditCentralRepoCommentAction.menuItemText.addEditCentralRepoCommentNoMD5=Add/
CentralRepoCommentDialog.title.addEditCentralRepoComment=Add/Edit Central Repository Comment CentralRepoCommentDialog.title.addEditCentralRepoComment=Add/Edit Central Repository Comment
OpenIDE-Module-Name=Central Repository OpenIDE-Module-Name=Central Repository
OpenIDE-Module-Display-Category=Ingest Module OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Short-Description=Correlation Engine Ingest Module OpenIDE-Module-Short-Description=Central Repository Ingest Module
OpenIDE-Module-Long-Description=\ OpenIDE-Module-Long-Description=\
Correlation Engine ingest module and central database. \n\n\ Central Repository ingest module and central database. \n\n\
The Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\ The Central Repository ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\
Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest. Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest.
CentralRepoCommentDialog.commentLabel.text=Comment: CentralRepoCommentDialog.commentLabel.text=Comment:
CentralRepoCommentDialog.okButton.text=&OK CentralRepoCommentDialog.okButton.text=&OK

View File

@ -28,7 +28,7 @@ import org.sleuthkit.datamodel.TskDataException;
/** /**
* *
* Stores information about a Data Source in the correlation engine * Stores information about a Data Source in the Central Repository
* *
*/ */
public class CorrelationDataSource implements Serializable { public class CorrelationDataSource implements Serializable {

View File

@ -234,7 +234,7 @@ public class Persona {
private static Persona createPersona(String name, String comment, PersonaStatus status) throws CentralRepoException { private static Persona createPersona(String name, String comment, PersonaStatus status) throws CentralRepoException {
// generate a UUID for the persona // generate a UUID for the persona
String uuidStr = UUID.randomUUID().toString(); String uuidStr = UUID.randomUUID().toString();
CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name")); CentralRepoExaminer examiner = getCRInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now(); Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli(); Long timeStampMillis = instant.toEpochMilli();
@ -248,22 +248,22 @@ public class Persona {
+ examiner.getId() + examiner.getId()
+ ")"; + ")";
CentralRepository.getInstance().executeInsertSQL(insertClause); getCRInstance().executeInsertSQL(insertClause);
return getPersonaByUUID(uuidStr); return getPersonaByUUID(uuidStr);
} }
/** /**
* Sets the comment of this persona. * Sets the comment of this persona.
* *
* @param name The new comment. * @param comment The new comment.
* *
* @throws CentralRepoException If there is an error. * @throws CentralRepoException If there is an error.
*/ */
public void setComment(String comment) throws CentralRepoException { public void setComment(String comment) throws CentralRepoException {
String updateClause = "UPDATE personas SET comment = \"" + comment + "\" WHERE id = " + id; String updateClause = "UPDATE personas SET comment = '" + comment + "' WHERE id = " + id;
CentralRepository cr = CentralRepository.getInstance(); CentralRepository cr = CentralRepository.getInstance();
if (cr != null) { if (cr != null) {
cr.executeUpdateSQL(updateClause); getCRInstance().executeUpdateSQL(updateClause);
} }
} }
@ -275,7 +275,7 @@ public class Persona {
* @throws CentralRepoException If there is an error. * @throws CentralRepoException If there is an error.
*/ */
public void setName(String name) throws CentralRepoException { public void setName(String name) throws CentralRepoException {
String updateClause = "UPDATE personas SET name = \"" + name + "\" WHERE id = " + id; String updateClause = "UPDATE personas SET name = '" + name + "' WHERE id = " + id;
CentralRepository cr = CentralRepository.getInstance(); CentralRepository cr = CentralRepository.getInstance();
if (cr != null) { if (cr != null) {
cr.executeUpdateSQL(updateClause); cr.executeUpdateSQL(updateClause);
@ -313,7 +313,9 @@ public class Persona {
/** /**
* Modifies the confidence / justification of the given PersonaAccount * Modifies the confidence / justification of the given PersonaAccount
* *
* @param account account to modify * @param account Account to modify.
* @param confidence Level of confidence.
* @param justification Justification.
* *
* @throws CentralRepoException If there is an error in querying the * @throws CentralRepoException If there is an error in querying the
* Personas table. * Personas table.
@ -329,7 +331,7 @@ public class Persona {
String deleteSQL = "UPDATE personas SET status_id = " + PersonaStatus.DELETED.status_id + " WHERE id = " + this.id; String deleteSQL = "UPDATE personas SET status_id = " + PersonaStatus.DELETED.status_id + " WHERE id = " + this.id;
CentralRepository cr = CentralRepository.getInstance(); CentralRepository cr = CentralRepository.getInstance();
if (cr != null) { if (cr != null) {
cr.executeDeleteSQL(deleteSQL); cr.executeUpdateSQL(deleteSQL);
} }
} }
@ -395,7 +397,7 @@ public class Persona {
+ "WHERE p.uuid = '" + uuid + "'"; + "WHERE p.uuid = '" + uuid + "'";
PersonaQueryCallback queryCallback = new PersonaQueryCallback(); PersonaQueryCallback queryCallback = new PersonaQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeSelectSQL(queryClause, queryCallback);
Collection<Persona> personas = queryCallback.getPersonas(); Collection<Persona> personas = queryCallback.getPersonas();
@ -420,7 +422,7 @@ public class Persona {
" AND LOWER(p.name) LIKE " + "LOWER('%" + partialName + "%')" ; " AND LOWER(p.name) LIKE " + "LOWER('%" + partialName + "%')" ;
PersonaQueryCallback queryCallback = new PersonaQueryCallback(); PersonaQueryCallback queryCallback = new PersonaQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonas(); return queryCallback.getPersonas();
} }
@ -485,7 +487,9 @@ public class Persona {
/** /**
* Modifies the given alias. * Modifies the given alias.
* *
* @param alias alias to modify * @param key Key for the alias to modify.
* @param confidence Level of confidence.
* @param justification Justification.
* *
* @throws CentralRepoException If there is an error in querying the * @throws CentralRepoException If there is an error in querying the
* Personas table. * Personas table.
@ -535,7 +539,9 @@ public class Persona {
/** /**
* Modifies the given metadata. * Modifies the given metadata.
* *
* @param metadata metadata to modify * @param key Key for the metadata to modify.
* @param confidence Level of confidence.
* @param justification Justification.
* *
* @throws CentralRepoException If there is an error in querying the * @throws CentralRepoException If there is an error in querying the
* Personas table. * Personas table.
@ -580,7 +586,7 @@ public class Persona {
while (resultSet.next()) { while (resultSet.next()) {
// get Case for case_id // get Case for case_id
CorrelationCase correlationCase = CentralRepository.getInstance().getCaseById(resultSet.getInt("case_id")); CorrelationCase correlationCase = getCRInstance().getCaseById(resultSet.getInt("case_id"));
correlationCases.add(correlationCase); correlationCases.add(correlationCase);
} }
} }
@ -605,14 +611,14 @@ public class Persona {
Collection<CentralRepoAccount> accounts = PersonaAccount.getAccountsForPersona(this.getId()); Collection<CentralRepoAccount> accounts = PersonaAccount.getAccountsForPersona(this.getId());
for (CentralRepoAccount account : accounts) { for (CentralRepoAccount account : accounts) {
int corrTypeId = account.getAccountType().getCorrelationTypeId(); int corrTypeId = account.getAccountType().getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId); CorrelationAttributeInstance.Type correlationType = getCRInstance().getCorrelationTypeById(corrTypeId);
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType); String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType);
String querySql = "SELECT DISTINCT case_id FROM " + tableName String querySql = "SELECT DISTINCT case_id FROM " + tableName
+ " WHERE account_id = " + account.getId(); + " WHERE account_id = " + account.getId();
CaseForAccountInstanceQueryCallback queryCallback = new CaseForAccountInstanceQueryCallback(); CaseForAccountInstanceQueryCallback queryCallback = new CaseForAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback); getCRInstance().executeSelectSQL(querySql, queryCallback);
// Add any cases that aren't already on the list. // Add any cases that aren't already on the list.
for (CorrelationCase corrCase : queryCallback.getCases()) { for (CorrelationCase corrCase : queryCallback.getCases()) {
@ -639,8 +645,8 @@ public class Persona {
while (resultSet.next()) { while (resultSet.next()) {
// get Case for case_id // get Case for case_id
CorrelationCase correlationCase = CentralRepository.getInstance().getCaseById(resultSet.getInt("case_id")); CorrelationCase correlationCase = getCRInstance().getCaseById(resultSet.getInt("case_id"));
CorrelationDataSource correlationDatasource = CentralRepository.getInstance().getDataSourceById(correlationCase, resultSet.getInt("data_source_id")); CorrelationDataSource correlationDatasource = getCRInstance().getDataSourceById(correlationCase, resultSet.getInt("data_source_id"));
// Add data source to list if not already on it. // Add data source to list if not already on it.
if (!correlationDataSources.stream().anyMatch(p -> Objects.equals(p.getDataSourceObjectID(), correlationDatasource.getDataSourceObjectID()))) { if (!correlationDataSources.stream().anyMatch(p -> Objects.equals(p.getDataSourceObjectID(), correlationDatasource.getDataSourceObjectID()))) {
@ -668,14 +674,14 @@ public class Persona {
Collection<CentralRepoAccount> accounts = PersonaAccount.getAccountsForPersona(this.getId()); Collection<CentralRepoAccount> accounts = PersonaAccount.getAccountsForPersona(this.getId());
for (CentralRepoAccount account : accounts) { for (CentralRepoAccount account : accounts) {
int corrTypeId = account.getAccountType().getCorrelationTypeId(); int corrTypeId = account.getAccountType().getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId); CorrelationAttributeInstance.Type correlationType = getCRInstance().getCorrelationTypeById(corrTypeId);
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType); String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType);
String querySql = "SELECT case_id, data_source_id FROM " + tableName String querySql = "SELECT case_id, data_source_id FROM " + tableName
+ " WHERE account_id = " + account.getId(); + " WHERE account_id = " + account.getId();
DatasourceForAccountInstanceQueryCallback queryCallback = new DatasourceForAccountInstanceQueryCallback(); DatasourceForAccountInstanceQueryCallback queryCallback = new DatasourceForAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback); getCRInstance().executeSelectSQL(querySql, queryCallback);
// Add any data sources that aren't already on the list. // Add any data sources that aren't already on the list.
for (CorrelationDataSource correlationDatasource : queryCallback.getDataSources()) { for (CorrelationDataSource correlationDatasource : queryCallback.getDataSources()) {
@ -738,7 +744,7 @@ public class Persona {
private static String getPersonaFromInstanceTableQueryTemplate(CentralRepoAccount.CentralRepoAccountType crAccountType) throws CentralRepoException { private static String getPersonaFromInstanceTableQueryTemplate(CentralRepoAccount.CentralRepoAccountType crAccountType) throws CentralRepoException {
int corrTypeId = crAccountType.getCorrelationTypeId(); int corrTypeId = crAccountType.getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId); CorrelationAttributeInstance.Type correlationType = getCRInstance().getCorrelationTypeById(corrTypeId);
String instanceTableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType); String instanceTableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType);
return "SELECT " + instanceTableName + ".account_id, case_id, data_source_id, " return "SELECT " + instanceTableName + ".account_id, case_id, data_source_id, "
@ -762,7 +768,7 @@ public class Persona {
public static Collection<Persona> getPersonasForCase(CorrelationCase correlationCase) throws CentralRepoException { public static Collection<Persona> getPersonasForCase(CorrelationCase correlationCase) throws CentralRepoException {
Collection<Persona> personaList = new ArrayList<>(); Collection<Persona> personaList = new ArrayList<>();
Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = CentralRepository.getInstance().getAllAccountTypes(); Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = getCRInstance().getAllAccountTypes();
for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) { for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) {
String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType) String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType)
@ -770,7 +776,7 @@ public class Persona {
+ "AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); + "AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback(); PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback); getCRInstance().executeSelectSQL(querySql, queryCallback);
// Add persona that aren't already on the list. // Add persona that aren't already on the list.
for (Persona persona : queryCallback.getPersonasList()) { for (Persona persona : queryCallback.getPersonasList()) {
@ -794,7 +800,7 @@ public class Persona {
public static Collection<Persona> getPersonasForDataSource(CorrelationDataSource dataSource) throws CentralRepoException { public static Collection<Persona> getPersonasForDataSource(CorrelationDataSource dataSource) throws CentralRepoException {
Collection<Persona> personaList = new ArrayList<>(); Collection<Persona> personaList = new ArrayList<>();
Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = CentralRepository.getInstance().getAllAccountTypes(); Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = getCRInstance().getAllAccountTypes();
for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) { for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) {
String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType) String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType)
@ -802,7 +808,7 @@ public class Persona {
+ "AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); + "AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback(); PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback); getCRInstance().executeSelectSQL(querySql, queryCallback);
// Add persona that aren't already on the list. // Add persona that aren't already on the list.
for (Persona persona : queryCallback.getPersonasList()) { for (Persona persona : queryCallback.getPersonasList()) {
@ -814,4 +820,23 @@ public class Persona {
} }
return personaList; return personaList;
} }
/**
* Wraps the call to CentralRepository.getInstance() throwing an
* exception if instance is null;
*
* @return Instance of CentralRepository
*
* @throws CentralRepoException
*/
private static CentralRepository getCRInstance() throws CentralRepoException {
CentralRepository instance = CentralRepository.getInstance();
if(instance == null) {
throw new CentralRepoException("Failed to get instance of CentralRespository, CR was null");
}
return instance;
}
} }

View File

@ -121,23 +121,18 @@ public class PersonaAccount {
/** /**
* Creates an account for the specified Persona. * Creates an account for the specified Persona.
* *
* @param persona Persona for which the account is being added. * @param persona Persona for which the account is being added.
* @param account Account. * @param account Account.
* @param justification Reason for assigning the alias, may be null. * @param justification Reason for assigning the alias, may be null.
* @param confidence Confidence level. * @param confidence Confidence level.
* *
* @return PersonaAccount * @return PersonaAccount
*
* @throws CentralRepoException If there is an error in creating the * @throws CentralRepoException If there is an error in creating the
* account. * account.
*/ */
static PersonaAccount addPersonaAccount(Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException { static PersonaAccount addPersonaAccount(Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance(); CentralRepoExaminer currentExaminer = getCRInstance().getOrInsertExaminer(System.getProperty("user.name"));
if(cr == null) {
throw new CentralRepoException("Failed to add Persona, Central Repository is not enable");
}
CentralRepoExaminer currentExaminer = cr.getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now(); Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli(); Long timeStampMillis = instant.toEpochMilli();
@ -151,14 +146,14 @@ public class PersonaAccount {
+ currentExaminer.getId() + currentExaminer.getId()
+ ")"; + ")";
CentralRepository.getInstance().executeInsertSQL(insertClause); getCRInstance().executeInsertSQL(insertClause);
String queryClause = PERSONA_ACCOUNTS_QUERY_CLAUSE String queryClause = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ "WHERE persona_id = " + persona.getId() + "WHERE persona_id = " + persona.getId()
+ " AND account_type_id = " + account.getAccountType().getAccountTypeId() + " AND account_type_id = " + account.getAccountType().getAccountTypeId()
+ " AND account_unique_identifier = \"" + account.getIdentifier() + "\""; + " AND account_unique_identifier = '" + account.getIdentifier() + "'";
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeSelectSQL(queryClause, queryCallback);
Collection<PersonaAccount> accounts = queryCallback.getPersonaAccountsList(); Collection<PersonaAccount> accounts = queryCallback.getPersonaAccountsList();
if (accounts.size() != 1) { if (accounts.size() != 1) {
@ -203,7 +198,7 @@ public class PersonaAccount {
); );
// create account // create account
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(rs.getString("type_name")); CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
CentralRepoAccount account = new CentralRepoAccount( CentralRepoAccount account = new CentralRepoAccount(
rs.getInt("account_id"), rs.getInt("account_id"),
crAccountType, crAccountType,
@ -249,19 +244,13 @@ public class PersonaAccount {
* persona_account. * persona_account.
*/ */
static Collection<PersonaAccount> getPersonaAccountsForPersona(long personaId) throws CentralRepoException { static Collection<PersonaAccount> getPersonaAccountsForPersona(long personaId) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance(); String queryClause = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ " WHERE persona_accounts.persona_id = " + personaId;
if (cr != null) { PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
String queryClause = PERSONA_ACCOUNTS_QUERY_CLAUSE getCRInstance().executeSelectSQL(queryClause, queryCallback);
+ " WHERE persona_accounts.persona_id = " + personaId;
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); return queryCallback.getPersonaAccountsList();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
}
return new ArrayList<>();
} }
/** /**
@ -279,16 +268,9 @@ public class PersonaAccount {
+ " WHERE persona_accounts.account_id = " + accountId + " WHERE persona_accounts.account_id = " + accountId
+ " AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); + " AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
CentralRepository cr = CentralRepository.getInstance(); PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
getCRInstance().executeSelectSQL(queryClause, queryCallback);
if (cr != null) { return queryCallback.getPersonaAccountsList();
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
}
return new ArrayList<>();
} }
/** /**
@ -308,15 +290,10 @@ public class PersonaAccount {
+ " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')" + " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')"
+ " AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); + " AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
CentralRepository cr = CentralRepository.getInstance(); PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
if (cr != null) { getCRInstance().executeSelectSQL(queryClause, queryCallback);
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); return queryCallback.getPersonaAccountsList();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
}
return new ArrayList<>();
} }
/** /**
@ -335,14 +312,9 @@ public class PersonaAccount {
+ " AND type_name = '" + account.getAccountType().getTypeName() + "' " + " AND type_name = '" + account.getAccountType().getTypeName() + "' "
+ " AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); + " AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
CentralRepository cr = CentralRepository.getInstance(); PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
if (cr != null) { getCRInstance().executeSelectSQL(queryClause, queryCallback);
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); return queryCallback.getPersonaAccountsList();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
}
return new ArrayList<>();
} }
/** /**
@ -351,36 +323,24 @@ public class PersonaAccount {
* @param id row id for the account to be removed * @param id row id for the account to be removed
* *
* @throws CentralRepoException If there is an error in removing the * @throws CentralRepoException If there is an error in removing the
* account. * account.
*/ */
static void removePersonaAccount(long id) throws CentralRepoException { static void removePersonaAccount(long id) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance();
if(cr == null) {
throw new CentralRepoException("Failed to remove persona account, Central Repo is not enabled");
}
String deleteClause = " DELETE FROM persona_accounts WHERE id = " + id; String deleteClause = " DELETE FROM persona_accounts WHERE id = " + id;
cr.executeDeleteSQL(deleteClause); getCRInstance().executeDeleteSQL(deleteClause);
} }
/** /**
* Modifies the PersonaAccount row by the given id * Modifies the PersonaAccount row by the given id
* *
* @param id row id for the account to be removed * @param id row id for the account to be removed
* *
* @throws CentralRepoException If there is an error in removing the * @throws CentralRepoException If there is an error in removing the
* account. * account.
*/ */
static void modifyPersonaAccount(long id, Persona.Confidence confidence, String justification) throws CentralRepoException { static void modifyPersonaAccount(long id, Persona.Confidence confidence, String justification) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance(); String updateClause = "UPDATE persona_accounts SET confidence_id = " + confidence.getLevelId() + ", justification = '" + justification + "' WHERE id = " + id;
getCRInstance().executeUpdateSQL(updateClause);
if (cr == null) {
throw new CentralRepoException("Failed to modify persona account, Central Repo is not enabled");
}
String updateClause = "UPDATE persona_accounts SET confidence_id = " + confidence.getLevelId() + ", justification = \"" + justification + "\" WHERE id = " + id;
cr.executeUpdateSQL(updateClause);
} }
/** /**
@ -397,7 +357,7 @@ public class PersonaAccount {
while (rs.next()) { while (rs.next()) {
// create account // create account
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(rs.getString("type_name")); CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
CentralRepoAccount account = new CentralRepoAccount( CentralRepoAccount account = new CentralRepoAccount(
rs.getInt("account_id"), rs.getInt("account_id"),
crAccountType, crAccountType,
@ -418,28 +378,41 @@ public class PersonaAccount {
* @param personaId Id of the persona to look for. * @param personaId Id of the persona to look for.
* *
* @return Collection of all accounts associated with the given persona, may * @return Collection of all accounts associated with the given persona, may
* be empty. * be empty.
*
* @throws CentralRepoException If there is an error in getting the * @throws CentralRepoException If there is an error in getting the
* accounts. * accounts.
*/ */
static Collection<CentralRepoAccount> getAccountsForPersona(long personaId) throws CentralRepoException { static Collection<CentralRepoAccount> getAccountsForPersona(long personaId) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance(); String queryClause = "SELECT account_id, "
+ " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier,"
+ " account_types.type_name as type_name "
+ " FROM persona_accounts "
+ " JOIN accounts as accounts on persona_accounts.account_id = accounts.id "
+ " JOIN account_types as account_types on accounts.account_type_id = account_types.id "
+ " WHERE persona_accounts.persona_id = " + personaId;
if (cr != null) { AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback();
String queryClause = "SELECT account_id, " getCRInstance().executeSelectSQL(queryClause, queryCallback);
+ " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier,"
+ " account_types.type_name as type_name "
+ " FROM persona_accounts "
+ " JOIN accounts as accounts on persona_accounts.account_id = accounts.id "
+ " JOIN account_types as account_types on accounts.account_type_id = account_types.id "
+ " WHERE persona_accounts.persona_id = " + personaId;
AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback(); return queryCallback.getAccountsList();
cr.executeSelectSQL(queryClause, queryCallback); }
return queryCallback.getAccountsList(); /**
* Wraps the call to CentralRepository.getInstance() throwing an exception
* if instance is null;
*
* @return Instance of CentralRepository
*
* @throws CentralRepoException
*/
private static CentralRepository getCRInstance() throws CentralRepoException {
CentralRepository instance = CentralRepository.getInstance();
if (instance == null) {
throw new CentralRepoException("Failed to get instance of CentralRespository, CR was null");
} }
return new ArrayList<>(); return instance;
} }
} }

View File

@ -98,7 +98,7 @@ public class PersonaAlias {
*/ */
static PersonaAlias addPersonaAlias(Persona persona, String alias, String justification, Persona.Confidence confidence) throws CentralRepoException { static PersonaAlias addPersonaAlias(Persona persona, String alias, String justification, Persona.Confidence confidence) throws CentralRepoException {
CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name")); CentralRepoExaminer examiner = getCRInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now(); Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli(); Long timeStampMillis = instant.toEpochMilli();
@ -113,16 +113,16 @@ public class PersonaAlias {
+ examiner.getId() + examiner.getId()
+ ")"; + ")";
CentralRepository.getInstance().executeInsertSQL(insertClause); getCRInstance().executeInsertSQL(insertClause);
String queryClause = SELECT_QUERY_BASE String queryClause = SELECT_QUERY_BASE
+ "WHERE pa.persona_id = " + persona.getId() + "WHERE pa.persona_id = " + persona.getId()
+ " AND pa.alias = \"" + alias + "\"" + " AND pa.alias = '" + alias + "'"
+ " AND pa.date_added = " + timeStampMillis + " AND pa.date_added = " + timeStampMillis
+ " AND pa.examiner_id = " + examiner.getId(); + " AND pa.examiner_id = " + examiner.getId();
PersonaAliasesQueryCallback queryCallback = new PersonaAliasesQueryCallback(); PersonaAliasesQueryCallback queryCallback = new PersonaAliasesQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeSelectSQL(queryClause, queryCallback);
Collection<PersonaAlias> aliases = queryCallback.getAliases(); Collection<PersonaAlias> aliases = queryCallback.getAliases();
if (aliases.size() != 1) { if (aliases.size() != 1) {
@ -141,7 +141,7 @@ public class PersonaAlias {
*/ */
static void removePersonaAlias(PersonaAlias alias) throws CentralRepoException { static void removePersonaAlias(PersonaAlias alias) throws CentralRepoException {
String deleteClause = " DELETE FROM persona_alias WHERE id = " + alias.getId(); String deleteClause = " DELETE FROM persona_alias WHERE id = " + alias.getId();
CentralRepository.getInstance().executeDeleteSQL(deleteClause); getCRInstance().executeDeleteSQL(deleteClause);
} }
/** /**
@ -158,7 +158,7 @@ public class PersonaAlias {
throw new CentralRepoException("Failed to modify persona alias, Central Repo is not enabled"); throw new CentralRepoException("Failed to modify persona alias, Central Repo is not enabled");
} }
String updateClause = "UPDATE persona_alias SET confidence_id = " + confidence.getLevelId() + ", justification = \"" + justification + "\" WHERE id = " + alias.id; String updateClause = "UPDATE persona_alias SET confidence_id = " + confidence.getLevelId() + ", justification = '" + justification + "' WHERE id = " + alias.id;
cr.executeUpdateSQL(updateClause); cr.executeUpdateSQL(updateClause);
} }
@ -208,9 +208,26 @@ public class PersonaAlias {
String queryClause = SELECT_QUERY_BASE + "WHERE pa.persona_id = " + personaId; String queryClause = SELECT_QUERY_BASE + "WHERE pa.persona_id = " + personaId;
PersonaAliasesQueryCallback queryCallback = new PersonaAliasesQueryCallback(); PersonaAliasesQueryCallback queryCallback = new PersonaAliasesQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeSelectSQL(queryClause, queryCallback);
return queryCallback.getAliases(); return queryCallback.getAliases();
} }
/**
* Wraps the call to CentralRepository.getInstance() throwing an
* exception if instance is null;
*
* @return Instance of CentralRepository
*
* @throws CentralRepoException
*/
private static CentralRepository getCRInstance() throws CentralRepoException {
CentralRepository instance = CentralRepository.getInstance();
if(instance == null) {
throw new CentralRepoException("Failed to get instance of CentralRespository, CR was null");
}
return instance;
}
} }

View File

@ -107,7 +107,7 @@ public class PersonaMetadata {
*/ */
static PersonaMetadata addPersonaMetadata(long personaId, String name, String value, String justification, Persona.Confidence confidence) throws CentralRepoException { static PersonaMetadata addPersonaMetadata(long personaId, String name, String value, String justification, Persona.Confidence confidence) throws CentralRepoException {
CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name")); CentralRepoExaminer examiner = getCRInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now(); Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli(); Long timeStampMillis = instant.toEpochMilli();
@ -123,17 +123,17 @@ public class PersonaMetadata {
+ examiner.getId() + examiner.getId()
+ ")"; + ")";
CentralRepository.getInstance().executeInsertSQL(insertClause); getCRInstance().executeInsertSQL(insertClause);
String queryClause = SELECT_QUERY_BASE String queryClause = SELECT_QUERY_BASE
+ "WHERE pmd.persona_id = " + personaId + "WHERE pmd.persona_id = " + personaId
+ " AND pmd.name = \"" + name + "\"" + " AND pmd.name = '" + name + "'"
+ " AND pmd.value = \"" + value + "\"" + " AND pmd.value = '" + value + "'"
+ " AND pmd.date_added = " + timeStampMillis + " AND pmd.date_added = " + timeStampMillis
+ " AND pmd.examiner_id = " + examiner.getId(); + " AND pmd.examiner_id = " + examiner.getId();
PersonaMetadataQueryCallback queryCallback = new PersonaMetadataQueryCallback(); PersonaMetadataQueryCallback queryCallback = new PersonaMetadataQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeSelectSQL(queryClause, queryCallback);
Collection<PersonaMetadata> metadata = queryCallback.getMetadataList(); Collection<PersonaMetadata> metadata = queryCallback.getMetadataList();
if (metadata.size() != 1) { if (metadata.size() != 1) {
@ -152,7 +152,7 @@ public class PersonaMetadata {
*/ */
static void removePersonaMetadata(PersonaMetadata metadata) throws CentralRepoException { static void removePersonaMetadata(PersonaMetadata metadata) throws CentralRepoException {
String deleteClause = " DELETE FROM persona_metadata WHERE id = " + metadata.getId(); String deleteClause = " DELETE FROM persona_metadata WHERE id = " + metadata.getId();
CentralRepository.getInstance().executeDeleteSQL(deleteClause); getCRInstance().executeDeleteSQL(deleteClause);
} }
/** /**
@ -169,7 +169,7 @@ public class PersonaMetadata {
throw new CentralRepoException("Failed to modify persona metadata, Central Repo is not enabled"); throw new CentralRepoException("Failed to modify persona metadata, Central Repo is not enabled");
} }
String updateClause = "UPDATE persona_metadata SET confidence_id = " + confidence.getLevelId() + ", justification = \"" + justification + "\" WHERE id = " + metadata.id; String updateClause = "UPDATE persona_metadata SET confidence_id = " + confidence.getLevelId() + ", justification = '" + justification + "' WHERE id = " + metadata.id;
cr.executeUpdateSQL(updateClause); cr.executeUpdateSQL(updateClause);
} }
@ -219,10 +219,27 @@ public class PersonaMetadata {
String queryClause = SELECT_QUERY_BASE + "WHERE pmd.persona_id = " + personaId; String queryClause = SELECT_QUERY_BASE + "WHERE pmd.persona_id = " + personaId;
PersonaMetadataQueryCallback queryCallback = new PersonaMetadataQueryCallback(); PersonaMetadataQueryCallback queryCallback = new PersonaMetadataQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); getCRInstance().executeSelectSQL(queryClause, queryCallback);
return queryCallback.getMetadataList(); return queryCallback.getMetadataList();
} }
/**
* Wraps the call to CentralRepository.getInstance() throwing an
* exception if instance is null;
*
* @return Instance of CentralRepository
*
* @throws CentralRepoException
*/
private static CentralRepository getCRInstance() throws CentralRepoException {
CentralRepository instance = CentralRepository.getInstance();
if(instance == null) {
throw new CentralRepoException("Failed to get instance of CentralRespository, CR was null");
}
return instance;
}
} }

View File

@ -1679,7 +1679,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
bulkArtifacts.get(tableName).clear(); bulkArtifacts.get(tableName).clear();
} }
TimingMetric timingMetric = HealthMonitor.getTimingMetric("Correlation Engine: Bulk insert"); TimingMetric timingMetric = HealthMonitor.getTimingMetric("Central Repository: Bulk insert");
HealthMonitor.submitTimingMetric(timingMetric); HealthMonitor.submitTimingMetric(timingMetric);
// Reset state // Reset state

View File

@ -1,5 +1,5 @@
caseeventlistener.evidencetag=Evidence caseeventlistener.evidencetag=Evidence
IngestEventsListener.ingestmodule.name=Correlation Engine IngestEventsListener.ingestmodule.name=Central Repository
IngestEventsListener.prevCaseComment.text=Previous Case: IngestEventsListener.prevCaseComment.text=Previous Case:
# {0} - typeName # {0} - typeName
# {1} - count # {1} - count

View File

@ -73,7 +73,7 @@ import org.sleuthkit.datamodel.CommunicationsUtils;
* Listen for ingest events and update entries in the Central Repository * Listen for ingest events and update entries in the Central Repository
* database accordingly * database accordingly
*/ */
@NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Correlation Engine"}) @NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Central Repository"})
public class IngestEventsListener { public class IngestEventsListener {
private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName()); private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName());
@ -116,24 +116,24 @@ public class IngestEventsListener {
/** /**
* Increase the number of IngestEventsListeners adding contents to the * Increase the number of IngestEventsListeners adding contents to the
* Correlation Engine. * Central Repository.
*/ */
public synchronized static void incrementCorrelationEngineModuleCount() { public synchronized static void incrementCorrelationEngineModuleCount() {
correlationModuleInstanceCount++; //Should be called once in the Correlation Engine module's startup method. correlationModuleInstanceCount++; //Should be called once in the Central Repository module's startup method.
} }
/** /**
* Decrease the number of IngestEventsListeners adding contents to the * Decrease the number of IngestEventsListeners adding contents to the
* Correlation Engine. * Central Repository.
*/ */
public synchronized static void decrementCorrelationEngineModuleCount() { public synchronized static void decrementCorrelationEngineModuleCount() {
if (getCeModuleInstanceCount() > 0) { //prevent it ingestJobCounter from going negative if (getCeModuleInstanceCount() > 0) { //prevent it ingestJobCounter from going negative
correlationModuleInstanceCount--; //Should be called once in the Correlation Engine module's shutdown method. correlationModuleInstanceCount--; //Should be called once in the Central Repository module's shutdown method.
} }
} }
/** /**
* Reset the counter which keeps track of if the Correlation Engine Module * Reset the counter which keeps track of if the Central Repository Module
* is being run during injest to 0. * is being run during injest to 0.
*/ */
synchronized static void resetCeModuleInstanceCount() { synchronized static void resetCeModuleInstanceCount() {
@ -141,10 +141,10 @@ public class IngestEventsListener {
} }
/** /**
* Whether or not the Correlation Engine Module is enabled for any of the * Whether or not the Central Repository Module is enabled for any of the
* currently running ingest jobs. * currently running ingest jobs.
* *
* @return boolean True for Correlation Engine enabled, False for disabled * @return boolean True for Central Repository enabled, False for disabled
*/ */
public synchronized static int getCeModuleInstanceCount() { public synchronized static int getCeModuleInstanceCount() {
return correlationModuleInstanceCount; return correlationModuleInstanceCount;
@ -282,7 +282,7 @@ public class IngestEventsListener {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
//if ingest is running we want there to check if there is a Correlation Engine module running //if ingest is running we want there to check if there is a Central Repository module running
//sometimes artifacts are generated by DSPs or other sources while ingest is not running //sometimes artifacts are generated by DSPs or other sources while ingest is not running
//in these cases we still want to create correlation attributesForNewArtifact for those artifacts when appropriate //in these cases we still want to create correlation attributesForNewArtifact for those artifacts when appropriate
if (!IngestManager.getInstance().isIngestRunning() || getCeModuleInstanceCount() > 0) { if (!IngestManager.getInstance().isIngestRunning() || getCeModuleInstanceCount() > 0) {
@ -349,7 +349,7 @@ public class IngestEventsListener {
if (getCeModuleInstanceCount() == 0) { if (getCeModuleInstanceCount() == 0) {
recentlyAddedCeArtifacts.clear(); recentlyAddedCeArtifacts.clear();
} }
//else another instance of the Correlation Engine Module is still being run. //else another instance of the Central Repository Module is still being run.
/* /*
* Ensure the data source in the Central Repository has hash values * Ensure the data source in the Central Repository has hash values

View File

@ -1,6 +1,6 @@
CentralRepoIngestModel_name_header=Name:<br> CentralRepoIngestModel_name_header=Name:<br>
CentralRepoIngestModel_previous_case_header=<br>Previous Cases:<br> CentralRepoIngestModel_previous_case_header=<br>Previous Cases:<br>
CentralRepoIngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Correlation Engine ingest module. CentralRepoIngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Central Repository ingest module.
CentralRepoIngestModule.notfyBubble.title=Central Repository Not Initialized CentralRepoIngestModule.notfyBubble.title=Central Repository Not Initialized
CentralRepoIngestModule.prevCaseComment.text=Previous Case: CentralRepoIngestModule.prevCaseComment.text=Previous Case:
CentralRepoIngestModule.prevTaggedSet.text=Previously Tagged As Notable (Central Repository) CentralRepoIngestModule.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)
@ -8,7 +8,7 @@ CentralRepoIngestModule_notable_message_header=<html>A file in this data source
# {0} - Name of file that is Notable # {0} - Name of file that is Notable
CentralRepoIngestModule_postToBB_knownBadMsg=Notable: {0} CentralRepoIngestModule_postToBB_knownBadMsg=Notable: {0}
CentralRepoIngestModuleFactory.ingestmodule.desc=Saves properties to the central repository for later correlation CentralRepoIngestModuleFactory.ingestmodule.desc=Saves properties to the central repository for later correlation
CentralRepoIngestModuleFactory.ingestmodule.name=Correlation Engine CentralRepoIngestModuleFactory.ingestmodule.name=Central Repository
IngestSettingsPanel.ingestSettingsLabel.text=Ingest Settings IngestSettingsPanel.ingestSettingsLabel.text=Ingest Settings
IngestSettingsPanel.flagTaggedNotableItemsCheckbox.text=Flag items previously tagged as notable IngestSettingsPanel.flagTaggedNotableItemsCheckbox.text=Flag items previously tagged as notable
IngestSettingsPanel.flagPreviouslySeenDevicesCheckbox.text=Flag devices previously seen in other cases IngestSettingsPanel.flagPreviouslySeenDevicesCheckbox.text=Flag devices previously seen in other cases

View File

@ -85,7 +85,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
private final boolean createCorrelationProperties; private final boolean createCorrelationProperties;
/** /**
* Instantiate the Correlation Engine ingest module. * Instantiate the Central Repository ingest module.
* *
* @param settings The ingest settings for the module instance. * @param settings The ingest settings for the module instance.
*/ */
@ -147,7 +147,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
*/ */
if (abstractFile.getKnown() != TskData.FileKnown.KNOWN && flagTaggedNotableItems) { if (abstractFile.getKnown() != TskData.FileKnown.KNOWN && flagTaggedNotableItems) {
try { try {
TimingMetric timingMetric = HealthMonitor.getTimingMetric("Correlation Engine: Notable artifact query"); TimingMetric timingMetric = HealthMonitor.getTimingMetric("Central Repository: Notable artifact query");
List<String> caseDisplayNamesList = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5); List<String> caseDisplayNamesList = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5);
HealthMonitor.submitTimingMetric(timingMetric); HealthMonitor.submitTimingMetric(timingMetric);
if (!caseDisplayNamesList.isEmpty()) { if (!caseDisplayNamesList.isEmpty()) {
@ -220,7 +220,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
// see ArtifactManagerTimeTester for details // see ArtifactManagerTimeTester for details
@Messages({ @Messages({
"CentralRepoIngestModule.notfyBubble.title=Central Repository Not Initialized", "CentralRepoIngestModule.notfyBubble.title=Central Repository Not Initialized",
"CentralRepoIngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Correlation Engine ingest module." "CentralRepoIngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Central Repository ingest module."
}) })
@Override @Override
public void startUp(IngestJobContext context) throws IngestModuleException { public void startUp(IngestJobContext context) throws IngestModuleException {
@ -235,7 +235,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
* posited. * posited.
* *
* Note: Flagging cannot be disabled if any other instances of the * Note: Flagging cannot be disabled if any other instances of the
* Correlation Engine module are running. This restriction is to prevent * Central Repository module are running. This restriction is to prevent
* missing results in the case where the first module is flagging * missing results in the case where the first module is flagging
* notable items, and the proceeding module (with flagging disabled) * notable items, and the proceeding module (with flagging disabled)
* causes the first to stop flagging. * causes the first to stop flagging.
@ -276,7 +276,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
// Don't allow sqlite central repo databases to be used for multi user cases // Don't allow sqlite central repo databases to be used for multi user cases
if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE)
&& (CentralRepoDbManager.getSavedDbChoice().getDbPlatform() == CentralRepoPlatforms.SQLITE)) { && (CentralRepoDbManager.getSavedDbChoice().getDbPlatform() == CentralRepoPlatforms.SQLITE)) {
logger.log(Level.SEVERE, "Cannot run correlation engine on a multi-user case with a SQLite central repository."); logger.log(Level.SEVERE, "Cannot run Central Repository ingest module on a multi-user case with a SQLite central repository.");
throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS
} }
jobId = context.getJobId(); jobId = context.getJobId();

View File

@ -33,7 +33,7 @@ import org.sleuthkit.autopsy.ingest.NoIngestModuleIngestJobSettings;
* Factory for Central Repository ingest modules * Factory for Central Repository ingest modules
*/ */
@ServiceProvider(service = org.sleuthkit.autopsy.ingest.IngestModuleFactory.class) @ServiceProvider(service = org.sleuthkit.autopsy.ingest.IngestModuleFactory.class)
@NbBundle.Messages({"CentralRepoIngestModuleFactory.ingestmodule.name=Correlation Engine", @NbBundle.Messages({"CentralRepoIngestModuleFactory.ingestmodule.name=Central Repository",
"CentralRepoIngestModuleFactory.ingestmodule.desc=Saves properties to the central repository for later correlation"}) "CentralRepoIngestModuleFactory.ingestmodule.desc=Saves properties to the central repository for later correlation"})
public class CentralRepoIngestModuleFactory extends IngestModuleFactoryAdapter { public class CentralRepoIngestModuleFactory extends IngestModuleFactoryAdapter {

View File

@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.centralrepository.ingestmodule;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
/** /**
* Ingest job settings for the Correlation Engine module. * Ingest job settings for the Central Repository module.
*/ */
final class IngestSettings implements IngestModuleIngestJobSettings { final class IngestSettings implements IngestModuleIngestJobSettings {

View File

@ -22,7 +22,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
/** /**
* Ingest job settings panel for the Correlation Engine module. * Ingest job settings panel for the Central Repository module.
*/ */
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel {

View File

@ -1,15 +1,6 @@
CTL_OpenPersonaManager=Persona Manager CTL_OpenPersonas=Personas
CTL_PersonaManagerTopComponentAction=Persona Manager CTL_PersonasTopComponentAction=Personas
CTL_PersonaDetailsTopComponent=Persona Details CTL_PersonaDetailsTopComponent=Persona Details
PersonaManagerTopComponent.createBtn.text=New Persona
PersonaManagerTopComponent.searchBtn.text=Search
PersonaManagerTopComponent.resultsTable.columnModel.title1=Name
PersonaManagerTopComponent.resultsTable.columnModel.title0=ID
PersonaManagerTopComponent.resultsTable.toolTipText=
PersonaManagerTopComponent.searchAccountRadio.text=Account
PersonaManagerTopComponent.searchNameRadio.text=Name
PersonaManagerTopComponent.searchField.text=
PersonaManagerTopComponent.editBtn.text=Edit Persona
PersonaDetailsDialog.cancelBtn.text=Cancel PersonaDetailsDialog.cancelBtn.text=Cancel
PersonaDetailsDialog.okBtn.text=OK PersonaDetailsDialog.okBtn.text=OK
PersonaDetailsPanel.casesLbl.text=Cases found in: PersonaDetailsPanel.casesLbl.text=Cases found in:
@ -27,7 +18,6 @@ PersonaDetailsPanel.nameLbl.text=Name:
AddAliasDialog.accountsLbl.text=Account: AddAliasDialog.accountsLbl.text=Account:
AddAliasDialog.okBtn.text=OK AddAliasDialog.okBtn.text=OK
AddAliasDialog.cancelBtn.text=Cancel AddAliasDialog.cancelBtn.text=Cancel
PersonaManagerTopComponent.deleteBtn.text=Delete Persona
PersonaDetailsPanel.casesLbl.text=Cases found in: PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add PersonaDetailsPanel.addAliasBtn.text=Add
@ -73,3 +63,20 @@ PersonaMetadataDialog.cancelBtn.text=Cancel
PersonaDetailsPanel.editAccountBtn.text=Edit PersonaDetailsPanel.editAccountBtn.text=Edit
PersonaDetailsPanel.editMetadataBtn.text=Edit PersonaDetailsPanel.editMetadataBtn.text=Edit
PersonaDetailsPanel.editAliasBtn.text=Edit PersonaDetailsPanel.editAliasBtn.text=Edit
PersonasTopComponent.searchAccountRadio.text=Account
PersonasTopComponent.searchNameRadio.text=Name
PersonasTopComponent.searchField.text=
PersonasTopComponent.deleteBtn.text=Delete Persona
PersonasTopComponent.editBtn.text=Edit Persona
PersonasTopComponent.createBtn.text=New Persona
PersonasTopComponent.createAccountBtn.text=Create Account
PersonasTopComponent.searchBtn.text=Search
PersonasTopComponent.resultsTable.columnModel.title1=Name
PersonasTopComponent.resultsTable.columnModel.title0=ID
PersonasTopComponent.resultsTable.toolTipText=
CreatePersonaAccountDialog.cancelBtn.text=Cancel
CreatePersonaAccountDialog.typeLbl.text=Type:
CreatePersonaAccountDialog.identifierTextField.text=
CreatePersonaAccountDialog.identiferLbl.text=Identifier:
CreatePersonaAccountDialog.okBtn.text=OK
PersonasTopComponent.introText.text=Personas represent an online identity. They span cases and are stored in the Central Repository based on accounts that were found in artifacts. You can create, edit, and delete personas here.

View File

@ -3,10 +3,15 @@ AddMetadataDialog_dup_msg=A metadata entry with this name has already been added
AddMetadataDialog_dup_Title=Metadata add failure AddMetadataDialog_dup_Title=Metadata add failure
AddMetadataDialog_empty_name_msg=A metadata entry cannot have an empty name or value. AddMetadataDialog_empty_name_msg=A metadata entry cannot have an empty name or value.
AddMetadataDialog_empty_name_Title=Missing field(s) AddMetadataDialog_empty_name_Title=Missing field(s)
CTL_OpenPersonaManager=Persona Manager CreatePersonaAccountDialog.title.text=Create Account
CTL_PersonaManagerTopComponentAction=Persona Manager CreatePersonaAccountDialog_error_msg=Failed to create account.
CreatePersonaAccountDialog_error_title=Account failure
CreatePersonaAccountDialog_success_msg=Account added.
CreatePersonaAccountDialog_success_title=Account added
CTL_OpenPersonas=Personas
CTL_PersonasTopComponentAction=Personas
CTL_PersonaDetailsTopComponent=Persona Details CTL_PersonaDetailsTopComponent=Persona Details
OpenPersonasAction.displayName=Persona Manager OpenPersonasAction.displayName=Personas
PersonaAccountDialog.title.text=Add Account PersonaAccountDialog.title.text=Add Account
PersonaAccountDialog_dup_msg=This account is already added to the persona. PersonaAccountDialog_dup_msg=This account is already added to the persona.
PersonaAccountDialog_dup_Title=Account add failure PersonaAccountDialog_dup_Title=Account add failure
@ -23,32 +28,11 @@ PersonaAliasDialog_dup_msg=This alias has already been added to this persona.
PersonaAliasDialog_dup_Title=Alias add failure PersonaAliasDialog_dup_Title=Alias add failure
PersonaAliasDialog_empty_msg=An alias cannot be empty. PersonaAliasDialog_empty_msg=An alias cannot be empty.
PersonaAliasDialog_empty_Title=Empty alias PersonaAliasDialog_empty_Title=Empty alias
PersonaDetailsDialog.cancelBtn.text=Cancel
PersonaDetailsDialog.okBtn.text=OK
PersonaDetailsDialogCreateTitle=Create Persona PersonaDetailsDialogCreateTitle=Create Persona
PersonaDetailsDialogEditTitle=Edit Persona PersonaDetailsDialogEditTitle=Edit Persona
PersonaDetailsDialogViewTitle=View Persona PersonaDetailsDialogViewTitle=View Persona
PersonaDetailsPanel_CentralRepoErr_msg=Failure to write to Central Repository.
PersonaDetailsPanel_CentralRepoErr_Title=Central Repository failure
PersonaDetailsPanel_empty_justification_msg=The justification field cannot be empty
PersonaDetailsPanel_empty_justification_Title=Empty justification
PersonaDetailsPanel_EmptyComment_msg=Persona comment cannot be empty.
PersonaDetailsPanel_EmptyComment_Title=Empty persona comment
PersonaDetailsPanel_EmptyName_msg=Persona name cannot be empty.
PersonaDetailsPanel_EmptyName_Title=Empty persona name
PersonaDetailsPanel_load_exception_msg=Failed to load persona.
PersonaDetailsPanel_load_exception_Title=Initialization failure
PersonaDetailsPanel_NotEnoughAccounts_msg=A persona needs at least one account.
PersonaDetailsPanel_NotEnoughAccounts_Title=Missing account
PersonaManagerTopComponent.createBtn.text=New Persona
PersonaManagerTopComponent.searchBtn.text=Search
PersonaManagerTopComponent.resultsTable.columnModel.title1=Name
PersonaManagerTopComponent.resultsTable.columnModel.title0=ID
PersonaManagerTopComponent.resultsTable.toolTipText=
PersonaManagerTopComponent.searchAccountRadio.text=Account
PersonaManagerTopComponent.searchNameRadio.text=Name
PersonaManagerTopComponent.searchField.text=
PersonaManagerTopComponent.editBtn.text=Edit Persona
PersonaDetailsDialog.cancelBtn.text=Cancel
PersonaDetailsDialog.okBtn.text=OK
PersonaDetailsPanel.casesLbl.text=Cases found in: PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add PersonaDetailsPanel.addAliasBtn.text=Add
@ -64,7 +48,6 @@ PersonaDetailsPanel.nameLbl.text=Name:
AddAliasDialog.accountsLbl.text=Account: AddAliasDialog.accountsLbl.text=Account:
AddAliasDialog.okBtn.text=OK AddAliasDialog.okBtn.text=OK
AddAliasDialog.cancelBtn.text=Cancel AddAliasDialog.cancelBtn.text=Cancel
PersonaManagerTopComponent.deleteBtn.text=Delete Persona
PersonaDetailsPanel.casesLbl.text=Cases found in: PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add PersonaDetailsPanel.addAliasBtn.text=Add
@ -98,6 +81,18 @@ PersonaAliasDialog.justificationLbl.text=Justification:
PersonaAliasDialog.aliasTextField.text= PersonaAliasDialog.aliasTextField.text=
PersonaAliasDialog.aliasLbl.text=Alias: PersonaAliasDialog.aliasLbl.text=Alias:
PersonaAliasDialog.okBtn.text_1=OK PersonaAliasDialog.okBtn.text_1=OK
PersonaDetailsPanel_CentralRepoErr_msg=Failure to write to Central Repository.
PersonaDetailsPanel_CentralRepoErr_Title=Central Repository failure
PersonaDetailsPanel_empty_justification_msg=The justification field cannot be empty
PersonaDetailsPanel_empty_justification_Title=Empty justification
PersonaDetailsPanel_EmptyComment_msg=Persona comment cannot be empty.
PersonaDetailsPanel_EmptyComment_Title=Empty persona comment
PersonaDetailsPanel_EmptyName_msg=Persona name cannot be empty.
PersonaDetailsPanel_EmptyName_Title=Empty persona name
PersonaDetailsPanel_load_exception_msg=Failed to load persona.
PersonaDetailsPanel_load_exception_Title=Initialization failure
PersonaDetailsPanel_NotEnoughAccounts_msg=A persona needs at least one account.
PersonaDetailsPanel_NotEnoughAccounts_Title=Missing account
PersonaMetadataDialog.confidenceLbl.text=Confidence: PersonaMetadataDialog.confidenceLbl.text=Confidence:
PersonaMetadataDialog.justificationTextField.text= PersonaMetadataDialog.justificationTextField.text=
PersonaMetadataDialog.justificationLbl.text=Justification: PersonaMetadataDialog.justificationLbl.text=Justification:
@ -110,10 +105,27 @@ PersonaMetadataDialog.cancelBtn.text=Cancel
PersonaDetailsPanel.editAccountBtn.text=Edit PersonaDetailsPanel.editAccountBtn.text=Edit
PersonaDetailsPanel.editMetadataBtn.text=Edit PersonaDetailsPanel.editMetadataBtn.text=Edit
PersonaDetailsPanel.editAliasBtn.text=Edit PersonaDetailsPanel.editAliasBtn.text=Edit
PMTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona? PersonasTopComponent.searchAccountRadio.text=Account
PMTopComponent_delete_confirmation_Title=Are you sure? PersonasTopComponent.searchNameRadio.text=Name
PMTopComponent_delete_exception_msg=Failed to delete persona. PersonasTopComponent.searchField.text=
PMTopComponent_delete_exception_Title=Delete failure PersonasTopComponent.deleteBtn.text=Delete Persona
PMTopComponent_Name=Persona Manager PersonasTopComponent.editBtn.text=Edit Persona
PMTopComponent_search_exception_msg=Failed to search personas. PersonasTopComponent.createBtn.text=New Persona
PMTopComponent_search_exception_Title=Search failure PersonasTopComponent.createAccountBtn.text=Create Account
PersonasTopComponent.searchBtn.text=Search
PersonasTopComponent.resultsTable.columnModel.title1=Name
PersonasTopComponent.resultsTable.columnModel.title0=ID
PersonasTopComponent.resultsTable.toolTipText=
CreatePersonaAccountDialog.cancelBtn.text=Cancel
CreatePersonaAccountDialog.typeLbl.text=Type:
CreatePersonaAccountDialog.identifierTextField.text=
CreatePersonaAccountDialog.identiferLbl.text=Identifier:
CreatePersonaAccountDialog.okBtn.text=OK
PersonasTopComponent.introText.text=Personas represent an online identity. They span cases and are stored in the Central Repository based on accounts that were found in artifacts. You can create, edit, and delete personas here.
PersonasTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona?
PersonasTopComponent_delete_confirmation_Title=Are you sure?
PersonasTopComponent_delete_exception_msg=Failed to delete persona.
PersonasTopComponent_delete_exception_Title=Delete failure
PersonasTopComponent_Name=Personas
PersonasTopComponent_search_exception_msg=Failed to search personas.
PersonasTopComponent_search_exception_Title=Search failure

View File

@ -8,3 +8,5 @@ PersonaAliasDialog.cancelBtn.text_1=\u53d6\u308a\u6d88\u3057
PersonaAliasDialog.okBtn.text_1=OK PersonaAliasDialog.okBtn.text_1=OK
PersonaMetadataDialog.okBtn.text=OK PersonaMetadataDialog.okBtn.text=OK
PersonaMetadataDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057 PersonaMetadataDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057
CreatePersonaAccountDialog.okBtn.text=OK
CreatePersonaAccountDialog.cancelBtn.text=\u53d6\u308a\u6d88\u3057

View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="2"/>
<Property name="resizable" type="boolean" value="false"/>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace pref="194" max="32767" attributes="0"/>
<Component id="okBtn" linkSize="2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelBtn" linkSize="2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Component id="settingsPanel" 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">
<EmptySpace max="-2" attributes="0"/>
<Component id="settingsPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="okBtn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="settingsPanel">
<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>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="typeLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="typeComboBox" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="identiferLbl" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="identifierTextField" pref="281" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="identiferLbl" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="identifierTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="typeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="typeLbl" alignment="3" min="-2" pref="9" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="identiferLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="CreatePersonaAccountDialog.identiferLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="identifierTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="CreatePersonaAccountDialog.identifierTextField.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="identifierTextFieldActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="typeLbl">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="CreatePersonaAccountDialog.typeLbl.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JComboBox" name="typeComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new javax.swing.DefaultComboBoxModel&lt;&gt;(getAllAccountTypes())" type="code"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="cancelBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="CreatePersonaAccountDialog.cancelBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[79, 23]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[79, 23]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[79, 23]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelBtnActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="okBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="CreatePersonaAccountDialog.okBtn.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="okBtnActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,281 @@
/*
* Central Repository
*
* 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.centralrepository.persona;
import java.awt.Component;
import java.io.Serializable;
import java.util.Collection;
import java.util.logging.Level;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Configuration dialog for creating an account.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class CreatePersonaAccountDialog extends JDialog {
private static final Logger logger = Logger.getLogger(CreatePersonaAccountDialog.class.getName());
private static final long serialVersionUID = 1L;
private final TypeChoiceRenderer TYPE_CHOICE_RENDERER = new TypeChoiceRenderer();
/**
* Creates new create account dialog.
*/
@Messages({"CreatePersonaAccountDialog.title.text=Create Account",})
public CreatePersonaAccountDialog(PersonaDetailsPanel pdp) {
super(SwingUtilities.windowForComponent(pdp),
Bundle.PersonaAccountDialog_title_text(),
ModalityType.APPLICATION_MODAL);
initComponents();
typeComboBox.setRenderer(TYPE_CHOICE_RENDERER);
display();
}
/**
* This class handles displaying and rendering drop down menu for account
* choices.
*/
private class TypeChoiceRenderer extends JLabel implements ListCellRenderer<CentralRepoAccountType>, Serializable {
private static final long serialVersionUID = 1L;
@Override
public Component getListCellRendererComponent(
JList<? extends CentralRepoAccountType> list, CentralRepoAccountType value,
int index, boolean isSelected, boolean cellHasFocus) {
setText(value.getAcctType().getDisplayName());
return this;
}
}
private CentralRepoAccountType[] getAllAccountTypes() {
Collection<CentralRepoAccountType> allAccountTypes;
try {
allAccountTypes = CentralRepository.getInstance().getAllAccountTypes();
} catch (CentralRepoException e) {
logger.log(Level.SEVERE, "Failed to access central repository", e);
JOptionPane.showMessageDialog(this,
Bundle.PersonaAccountDialog_get_types_exception_Title(),
Bundle.PersonaAccountDialog_get_types_exception_msg(),
JOptionPane.ERROR_MESSAGE);
return new CentralRepoAccountType[0];
}
return allAccountTypes.toArray(new CentralRepoAccountType[0]);
}
/**
* 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() {
settingsPanel = new javax.swing.JPanel();
identiferLbl = new javax.swing.JLabel();
identifierTextField = new javax.swing.JTextField();
typeLbl = new javax.swing.JLabel();
typeComboBox = new javax.swing.JComboBox<>();
cancelBtn = new javax.swing.JButton();
okBtn = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setResizable(false);
settingsPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
org.openide.awt.Mnemonics.setLocalizedText(identiferLbl, org.openide.util.NbBundle.getMessage(CreatePersonaAccountDialog.class, "CreatePersonaAccountDialog.identiferLbl.text")); // NOI18N
identifierTextField.setText(org.openide.util.NbBundle.getMessage(CreatePersonaAccountDialog.class, "CreatePersonaAccountDialog.identifierTextField.text")); // NOI18N
identifierTextField.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
identifierTextFieldActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(typeLbl, org.openide.util.NbBundle.getMessage(CreatePersonaAccountDialog.class, "CreatePersonaAccountDialog.typeLbl.text")); // NOI18N
typeComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(getAllAccountTypes()));
javax.swing.GroupLayout settingsPanelLayout = new javax.swing.GroupLayout(settingsPanel);
settingsPanel.setLayout(settingsPanelLayout);
settingsPanelLayout.setHorizontalGroup(
settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(settingsPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(settingsPanelLayout.createSequentialGroup()
.addComponent(typeLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(typeComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(settingsPanelLayout.createSequentialGroup()
.addComponent(identiferLbl)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(identifierTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 281, Short.MAX_VALUE)))
.addContainerGap())
);
settingsPanelLayout.setVerticalGroup(
settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(settingsPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(identiferLbl)
.addComponent(identifierTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(settingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(typeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(typeLbl, javax.swing.GroupLayout.PREFERRED_SIZE, 9, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
org.openide.awt.Mnemonics.setLocalizedText(cancelBtn, org.openide.util.NbBundle.getMessage(CreatePersonaAccountDialog.class, "CreatePersonaAccountDialog.cancelBtn.text")); // NOI18N
cancelBtn.setMaximumSize(new java.awt.Dimension(79, 23));
cancelBtn.setMinimumSize(new java.awt.Dimension(79, 23));
cancelBtn.setPreferredSize(new java.awt.Dimension(79, 23));
cancelBtn.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancelBtnActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(okBtn, org.openide.util.NbBundle.getMessage(CreatePersonaAccountDialog.class, "CreatePersonaAccountDialog.okBtn.text")); // NOI18N
okBtn.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
okBtnActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap(194, Short.MAX_VALUE)
.addComponent(okBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelBtn, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
.addComponent(settingsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelBtn, okBtn});
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(settingsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(okBtn)
.addComponent(cancelBtn, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void display() {
this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
setVisible(true);
}
@Messages({
"CreatePersonaAccountDialog_error_title=Account failure",
"CreatePersonaAccountDialog_error_msg=Failed to create account.",
})
private CentralRepoAccount createAccount(CentralRepoAccount.CentralRepoAccountType type, String identifier) {
CentralRepoAccount ret = null;
try {
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
ret = cr.getOrCreateAccount(type, identifier);
}
} catch (CentralRepoException e) {
logger.log(Level.SEVERE, "Failed to create account", e);
JOptionPane.showMessageDialog(this,
Bundle.CreatePersonaAccountDialog_error_title(),
Bundle.CreatePersonaAccountDialog_error_msg(),
JOptionPane.ERROR_MESSAGE);
}
return ret;
}
@Messages({
"CreatePersonaAccountDialog_success_title=Account added",
"CreatePersonaAccountDialog_success_msg=Account added.",
})
private void okBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okBtnActionPerformed
if (identifierTextField.getText().isEmpty()) {
JOptionPane.showMessageDialog(this,
Bundle.PersonaAccountDialog_identifier_empty_msg(),
Bundle.PersonaAccountDialog_identifier_empty_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
CentralRepoAccount.CentralRepoAccountType type =
(CentralRepoAccount.CentralRepoAccountType) typeComboBox.getSelectedItem();
String identifier = identifierTextField.getText();
if (createAccount(type, identifier) != null) {
// show account created message
JOptionPane.showMessageDialog(this,
Bundle.CreatePersonaAccountDialog_success_msg(),
Bundle.CreatePersonaAccountDialog_success_title(),
JOptionPane.INFORMATION_MESSAGE);
dispose();
}
}//GEN-LAST:event_okBtnActionPerformed
private void cancelBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelBtnActionPerformed
dispose();
}//GEN-LAST:event_cancelBtnActionPerformed
private void identifierTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_identifierTextFieldActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_identifierTextFieldActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton cancelBtn;
private javax.swing.JLabel identiferLbl;
private javax.swing.JTextField identifierTextField;
private javax.swing.JButton okBtn;
private javax.swing.JPanel settingsPanel;
private javax.swing.JComboBox<org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType> typeComboBox;
private javax.swing.JLabel typeLbl;
// End of variables declaration//GEN-END:variables
}

View File

@ -35,18 +35,18 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
* An Action that opens the Persona Search window. * An Action that opens the Persona Search window.
*/ */
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.centralrepository.persona.OpenPersonaManagerAction") @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.centralrepository.persona.OpenPersonasAction")
@ActionRegistration(displayName = "#CTL_OpenPersonaManager", lazy = false) @ActionRegistration(displayName = "#CTL_OpenPersonas", lazy = false)
@ActionReferences(value = { @ActionReferences(value = {
@ActionReference(path = "Menu/Tools", position = 105) @ActionReference(path = "Menu/Tools", position = 105)
}) })
public final class OpenPersonaManagerAction extends CallableSystemAction { public final class OpenPersonasAction extends CallableSystemAction {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final JMenuItem menuItem; private final JMenuItem menuItem;
public OpenPersonaManagerAction() { public OpenPersonasAction() {
menuItem = super.getMenuPresenter(); menuItem = super.getMenuPresenter();
this.setEnabled(CentralRepository.isEnabled()); this.setEnabled(CentralRepository.isEnabled());
} }
@ -54,7 +54,7 @@ public final class OpenPersonaManagerAction extends CallableSystemAction {
@Override @Override
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void performAction() { public void performAction() {
final TopComponent topComponent = WindowManager.getDefault().findTopComponent("PersonaManagerTopComponent"); final TopComponent topComponent = WindowManager.getDefault().findTopComponent("PersonasTopComponent");
if (topComponent != null) { if (topComponent != null) {
if (topComponent.isOpened() == false) { if (topComponent.isOpened() == false) {
topComponent.open(); topComponent.open();
@ -65,7 +65,7 @@ public final class OpenPersonaManagerAction extends CallableSystemAction {
} }
@Override @Override
@NbBundle.Messages("OpenPersonasAction.displayName=Persona Manager") @NbBundle.Messages("OpenPersonasAction.displayName=Personas")
public String getName() { public String getName() {
return Bundle.OpenPersonasAction_displayName(); return Bundle.OpenPersonasAction_displayName();
} }

View File

@ -1,35 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?> <?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"> <Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents> <NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="searchButtonGroup"> <Component class="javax.swing.ButtonGroup" name="searchButtonGroup">
</Component> </Component>
</NonVisualComponents> </NonVisualComponents>
<Properties> <Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="name" type="java.lang.String" value="" noResource="true"/>
<Dimension value="[400, 400]"/>
</Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
@ -46,17 +23,52 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jSplitPane1" alignment="0" pref="692" max="32767" attributes="0"/> <Component id="introTextScrollPane" alignment="0" max="32767" attributes="0"/>
<Component id="mainSplitPane" alignment="0" pref="724" max="32767" attributes="0"/>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jSplitPane1" alignment="0" max="32767" attributes="0"/> <Group type="102" alignment="1" attributes="0">
<Component id="introTextScrollPane" min="-2" pref="49" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="mainSplitPane" pref="470" max="32767" attributes="0"/>
</Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
</Layout> </Layout>
<SubComponents> <SubComponents>
<Container class="javax.swing.JSplitPane" name="jSplitPane1"> <Container class="javax.swing.JScrollPane" name="introTextScrollPane">
<Properties>
<Property name="verticalScrollBarPolicy" type="int" value="21"/>
</Properties>
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextArea" name="introText">
<Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection component="Form" name="getBackground" type="method"/>
</Property>
<Property name="columns" type="int" value="20"/>
<Property name="lineWrap" type="boolean" value="true"/>
<Property name="rows" type="int" value="5"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.introText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="wrapStyleWord" type="boolean" value="true"/>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
<Properties>
<Property name="dividerLocation" type="int" value="400"/>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents> <SubComponents>
@ -70,16 +82,10 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Component id="createButtonSeparator" max="32767" attributes="0"/>
<Component id="createBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="editBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="deleteBtn" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="resultsPane" pref="0" max="32767" attributes="0"/> <Component id="resultsPane" pref="0" max="32767" attributes="0"/>
<Component id="searchField" max="32767" attributes="0"/> <Component id="searchField" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
@ -89,6 +95,19 @@
<EmptySpace max="32767" attributes="0"/> <EmptySpace max="32767" attributes="0"/>
<Component id="searchBtn" min="-2" max="-2" attributes="0"/> <Component id="searchBtn" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="createAccountBtn" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="createBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="editBtn" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="deleteBtn" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -106,7 +125,7 @@
<Component id="searchBtn" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="searchBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="resultsPane" pref="528" max="32767" attributes="0"/> <Component id="resultsPane" pref="300" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="editBtn" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="editBtn" alignment="3" min="-2" max="-2" attributes="0"/>
@ -114,6 +133,10 @@
<Component id="deleteBtn" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="deleteBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="createButtonSeparator" min="-2" pref="4" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="createAccountBtn" min="-2" pref="32" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -122,7 +145,7 @@
<Component class="javax.swing.JTextField" name="searchField"> <Component class="javax.swing.JTextField" name="searchField">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.searchField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.searchField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -133,7 +156,7 @@
</Property> </Property>
<Property name="selected" type="boolean" value="true"/> <Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.searchNameRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.searchNameRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -143,7 +166,14 @@
<ComponentRef name="searchButtonGroup"/> <ComponentRef name="searchButtonGroup"/>
</Property> </Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.searchAccountRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.searchAccountRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<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/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.searchBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -157,20 +187,20 @@
<Component class="javax.swing.JTable" name="resultsTable"> <Component class="javax.swing.JTable" name="resultsTable">
<Properties> <Properties>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.resultsTable.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.resultsTable.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor"> <Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0"> <TableColumnModel selectionModel="0">
<Column maxWidth="25" minWidth="-1" prefWidth="-1" resizable="true"> <Column maxWidth="25" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.resultsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.resultsTable.columnModel.title0" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title> </Title>
<Editor/> <Editor/>
<Renderer/> <Renderer/>
</Column> </Column>
<Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.resultsTable.columnModel.title1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.resultsTable.columnModel.title1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Title> </Title>
<Editor/> <Editor/>
<Renderer/> <Renderer/>
@ -184,48 +214,59 @@
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="javax.swing.JButton" name="searchBtn"> <Component class="javax.swing.JButton" name="createAccountBtn">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.searchBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.createAccountBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="editBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.editBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="createBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.createBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/> <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
</AuxValues> </AuxValues>
</Component> </Component>
<Component class="javax.swing.JButton" name="deleteBtn"> <Component class="javax.swing.JButton" name="editBtn">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonaManagerTopComponent.deleteBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.editBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="enabled" type="boolean" value="false"/> <Property name="enabled" type="boolean" value="false"/>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JButton" name="deleteBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.deleteBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JSeparator" name="createButtonSeparator">
</Component>
<Component class="javax.swing.JButton" name="createBtn">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.createBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
</AuxValues>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel" name="detailsPanel"> <Container class="javax.swing.JScrollPane" name="detailsScrollPane">
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/> <JSplitPaneConstraints position="right"/>
</Constraint> </Constraint>
</Constraints> </Constraints>
</Component>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel" name="detailsPanel">
</Component>
</SubComponents>
</Container>
</SubComponents> </SubComponents>
</Container> </Container>
</SubComponents> </SubComponents>

View File

@ -44,27 +44,29 @@ import org.sleuthkit.autopsy.coreutils.Logger;
* Top component for the Personas tool * Top component for the Personas tool
* *
*/ */
@TopComponent.Description(preferredID = "PersonaManagerTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER) @TopComponent.Description(preferredID = "PersonasTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER)
@TopComponent.Registration(mode = "personamanager", openAtStartup = false) @TopComponent.Registration(mode = "personas", openAtStartup = false)
@RetainLocation("personamanager") @RetainLocation("personas")
@SuppressWarnings("PMD.SingularField") @SuppressWarnings("PMD.SingularField")
public final class PersonaManagerTopComponent extends TopComponent { public final class PersonasTopComponent extends TopComponent {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(PersonaManagerTopComponent.class.getName()); private static final Logger logger = Logger.getLogger(PersonasTopComponent.class.getName());
private List<Persona> currentResults = null; private List<Persona> currentResults = null;
private Persona selectedPersona = null; private Persona selectedPersona = null;
@Messages({ @Messages({
"PMTopComponent_Name=Persona Manager", "PersonasTopComponent_Name=Personas",
"PMTopComponent_delete_exception_Title=Delete failure", "PersonasTopComponent_delete_exception_Title=Delete failure",
"PMTopComponent_delete_exception_msg=Failed to delete persona.", "PersonasTopComponent_delete_exception_msg=Failed to delete persona.",
"PMTopComponent_delete_confirmation_Title=Are you sure?", "PersonasTopComponent_delete_confirmation_Title=Are you sure?",
"PMTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona?", "PersonasTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona?",
}) })
public PersonaManagerTopComponent() { public PersonasTopComponent() {
initComponents(); initComponents();
setName(Bundle.PMTopComponent_Name()); setName(Bundle.PersonasTopComponent_Name());
executeSearch(); executeSearch();
searchBtn.addActionListener(new ActionListener() { searchBtn.addActionListener(new ActionListener() {
@ -77,7 +79,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
editBtn.addActionListener(new ActionListener() { editBtn.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
new PersonaDetailsDialog(PersonaManagerTopComponent.this, new PersonaDetailsDialog(PersonasTopComponent.this,
PersonaDetailsMode.EDIT, selectedPersona, new CreateEditCallbackImpl()); PersonaDetailsMode.EDIT, selectedPersona, new CreateEditCallbackImpl());
} }
}); });
@ -85,7 +87,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
createBtn.addActionListener(new ActionListener() { createBtn.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
new PersonaDetailsDialog(PersonaManagerTopComponent.this, new PersonaDetailsDialog(PersonasTopComponent.this,
PersonaDetailsMode.CREATE, selectedPersona, new CreateEditCallbackImpl()); PersonaDetailsMode.CREATE, selectedPersona, new CreateEditCallbackImpl());
} }
}); });
@ -94,8 +96,8 @@ public final class PersonaManagerTopComponent extends TopComponent {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
NotifyDescriptor confirm = new NotifyDescriptor.Confirmation( NotifyDescriptor confirm = new NotifyDescriptor.Confirmation(
Bundle.PMTopComponent_delete_confirmation_msg(), Bundle.PersonasTopComponent_delete_confirmation_msg(),
Bundle.PMTopComponent_delete_confirmation_Title(), Bundle.PersonasTopComponent_delete_confirmation_Title(),
NotifyDescriptor.YES_NO_OPTION); NotifyDescriptor.YES_NO_OPTION);
DialogDisplayer.getDefault().notify(confirm); DialogDisplayer.getDefault().notify(confirm);
if (confirm.getValue().equals(NotifyDescriptor.YES_OPTION)) { if (confirm.getValue().equals(NotifyDescriptor.YES_OPTION)) {
@ -105,9 +107,9 @@ public final class PersonaManagerTopComponent extends TopComponent {
} }
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Failed to delete persona: " + selectedPersona.getName(), ex); logger.log(Level.SEVERE, "Failed to delete persona: " + selectedPersona.getName(), ex);
JOptionPane.showMessageDialog(PersonaManagerTopComponent.this, JOptionPane.showMessageDialog(PersonasTopComponent.this,
Bundle.PMTopComponent_delete_exception_msg(), Bundle.PersonasTopComponent_delete_exception_msg(),
Bundle.PMTopComponent_delete_exception_Title(), Bundle.PersonasTopComponent_delete_exception_Title(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
@ -132,6 +134,13 @@ public final class PersonaManagerTopComponent extends TopComponent {
searchAccountRadio.addActionListener((ActionEvent e) -> { searchAccountRadio.addActionListener((ActionEvent e) -> {
searchField.setText(""); searchField.setText("");
}); });
createAccountBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new CreatePersonaAccountDialog(detailsPanel);
}
});
} }
/** /**
@ -214,8 +223,8 @@ public final class PersonaManagerTopComponent extends TopComponent {
} }
@Messages({ @Messages({
"PMTopComponent_search_exception_Title=Search failure", "PersonasTopComponent_search_exception_Title=Search failure",
"PMTopComponent_search_exception_msg=Failed to search personas.",}) "PersonasTopComponent_search_exception_msg=Failed to search personas.",})
private void executeSearch() { private void executeSearch() {
Collection<Persona> results; Collection<Persona> results;
try { try {
@ -227,8 +236,8 @@ public final class PersonaManagerTopComponent extends TopComponent {
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Failed to search personas", ex); logger.log(Level.SEVERE, "Failed to search personas", ex);
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.PMTopComponent_search_exception_Title(), Bundle.PersonasTopComponent_search_exception_Title(),
Bundle.PMTopComponent_search_exception_msg(), Bundle.PersonasTopComponent_search_exception_msg(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
@ -254,49 +263,69 @@ public final class PersonaManagerTopComponent extends TopComponent {
private void initComponents() { private void initComponents() {
searchButtonGroup = new javax.swing.ButtonGroup(); searchButtonGroup = new javax.swing.ButtonGroup();
jSplitPane1 = new javax.swing.JSplitPane(); introTextScrollPane = new javax.swing.JScrollPane();
introText = new javax.swing.JTextArea();
mainSplitPane = new javax.swing.JSplitPane();
searchPanel = new javax.swing.JPanel(); searchPanel = new javax.swing.JPanel();
searchField = new javax.swing.JTextField(); searchField = new javax.swing.JTextField();
searchNameRadio = new javax.swing.JRadioButton(); searchNameRadio = new javax.swing.JRadioButton();
searchAccountRadio = new javax.swing.JRadioButton(); searchAccountRadio = new javax.swing.JRadioButton();
searchBtn = new javax.swing.JButton();
resultsPane = new javax.swing.JScrollPane(); resultsPane = new javax.swing.JScrollPane();
resultsTable = new javax.swing.JTable(); resultsTable = new javax.swing.JTable();
searchBtn = new javax.swing.JButton(); createAccountBtn = new javax.swing.JButton();
editBtn = new javax.swing.JButton(); editBtn = new javax.swing.JButton();
createBtn = new javax.swing.JButton();
deleteBtn = new javax.swing.JButton(); deleteBtn = new javax.swing.JButton();
createButtonSeparator = new javax.swing.JSeparator();
createBtn = new javax.swing.JButton();
detailsScrollPane = new javax.swing.JScrollPane();
detailsPanel = new org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel(); detailsPanel = new org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel();
setMinimumSize(new java.awt.Dimension(400, 400)); setName(""); // NOI18N
searchField.setText(org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.searchField.text")); // NOI18N introTextScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
introText.setBackground(getBackground());
introText.setColumns(20);
introText.setLineWrap(true);
introText.setRows(5);
introText.setText(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.introText.text")); // NOI18N
introText.setWrapStyleWord(true);
introText.setFocusable(false);
introTextScrollPane.setViewportView(introText);
mainSplitPane.setDividerLocation(400);
searchField.setText(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchField.text")); // NOI18N
searchButtonGroup.add(searchNameRadio); searchButtonGroup.add(searchNameRadio);
searchNameRadio.setSelected(true); searchNameRadio.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(searchNameRadio, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.searchNameRadio.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(searchNameRadio, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchNameRadio.text")); // NOI18N
searchButtonGroup.add(searchAccountRadio); searchButtonGroup.add(searchAccountRadio);
org.openide.awt.Mnemonics.setLocalizedText(searchAccountRadio, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.searchAccountRadio.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(searchAccountRadio, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchAccountRadio.text")); // NOI18N
resultsTable.setToolTipText(org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.resultsTable.toolTipText")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(searchBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchBtn.text")); // NOI18N
resultsTable.setToolTipText(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultsTable.toolTipText")); // NOI18N
resultsTable.getTableHeader().setReorderingAllowed(false); resultsTable.getTableHeader().setReorderingAllowed(false);
resultsPane.setViewportView(resultsTable); resultsPane.setViewportView(resultsTable);
if (resultsTable.getColumnModel().getColumnCount() > 0) { if (resultsTable.getColumnModel().getColumnCount() > 0) {
resultsTable.getColumnModel().getColumn(0).setMaxWidth(25); resultsTable.getColumnModel().getColumn(0).setMaxWidth(25);
resultsTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.resultsTable.columnModel.title0")); // NOI18N resultsTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultsTable.columnModel.title0")); // NOI18N
resultsTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.resultsTable.columnModel.title1")); // NOI18N resultsTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultsTable.columnModel.title1")); // NOI18N
} }
org.openide.awt.Mnemonics.setLocalizedText(searchBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.searchBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(createAccountBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.createAccountBtn.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(editBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.editBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(editBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.editBtn.text")); // NOI18N
editBtn.setEnabled(false); editBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(createBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.createBtn.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(deleteBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.deleteBtn.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(deleteBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.deleteBtn.text")); // NOI18N
deleteBtn.setEnabled(false); deleteBtn.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(createBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.createBtn.text")); // NOI18N
javax.swing.GroupLayout searchPanelLayout = new javax.swing.GroupLayout(searchPanel); javax.swing.GroupLayout searchPanelLayout = new javax.swing.GroupLayout(searchPanel);
searchPanel.setLayout(searchPanelLayout); searchPanel.setLayout(searchPanelLayout);
searchPanelLayout.setHorizontalGroup( searchPanelLayout.setHorizontalGroup(
@ -304,12 +333,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
.addGroup(searchPanelLayout.createSequentialGroup() .addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup() .addComponent(createButtonSeparator)
.addComponent(createBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(deleteBtn))
.addComponent(resultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) .addComponent(resultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(searchField) .addComponent(searchField)
.addGroup(searchPanelLayout.createSequentialGroup() .addGroup(searchPanelLayout.createSequentialGroup()
@ -317,7 +341,17 @@ public final class PersonaManagerTopComponent extends TopComponent {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(searchAccountRadio) .addComponent(searchAccountRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(searchBtn))) .addComponent(searchBtn))
.addGroup(searchPanelLayout.createSequentialGroup()
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(createAccountBtn)
.addGroup(searchPanelLayout.createSequentialGroup()
.addComponent(createBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editBtn)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(deleteBtn)))
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap()) .addContainerGap())
); );
searchPanelLayout.setVerticalGroup( searchPanelLayout.setVerticalGroup(
@ -331,36 +365,52 @@ public final class PersonaManagerTopComponent extends TopComponent {
.addComponent(searchAccountRadio) .addComponent(searchAccountRadio)
.addComponent(searchBtn)) .addComponent(searchBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(resultsPane, javax.swing.GroupLayout.DEFAULT_SIZE, 528, Short.MAX_VALUE) .addComponent(resultsPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(editBtn) .addComponent(editBtn)
.addComponent(createBtn) .addComponent(createBtn)
.addComponent(deleteBtn)) .addComponent(deleteBtn))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(createButtonSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 4, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(createAccountBtn, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap()) .addContainerGap())
); );
jSplitPane1.setLeftComponent(searchPanel); mainSplitPane.setLeftComponent(searchPanel);
jSplitPane1.setRightComponent(detailsPanel);
detailsScrollPane.setViewportView(detailsPanel);
mainSplitPane.setRightComponent(detailsScrollPane);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 692, Short.MAX_VALUE) .addComponent(introTextScrollPane)
.addComponent(mainSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 724, Short.MAX_VALUE)
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSplitPane1) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(introTextScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(mainSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 470, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton createAccountBtn;
private javax.swing.JButton createBtn; private javax.swing.JButton createBtn;
private javax.swing.JSeparator createButtonSeparator;
private javax.swing.JButton deleteBtn; private javax.swing.JButton deleteBtn;
private org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel detailsPanel; private org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel detailsPanel;
private javax.swing.JScrollPane detailsScrollPane;
private javax.swing.JButton editBtn; private javax.swing.JButton editBtn;
private javax.swing.JSplitPane jSplitPane1; private javax.swing.JTextArea introText;
private javax.swing.JScrollPane introTextScrollPane;
private javax.swing.JSplitPane mainSplitPane;
private javax.swing.JScrollPane resultsPane; private javax.swing.JScrollPane resultsPane;
private javax.swing.JTable resultsTable; private javax.swing.JTable resultsTable;
private javax.swing.JRadioButton searchAccountRadio; private javax.swing.JRadioButton searchAccountRadio;

View File

@ -862,7 +862,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
/** /**
* If the settings reflect that a inter-case search is being performed, * If the settings reflect that a inter-case search is being performed,
* checks that the data sources in the current case have been processed with * checks that the data sources in the current case have been processed with
* Correlation Engine enabled and exist in the central repository. Prompting * Central Repository enabled and exist in the central repository. Prompting
* the user as to whether they still want to perform the search in the case * the user as to whether they still want to perform the search in the case
* any data sources are unprocessed. If the settings reflect that a * any data sources are unprocessed. If the settings reflect that a
* intra-case search is being performed, it just performs the search. * intra-case search is being performed, it just performs the search.
@ -870,7 +870,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
* Notes: - Does not check that the data sources were processed into the * Notes: - Does not check that the data sources were processed into the
* current central repository instead of another. - Does not check that the * current central repository instead of another. - Does not check that the
* appropriate modules to make all correlation types available were run. - * appropriate modules to make all correlation types available were run. -
* Does not check if the correlation engine was run with any of the * Does not check if the Central Repository was run with any of the
* correlation properties properties disabled. * correlation properties properties disabled.
*/ */
@Messages({"CommonAttributePanel.incompleteResults.introText=Results may be incomplete. Not all data sources in the current case were ingested into the current Central Repository. The following data sources have not been processed:", @Messages({"CommonAttributePanel.incompleteResults.introText=Results may be incomplete. Not all data sources in the current case were ingested into the current Central Repository. The following data sources have not been processed:",
@ -902,14 +902,14 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
//if the datasource was previously processed we do not need to perform this check //if the datasource was previously processed we do not need to perform this check
for (CorrelationDataSource correlatedDataSource : correlatedDataSources) { for (CorrelationDataSource correlatedDataSource : correlatedDataSources) {
if (deviceID.equals(correlatedDataSource.getDeviceID())) { if (deviceID.equals(correlatedDataSource.getDeviceID())) {
//if the datasource exists in the central repository it may of been processed with the correlation engine //if the datasource exists in the central repository it may of been processed with the Central Repository
dataSourceCorrelationMap.put(dataSource, CorrelatedStatus.IN_CENTRAL_REPO); dataSourceCorrelationMap.put(dataSource, CorrelatedStatus.IN_CENTRAL_REPO);
break; break;
} }
} }
} }
if (dataSourceCorrelationMap.get(dataSource) == CorrelatedStatus.IN_CENTRAL_REPO) { if (dataSourceCorrelationMap.get(dataSource) == CorrelatedStatus.IN_CENTRAL_REPO) {
//if the data source was in the central repository check if any of the modules run on it were the correlation engine //if the data source was in the central repository check if any of the modules run on it were the Central Repository
for (IngestModuleInfo ingestModuleInfo : jobInfo.getIngestModuleInfo()) { for (IngestModuleInfo ingestModuleInfo : jobInfo.getIngestModuleInfo()) {
if (correlationEngineModuleName.equals(ingestModuleInfo.getDisplayName())) { if (correlationEngineModuleName.equals(ingestModuleInfo.getDisplayName())) {
dataSourceCorrelationMap.put(dataSource, CorrelatedStatus.CORRELATED); dataSourceCorrelationMap.put(dataSource, CorrelatedStatus.CORRELATED);

View File

@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.communications.relationships;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.sleuthkit.autopsy.contentviewers.CallLogArtifactViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.CallLogArtifactViewer;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;

View File

@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.communications.relationships;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.sleuthkit.autopsy.contentviewers.ContactArtifactViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.ContactArtifactViewer;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;

View File

@ -25,7 +25,7 @@ import org.openide.explorer.ExplorerManager;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.contentviewers.MessageArtifactViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.MessageArtifactViewer;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;

View File

@ -939,46 +939,4 @@ manager.properties.lafError =\
manager.properties.brokenProperty = Broken default property {0} value: {1} manager.properties.brokenProperty = Broken default property {0} value: {1}
manager.properties.missingProperty = Missing default property {0} value: {1} manager.properties.missingProperty = Missing default property {0} value: {1}
DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
ContactArtifactViewer.contactNameLabel.text=Joanna Doe
ContactArtifactViewer.phonesLabel.text=Phone
ContactArtifactViewer.emailsLabel.text=Email
ContactArtifactViewer.othersLabel.text=Other
DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
CallLogArtifactViewer.dataSourceNameLabel.text=data source name
CallLogArtifactViewer.jLabel2.text=Device Id
CallLogArtifactViewer.deviceIdLabel.text=device id
CallLogArtifactViewer.localAccountIdLabel.text=local account
CallLogArtifactViewer.localAccountLabel.text=Local Account
CallLogArtifactViewer.jLabel4.text=Data Source Name
CallLogArtifactViewer.otherInfoLabel.text=Other Information
CallLogArtifactViewer.sourceSectionLabel.text=Source
CallLogArtifactViewer.callDetailsLabel.text=Call Details
CallLogArtifactViewer.durationLabel.text=Duration....
CallLogArtifactViewer.dateTimeLabel.text=Date/Time.....
CallLogArtifactViewer.directionLabel.text=Direction
CallLogArtifactViewer.onLabel.text=On
CallLogArtifactViewer.callLabel.text=call
MessageArtifactViewer.fromText.text=from address goes here
MessageArtifactViewer.datetimeText.text=date goes here
MessageArtifactViewer.headersScrollPane.TabConstraints.tabTitle=Headers
MessageArtifactViewer.fromLabel.text=From:
MessageArtifactViewer.directionText.text=direction
MessageArtifactViewer.subjectText.text=subject goes here
MessageArtifactViewer.viewInNewWindowButton.text=View in New Window
MessageArtifactViewer.subjectLabel.text=Subject:
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
MessageArtifactViewer.ccText.text=cc list goes here
MessageArtifactViewer.ccLabel.text=CC:
MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageArtifactViewer.toText.text=to list goes here
MessageArtifactViewer.toLabel.text=To:
MessageArtifactViewer.htmlPane.TabConstraints.tabTitle=HTML
CallLogArtifactViewer.localAccountPersonaLabel.text=Persona
CallLogArtifactViewer.localAccountPersonaNameLabel.text=jLabel1
CallLogArtifactViewer.localAccountPersonaButton.text=jButton1
ContactArtifactViewer.personasLabel.text=Personas
MessageArtifactViewer.accountsTab.TabConstraints.tabTitle=Accounts
ContactArtifactViewer.contactImage.text=

View File

@ -35,28 +35,6 @@ AnnotationsContentViewer.title=Annotations
AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content. AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content.
ApplicationContentViewer.title=Application ApplicationContentViewer.title=Application
ApplicationContentViewer.toolTip=Displays file contents. ApplicationContentViewer.toolTip=Displays file contents.
CallLogArtifactViewer_crdisbaled_persona_button_text=Create
CallLogArtifactViewer_crdisbaled_persona_label=Unknown
CallLogArtifactViewer_number_from=From
CallLogArtifactViewer_number_to=To
CallLogArtifactViewer_persona_button_new=Create
CallLogArtifactViewer_persona_button_view=View
CallLogArtifactViewer_persona_label=\ Persona
CallLogArtifactViewer_persona_searching=Searching...
CallLogArtifactViewer_persona_text_none=None Found
ContactArtifactViewer_missing_account_label=Missing Account:
ContactArtifactViewer_persona_account_justification=Account found in Contact artifact
ContactArtifactViewer_persona_button_new=Create
ContactArtifactViewer_persona_button_view=View
ContactArtifactViewer_persona_label=Persona
ContactArtifactViewer_persona_searching=\ Searching...
ContactArtifactViewer_persona_text_none=None found
ContactArtifactViewer_persona_unknown=Unknown
DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database
DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database
DefaultArtifactContentViewer.attrsTableHeader.sources=Source(s)
DefaultArtifactContentViewer.attrsTableHeader.type=Type
DefaultArtifactContentViewer.attrsTableHeader.value=Value
FXVideoPanel.pauseButton.infoLabel.playbackErr=Unable to play video. FXVideoPanel.pauseButton.infoLabel.playbackErr=Unable to play video.
FXVideoPanel.progress.bufferingCancelled=media buffering was canceled FXVideoPanel.progress.bufferingCancelled=media buffering was canceled
FXVideoPanel.progress.bufferingInterrupted=media buffering was interrupted FXVideoPanel.progress.bufferingInterrupted=media buffering was interrupted
@ -107,11 +85,6 @@ MediaViewVideoPanel.progressLabel.text=00:00
MediaViewVideoPanel.infoLabel.text=info MediaViewVideoPanel.infoLabel.text=info
MediaViewImagePanel.imgFileTooLarge.msg=Could not load image file (too large): {0} MediaViewImagePanel.imgFileTooLarge.msg=Could not load image file (too large): {0}
MessageAccountPanel_button_create_label=Create
MessageAccountPanel_button_view_label=View
MessageAccountPanel_persona_label=Persona:
MessageAccountPanel_unknown_label=Unknown
MessageArtifactViewer.AttachmentPanel.title=Attachments
Metadata.nodeText.none=None Metadata.nodeText.none=None
Metadata.nodeText.truncated=(results truncated) Metadata.nodeText.truncated=(results truncated)
Metadata.nodeText.unknown=Unknown Metadata.nodeText.unknown=Unknown
@ -1054,46 +1027,4 @@ manager.properties.lafError =\
manager.properties.brokenProperty = Broken default property {0} value: {1} manager.properties.brokenProperty = Broken default property {0} value: {1}
manager.properties.missingProperty = Missing default property {0} value: {1} manager.properties.missingProperty = Missing default property {0} value: {1}
DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
ContactArtifactViewer.contactNameLabel.text=Joanna Doe
ContactArtifactViewer.phonesLabel.text=Phone
ContactArtifactViewer.emailsLabel.text=Email
ContactArtifactViewer.othersLabel.text=Other
DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
CallLogArtifactViewer.dataSourceNameLabel.text=data source name
CallLogArtifactViewer.jLabel2.text=Device Id
CallLogArtifactViewer.deviceIdLabel.text=device id
CallLogArtifactViewer.localAccountIdLabel.text=local account
CallLogArtifactViewer.localAccountLabel.text=Local Account
CallLogArtifactViewer.jLabel4.text=Data Source Name
CallLogArtifactViewer.otherInfoLabel.text=Other Information
CallLogArtifactViewer.sourceSectionLabel.text=Source
CallLogArtifactViewer.callDetailsLabel.text=Call Details
CallLogArtifactViewer.durationLabel.text=Duration....
CallLogArtifactViewer.dateTimeLabel.text=Date/Time.....
CallLogArtifactViewer.directionLabel.text=Direction
CallLogArtifactViewer.onLabel.text=On
CallLogArtifactViewer.callLabel.text=call
MessageArtifactViewer.fromText.text=from address goes here
MessageArtifactViewer.datetimeText.text=date goes here
MessageArtifactViewer.headersScrollPane.TabConstraints.tabTitle=Headers
MessageArtifactViewer.fromLabel.text=From:
MessageArtifactViewer.directionText.text=direction
MessageArtifactViewer.subjectText.text=subject goes here
MessageArtifactViewer.viewInNewWindowButton.text=View in New Window
MessageArtifactViewer.subjectLabel.text=Subject:
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
MessageArtifactViewer.ccText.text=cc list goes here
MessageArtifactViewer.ccLabel.text=CC:
MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageArtifactViewer.toText.text=to list goes here
MessageArtifactViewer.toLabel.text=To:
MessageArtifactViewer.htmlPane.TabConstraints.tabTitle=HTML
CallLogArtifactViewer.localAccountPersonaLabel.text=Persona
CallLogArtifactViewer.localAccountPersonaNameLabel.text=jLabel1
CallLogArtifactViewer.localAccountPersonaButton.text=jButton1
ContactArtifactViewer.personasLabel.text=Personas
MessageArtifactViewer.accountsTab.TabConstraints.tabTitle=Accounts
ContactArtifactViewer.contactImage.text=

View File

@ -149,20 +149,3 @@ MediaViewImagePanel.tagsMenu.text_1=\u30bf\u30b0\u30e1\u30cb\u30e5\u30fc
SQLiteViewer.readTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} SQLiteViewer.readTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
# {0} - tableName # {0} - tableName
SQLiteViewer.selectTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u30ab\u30a6\u30f3\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0} SQLiteViewer.selectTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u30ab\u30a6\u30f3\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
DefaultArtifactContentViewer.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e
DefaultArtifactContentViewer.copyMenuItem.text=\u30b3\u30d4\u30fc
MessageArtifactViewer.fromText.text=\u9001\u4fe1\u5143\u30a2\u30c9\u30ec\u30b9\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.datetimeText.text=\u65e5\u4ed8\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.headersScrollPane.TabConstraints.tabTitle=\u30d8\u30c3\u30c0\u30fc
MessageArtifactViewer.fromLabel.text=\u5dee\u51fa\u4eba:
MessageArtifactViewer.directionText.text=\u9001\u53d7\u4fe1\u306e\u7a2e\u5225
MessageArtifactViewer.subjectText.text=\u4ef6\u540d\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.viewInNewWindowButton.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a
MessageArtifactViewer.subjectLabel.text=\u4ef6\u540d:
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb
MessageArtifactViewer.ccText.text=\u5b9b\u5148\u306eCC\u30ea\u30b9\u30c8\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.ccLabel.text=CC:
MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageArtifactViewer.toText.text=\u5b9b\u5148\u306eTO\u30ea\u30b9\u30c8\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.toLabel.text=\u5b9b\u5148:
MessageArtifactViewer.htmlPane.TabConstraints.tabTitle=HTML

View File

@ -1,227 +0,0 @@
<?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,1,-57,0,0,2,31"/>
</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="1" gridWidth="5" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<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>
<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="1" gridHeight="1" fill="2" ipadX="111" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</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="2" 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="3" 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="4" 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="5" 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="6" 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="7" 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="interPanelfiller">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 32767]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<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="8" gridWidth="1" gridHeight="2" fill="3" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.1"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="personasLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="personasLabel" 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.personasLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[90, 19]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[90, 19]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[90, 19]"/>
</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="9" 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="personasPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="10" 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="bottomFiller">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 32767]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<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="11" 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="rightFiller">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 0]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<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="3" 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>
<Component class="javax.swing.JLabel" name="contactImage">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/defaultContact.png"/>
</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.contactImage.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="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="19" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Form>

View File

@ -1,942 +0,0 @@
/*
* 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.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import org.openide.util.lookup.ServiceProvider;
import javax.swing.SwingWorker;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* This class displays the TSK_CONTACT artifact.
*/
@ServiceProvider(service = ArtifactContentViewer.class)
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;
private static final int TOP_INSET = 4;
private static final int LEFT_INSET = 12;
// contact name, if available.
private String contactName;
// A list of unique accounts matching the attributes of the contact artifact.
private final List<CentralRepoAccount> contactUniqueAccountsList = new ArrayList<>();
// A list of all unique personas and their account, found by searching on the
// account identifier attributes of the Contact artifact.
private final Map<Persona, ArrayList<CentralRepoAccount>> contactUniquePersonasMap = new HashMap<>();
private final static String DEFAULT_IMAGE_PATH = "/org/sleuthkit/autopsy/images/defaultContact.png";
private final ImageIcon defaultImage;
/**
* Creates new form for ContactArtifactViewer
*/
public ContactArtifactViewer() {
initComponents();
defaultImage = new ImageIcon(ContactArtifactViewer.class.getResource(DEFAULT_IMAGE_PATH));
}
/**
* 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();
javax.swing.Box.Filler interPanelfiller = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
personasLabel = new javax.swing.JLabel();
personasPanel = new javax.swing.JPanel();
javax.swing.Box.Filler bottomFiller = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
javax.swing.Box.Filler rightFiller = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
contactImage = new javax.swing.JLabel();
setLayout(new java.awt.GridBagLayout());
namePanel.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
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.ipadx = 111;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
namePanel.add(contactNameLabel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 5;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
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 = 2;
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 = 3;
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 = 4;
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 = 5;
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 = 6;
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 = 7;
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 = 8;
gridBagConstraints.gridheight = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.weighty = 0.1;
add(interPanelfiller, gridBagConstraints);
personasLabel.setFont(personasLabel.getFont().deriveFont(personasLabel.getFont().getStyle() | java.awt.Font.BOLD, personasLabel.getFont().getSize()+2));
org.openide.awt.Mnemonics.setLocalizedText(personasLabel, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.personasLabel.text")); // NOI18N
personasLabel.setMaximumSize(new java.awt.Dimension(90, 19));
personasLabel.setMinimumSize(new java.awt.Dimension(90, 19));
personasLabel.setPreferredSize(new java.awt.Dimension(90, 19));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 9;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(personasLabel, gridBagConstraints);
personasPanel.setLayout(new java.awt.GridBagLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 10;
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(personasPanel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 11;
gridBagConstraints.gridwidth = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weighty = 1.0;
add(bottomFiller, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 4;
gridBagConstraints.gridy = 3;
gridBagConstraints.gridheight = 8;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.ipadx = 2;
gridBagConstraints.weightx = 1.0;
add(rightFiller, gridBagConstraints);
contactImage.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/defaultContact.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(contactImage, org.openide.util.NbBundle.getMessage(ContactArtifactViewer.class, "ContactArtifactViewer.contactImage.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.insets = new java.awt.Insets(6, 19, 0, 0);
add(contactImage, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
@Override
public void setArtifact(BlackboardArtifact artifact) {
// Reset the panel.
resetComponent();
if (artifact == null) {
return;
}
List<BlackboardAttribute> phoneNumList = new ArrayList<>();
List<BlackboardAttribute> emailList = new ArrayList<>();
List<BlackboardAttribute> nameList = new ArrayList<>();
List<BlackboardAttribute> otherList = new ArrayList<>();
List<BlackboardAttribute> accountAttributesList = 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);
accountAttributesList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_EMAIL")) {
emailList.add(bba);
accountAttributesList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_NAME")) {
nameList.add(bba);
} else {
otherList.add(bba);
if (bba.getAttributeType().getTypeName().equalsIgnoreCase("TSK_ID")) {
accountAttributesList.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);
try {
initiatePersonasSearch(accountAttributesList);
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, String.format("Error getting Personas for Contact artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
}
contactImage.setIcon(getImageFromArtifact(artifact));
// repaint
this.revalidate();
this.repaint();
}
@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();
}
/**
* Clears all artifact specific state.
*/
private void resetComponent() {
contactNameLabel.setVisible(false);
emailsLabel.setVisible(false);
emailsPanel.removeAll();
//namePanel.removeAll(); // this is not dynamically populated, do not remove.
otherAttrsPanel.removeAll();
othersLabel.setVisible(false);
personasLabel.setVisible(false);
personasPanel.removeAll();
phoneNumbersPanel.removeAll();
phonesLabel.setVisible(false);
contactName = null;
contactUniqueAccountsList.clear();
contactUniquePersonasMap.clear();
contactImage.setIcon(defaultImage);
}
/**
* 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")) {
contactName = bba.getDisplayString();
contactNameLabel.setText(contactName);
contactNameLabel.setVisible(true);
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(TOP_INSET, LEFT_INSET, 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++;
}
sectionLabel.setVisible(true);
sectionPanel.setVisible(true);
sectionPanel.setLayout(gridBagLayout);
sectionPanel.revalidate();
sectionPanel.repaint();
}
/**
* Kicks off a search for personas, based in the list of attributes.
*
* @param accountAttributesList a list of account identifying attributes.
*
* @throws CentralRepoException
*/
@NbBundle.Messages({
"ContactArtifactViewer_persona_searching= Searching...",
"ContactArtifactViewer_persona_unknown=Unknown"
})
private void initiatePersonasSearch(List<BlackboardAttribute> accountAttributesList) throws CentralRepoException {
personasLabel.setVisible(true);
String personaStatusLabelText = CentralRepository.isEnabled()
? Bundle.ContactArtifactViewer_persona_searching()
: Bundle.ContactArtifactViewer_persona_unknown();
// create a gridbag layout to show each participant on one line
GridBagLayout gridBagLayout = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.FIRST_LINE_START;
constraints.gridx = 0;
constraints.gridy = 0;
constraints.insets = new java.awt.Insets(TOP_INSET, LEFT_INSET, 0, 0);
// Add a Persona Name label
constraints.fill = GridBagConstraints.NONE;
constraints.weightx = 0;
//javax.swing.Box.Filler filler1 = this.createFiller(5, 0);
//personasPanel.add(filler1, constraints);
javax.swing.JLabel personaLabel = new javax.swing.JLabel();
personaLabel.setText(Bundle.ContactArtifactViewer_persona_label());
personaLabel.setFont(personaLabel.getFont().deriveFont(Font.BOLD, personaLabel.getFont().getSize()));
gridBagLayout.setConstraints(personaLabel, constraints);
personasPanel.add(personaLabel);
constraints.gridy++;
javax.swing.JLabel personaStatusLabel = new javax.swing.JLabel();
personaStatusLabel.setText(personaStatusLabelText);
gridBagLayout.setConstraints(personaStatusLabel, constraints);
personasPanel.add(personaStatusLabel);
if (CentralRepository.isEnabled() ) {
personasLabel.setEnabled(true);
// Kick off a background task to serach for personas for the contact
ContactPersonaSearcherTask personaSearchTask = new ContactPersonaSearcherTask(accountAttributesList);
personaSearchTask.execute();
} else {
personasLabel.setEnabled(false);
personaLabel.setEnabled(false);
personaStatusLabel.setEnabled(false);
}
personasPanel.setLayout(gridBagLayout);
personasPanel.revalidate();
personasPanel.repaint();
}
/**
* Updates the Persona panel with the gathered persona information.
*/
private void updatePersonasPanel() {
// Clear out the panel
personasPanel.removeAll();
GridBagLayout gridBagLayout = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.FIRST_LINE_START;
constraints.gridx = 0;
constraints.gridy = 0;
constraints.insets = new java.awt.Insets(TOP_INSET, LEFT_INSET, 0, 0);
if (contactUniquePersonasMap.isEmpty()) {
showPersona(null, Collections.emptyList(), gridBagLayout, constraints);
} else {
for (Map.Entry<Persona, ArrayList<CentralRepoAccount>> entry : contactUniquePersonasMap.entrySet()) {
List<CentralRepoAccount> missingAccounts = new ArrayList<>();
ArrayList<CentralRepoAccount> personaAccounts = entry.getValue();
// create a list of accounts missing from this persona
for (CentralRepoAccount account : contactUniqueAccountsList) {
if (personaAccounts.contains(account) == false) {
missingAccounts.add(account);
}
}
showPersona(entry.getKey(), missingAccounts, gridBagLayout, constraints);
constraints.gridy += 2;
}
}
personasPanel.setLayout(gridBagLayout);
personasPanel.setSize(personasPanel.getPreferredSize());
personasPanel.revalidate();
personasPanel.repaint();
}
@NbBundle.Messages({
"ContactArtifactViewer_persona_label=Persona ",
"ContactArtifactViewer_persona_text_none=None found",
"ContactArtifactViewer_persona_button_view=View",
"ContactArtifactViewer_persona_button_new=Create",
"ContactArtifactViewer_missing_account_label=Missing Account: "
})
/**
* Displays the given persona in the persona panel.
*
* @param persona Persona to display.
* @param missingAccountsList List of accounts this persona may be missing.
* @param gridBagLayout Layout to use.
* @param constraints layout constraints.
*
* @throws CentralRepoException
*/
private void showPersona(Persona persona, List<CentralRepoAccount> missingAccountsList, GridBagLayout gridBagLayout, GridBagConstraints constraints) {
constraints.fill = GridBagConstraints.NONE;
constraints.weightx = 0;
constraints.gridx = 0;
//javax.swing.Box.Filler filler1 = createFiller(5, 0);
// gridBagLayout.setConstraints(filler1, constraints);
//personasPanel.add(filler1);
// Add a "Persona" label
//constraints.gridx++;
javax.swing.JLabel personaLabel = new javax.swing.JLabel();
personaLabel.setText(Bundle.ContactArtifactViewer_persona_label());
personaLabel.setFont(personaLabel.getFont().deriveFont(Font.BOLD, personaLabel.getFont().getSize()));
gridBagLayout.setConstraints(personaLabel, constraints);
personasPanel.add(personaLabel);
javax.swing.JLabel personaNameLabel = new javax.swing.JLabel();
javax.swing.JButton personaButton = new javax.swing.JButton();
String personaName;
String personaButtonText;
ActionListener personaButtonListener;
if (persona != null) {
personaName = persona.getName();
personaButtonText = Bundle.ContactArtifactViewer_persona_button_view();
personaButtonListener = new ViewPersonaButtonListener(persona);
} else {
personaName = Bundle.ContactArtifactViewer_persona_text_none();
personaButtonText = Bundle.ContactArtifactViewer_persona_button_new();
personaButtonListener = new CreatePersonaButtonListener(new PersonaUIComponents(personaNameLabel, personaButton));
}
// Add the label for persona name,
constraints.gridy++;
constraints.gridx = 0;
personaNameLabel.setText(personaName);
gridBagLayout.setConstraints(personaNameLabel, constraints);
personasPanel.add(personaNameLabel);
//constraints.gridx++;
//personasPanel.add(createFiller(5, 0), constraints);
// Add a Persona action button
constraints.gridx++;
personaButton.setText(personaButtonText);
personaButton.addActionListener(personaButtonListener);
// no top inset of the button, in order to center align with the labels.
constraints.insets = new java.awt.Insets(0, LEFT_INSET, 0, 0);
gridBagLayout.setConstraints(personaButton, constraints);
personasPanel.add(personaButton);
// restore normal inset
constraints.insets = new java.awt.Insets(TOP_INSET, LEFT_INSET, 0, 0);
// show missing accounts.
for (CentralRepoAccount missingAccount : missingAccountsList) {
constraints.weightx = 0;
constraints.gridx = 0;
constraints.gridy++;
// Add a "Missing Account: " label
constraints.gridx++; // Ident
javax.swing.JLabel missingAccountLabel = new javax.swing.JLabel();
missingAccountLabel.setText(Bundle.ContactArtifactViewer_missing_account_label());
gridBagLayout.setConstraints(missingAccountLabel, constraints);
personasPanel.add(missingAccountLabel);
// Add the label for account id,
constraints.gridx++;
javax.swing.JLabel missingAccountIdentifierLabel = new javax.swing.JLabel();
missingAccountIdentifierLabel.setText(missingAccount.getIdentifier());
gridBagLayout.setConstraints(missingAccountIdentifierLabel, constraints);
personasPanel.add(missingAccountIdentifierLabel);
}
}
/**
* Gets an image from a TSK_CONTACT artifact.
*
* @param artifact
*
* @return Image from a TSK_CONTACT artifact or default image if none was
* found or the artifact is not a TSK_CONTACT
*/
private ImageIcon getImageFromArtifact(BlackboardArtifact artifact) {
ImageIcon imageIcon = defaultImage;
if (artifact == null) {
return imageIcon;
}
BlackboardArtifact.ARTIFACT_TYPE artifactType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
if (artifactType != BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT) {
return imageIcon;
}
try {
for (Content content : artifact.getChildren()) {
if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile) content;
try {
BufferedImage image = ImageIO.read(new File(file.getLocalAbsPath()));
imageIcon = new ImageIcon(image);
break;
} catch (IOException ex) {
// ImageIO.read will through an IOException if file is not an image
// therefore we don't need to report this exception just try
// the next file.
}
}
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, String.format("Unable to load image for contact: %d", artifact.getId()), ex);
}
return imageIcon;
}
/**
* Thread to search for a personas for all account identifier attributes for
* a contact.
*/
private class ContactPersonaSearcherTask extends SwingWorker<Map<Persona, ArrayList<CentralRepoAccount>>, Void> {
private final List<BlackboardAttribute> accountAttributesList;
private final List<CentralRepoAccount> uniqueAccountsList = new ArrayList<>();
/**
* Creates a persona searcher task.
*
* @param accountAttributesList List of attributes that may map to
* accounts.
*/
ContactPersonaSearcherTask(List<BlackboardAttribute> accountAttributesList) {
this.accountAttributesList = accountAttributesList;
}
@Override
protected Map<Persona, ArrayList<CentralRepoAccount>> doInBackground() throws Exception {
Map<Persona, ArrayList<CentralRepoAccount>> uniquePersonas = new HashMap<>();
for (BlackboardAttribute bba : accountAttributesList) {
// Get account, add to accounts list
Collection<Persona> personas;
Collection<CentralRepoAccount> accountCandidates
= CentralRepoAccount.getAccountsWithIdentifier(bba.getValueString());
if (accountCandidates.isEmpty() == false) {
CentralRepoAccount account = accountCandidates.iterator().next();
if (uniqueAccountsList.contains(account) == false) {
uniqueAccountsList.add(account);
}
// get personas for the account
personas = PersonaAccount.getPersonaAccountsForAccount(account.getId())
.stream()
.map(PersonaAccount::getPersona)
.collect(Collectors.toList());
// make a list of unique personas, along with all their accounts
for (Persona persona : personas) {
if (uniquePersonas.containsKey(persona) == false) {
Collection<CentralRepoAccount> accounts = persona.getPersonaAccounts()
.stream()
.map(PersonaAccount::getAccount)
.collect(Collectors.toList());
ArrayList<CentralRepoAccount> personaAccountsList = new ArrayList<>(accounts);
uniquePersonas.put(persona, personaAccountsList);
}
}
}
}
return uniquePersonas;
}
@Override
protected void done() {
Map<Persona, ArrayList<CentralRepoAccount>> personasMap;
try {
personasMap = super.get();
if (this.isCancelled()) {
return;
}
contactUniquePersonasMap.clear();
contactUniquePersonasMap.putAll(personasMap);
contactUniqueAccountsList.clear();
contactUniqueAccountsList.addAll(uniqueAccountsList);
updatePersonasPanel();
} catch (CancellationException ex) {
logger.log(Level.INFO, "Persona searching was canceled."); //NON-NLS
} catch (InterruptedException ex) {
logger.log(Level.INFO, "Persona searching was interrupted."); //NON-NLS
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, "Fatal error during Persona search.", ex); //NON-NLS
}
}
}
/**
* A wrapper class that bags the UI components that need to be updated when
* a persona search task or a create dialog returns.
*/
private class PersonaUIComponents {
private final JLabel personaNameLabel;
private final JButton personaActionButton;
/**
* Constructor.
*
* @param personaNameLabel Persona name label.
* @param personaActionButton Persona action button.
*/
PersonaUIComponents(JLabel personaNameLabel, JButton personaActionButton) {
this.personaNameLabel = personaNameLabel;
this.personaActionButton = personaActionButton;
}
/**
* Returns persona name label.
*
* @return Persona name label.
*/
public JLabel getPersonaNameLabel() {
return personaNameLabel;
}
/**
* Returns persona action button.
*
* @return Persona action button.
*/
public JButton getPersonaActionButton() {
return personaActionButton;
}
}
/**
* Action listener for Create persona button.
*/
private class CreatePersonaButtonListener implements ActionListener {
private final PersonaUIComponents personaUIComponents;
/**
* Constructs a listener for Create persona button..
*
* @param personaUIComponents UI components.
*/
CreatePersonaButtonListener(PersonaUIComponents personaUIComponents) {
this.personaUIComponents = personaUIComponents;
}
@NbBundle.Messages({
"ContactArtifactViewer_persona_account_justification=Account found in Contact artifact"
})
@Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
// Launch the Persona Create dialog - do not display immediately
PersonaDetailsDialog createPersonaDialog = new PersonaDetailsDialog(ContactArtifactViewer.this,
PersonaDetailsMode.CREATE, null, new PersonaCreateCallbackImpl(personaUIComponents), false);
// Pre populate the persona name and accounts if we have them.
PersonaDetailsPanel personaPanel = createPersonaDialog.getDetailsPanel();
if (contactName != null) {
personaPanel.setPersonaName(contactName);
}
// pass the list of accounts to the dialog
for (CentralRepoAccount account : contactUniqueAccountsList) {
personaPanel.addAccount(account, Bundle.ContactArtifactViewer_persona_account_justification(), Persona.Confidence.HIGH);
}
// display the dialog now
createPersonaDialog.display();
}
}
/**
* Action listener for View persona button.
*/
private class ViewPersonaButtonListener implements ActionListener {
private final Persona persona;
/**
* Creates listener for View persona button.
*
* @param persona
*/
ViewPersonaButtonListener(Persona persona) {
this.persona = persona;
}
@Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
new PersonaDetailsDialog(ContactArtifactViewer.this,
PersonaDetailsMode.VIEW, persona, new PersonaViewCallbackImpl());
}
}
/**
* Callback method for the create mode of the PersonaDetailsDialog
*/
class PersonaCreateCallbackImpl implements PersonaDetailsDialogCallback {
private final PersonaUIComponents personaUIComponents;
/**
* Creates a callback to handle new persona creation.
*
* @param personaUIComponents UI Components.
*/
PersonaCreateCallbackImpl(PersonaUIComponents personaUIComponents) {
this.personaUIComponents = personaUIComponents;
}
@Override
public void callback(Persona persona) {
JButton personaButton = personaUIComponents.getPersonaActionButton();
if (persona != null) {
// update the persona name label with newly created persona,
// and change the button to a "View" button
personaUIComponents.getPersonaNameLabel().setText(persona.getName());
personaUIComponents.getPersonaActionButton().setText(Bundle.ContactArtifactViewer_persona_button_view());
// replace action listener with a View button listener
for (ActionListener act : personaButton.getActionListeners()) {
personaButton.removeActionListener(act);
}
personaButton.addActionListener(new ViewPersonaButtonListener(persona));
}
personaButton.getParent().revalidate();
personaButton.getParent().repaint();
}
}
/**
* Callback method for the view mode of the PersonaDetailsDialog
*/
class PersonaViewCallbackImpl implements PersonaDetailsDialogCallback {
@Override
public void callback(Persona persona) {
// nothing to do
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel contactImage;
private javax.swing.JLabel contactNameLabel;
private javax.swing.JLabel emailsLabel;
private javax.swing.JPanel emailsPanel;
private javax.swing.JPanel namePanel;
private javax.swing.JPanel otherAttrsPanel;
private javax.swing.JLabel othersLabel;
private javax.swing.JLabel personasLabel;
private javax.swing.JPanel personasPanel;
private javax.swing.JPanel phoneNumbersPanel;
private javax.swing.JLabel phonesLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -42,7 +42,7 @@ import org.w3c.dom.events.EventTarget;
* A file content viewer for HTML files. * A file content viewer for HTML files.
*/ */
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class HtmlPanel extends javax.swing.JPanel { public final class HtmlPanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(HtmlPanel.class.getName()); private static final Logger logger = Logger.getLogger(HtmlPanel.class.getName());
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -54,7 +54,7 @@ final class HtmlPanel extends javax.swing.JPanel {
/** /**
* Creates new form HtmlViewerPanel * Creates new form HtmlViewerPanel
*/ */
HtmlPanel() { public HtmlPanel() {
initComponents(); initComponents();
Platform.runLater(() -> { Platform.runLater(() -> {
webView = new WebView(); webView = new WebView();
@ -83,7 +83,7 @@ final class HtmlPanel extends javax.swing.JPanel {
* *
* @param htmlText The HTML text to be applied to the text pane. * @param htmlText The HTML text to be applied to the text pane.
*/ */
void setHtmlText(String htmlText) { public void setHtmlText(String htmlText) {
this.htmlText = htmlText; this.htmlText = htmlText;
refresh(); refresh();
} }
@ -91,7 +91,7 @@ final class HtmlPanel extends javax.swing.JPanel {
/** /**
* Clear the HTML in the text pane and disable the show/hide button. * Clear the HTML in the text pane and disable the show/hide button.
*/ */
void reset() { public void reset() {
Platform.runLater(() -> { Platform.runLater(() -> {
webView.getEngine().loadContent("", TEXT_TYPE); webView.getEngine().loadContent("", TEXT_TYPE);
}); });

View File

@ -131,6 +131,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
private double zoomRatio; private double zoomRatio;
private double rotation; // Can be 0, 90, 180, and 270. private double rotation; // Can be 0, 90, 180, and 270.
private boolean autoResize = true; // Auto resize when the user changes the size
// of the content viewer unless the user has used the zoom buttons.
private static final double[] ZOOM_STEPS = { private static final double[] ZOOM_STEPS = {
0.0625, 0.125, 0.25, 0.375, 0.5, 0.75, 0.0625, 0.125, 0.25, 0.375, 0.5, 0.75,
1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10}; 1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10};
@ -197,7 +199,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
imageTaggingOptions.add(exportTagsMenuItem); imageTaggingOptions.add(exportTagsMenuItem);
imageTaggingOptions.setPopupSize(300, 150); imageTaggingOptions.setPopupSize(300, 150);
//Disable image tagging for non-windows users or upon failure to load OpenCV. //Disable image tagging for non-windows users or upon failure to load OpenCV.
if (!PlatformUtil.isWindowsOS() || !OpenCvLoader.openCvIsLoaded()) { if (!PlatformUtil.isWindowsOS() || !OpenCvLoader.openCvIsLoaded()) {
tagsMenu.setEnabled(false); tagsMenu.setEnabled(false);
@ -266,8 +268,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
/** /**
* Handle tags menu item enabling and disabling given the state of the * Handle tags menu item enabling and disabling given the state of the
* content viewer. For example, when the tags group is empty (no tags on image), * content viewer. For example, when the tags group is empty (no tags on
* disable delete menu item, hide menu item, and export menu item. * image), disable delete menu item, hide menu item, and export menu item.
*/ */
private void subscribeTagMenuItemsToStateChanges() { private void subscribeTagMenuItemsToStateChanges() {
pcs.addPropertyChangeListener((event) -> { pcs.addPropertyChangeListener((event) -> {
@ -394,6 +396,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
} }
try { try {
autoResize = true;
Image fxImage = readImageTask.get(); Image fxImage = readImageTask.get();
masterGroup.getChildren().clear(); masterGroup.getChildren().clear();
tagsGroup.getChildren().clear(); tagsGroup.getChildren().clear();
@ -464,7 +467,9 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
* current file. * current file.
* *
* @param contentTags * @param contentTags
*
* @return * @return
*
* @throws TskCoreException * @throws TskCoreException
* @throws NoCurrentCaseException * @throws NoCurrentCaseException
*/ */
@ -488,7 +493,9 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
* appropriate type. * appropriate type.
* *
* @param contentTags * @param contentTags
*
* @return * @return
*
* @throws TskCoreException * @throws TskCoreException
* @throws NoCurrentCaseException * @throws NoCurrentCaseException
*/ */
@ -685,16 +692,21 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void rotateLeftButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateLeftButtonActionPerformed private void rotateLeftButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateLeftButtonActionPerformed
autoResize = false;
rotation = (rotation + 270) % 360; rotation = (rotation + 270) % 360;
updateView(); updateView();
}//GEN-LAST:event_rotateLeftButtonActionPerformed }//GEN-LAST:event_rotateLeftButtonActionPerformed
private void rotateRightButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateRightButtonActionPerformed private void rotateRightButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateRightButtonActionPerformed
autoResize = false;
rotation = (rotation + 90) % 360; rotation = (rotation + 90) % 360;
updateView(); updateView();
}//GEN-LAST:event_rotateRightButtonActionPerformed }//GEN-LAST:event_rotateRightButtonActionPerformed
private void zoomInButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed private void zoomInButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed
autoResize = false;
// Find the next zoom step. // Find the next zoom step.
for (int i = 0; i < ZOOM_STEPS.length; i++) { for (int i = 0; i < ZOOM_STEPS.length; i++) {
if (zoomRatio < ZOOM_STEPS[i]) { if (zoomRatio < ZOOM_STEPS[i]) {
@ -706,6 +718,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}//GEN-LAST:event_zoomInButtonActionPerformed }//GEN-LAST:event_zoomInButtonActionPerformed
private void zoomOutButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed private void zoomOutButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed
autoResize = false;
// Find the next zoom step. // Find the next zoom step.
for (int i = ZOOM_STEPS.length - 1; i >= 0; i--) { for (int i = ZOOM_STEPS.length - 1; i >= 0; i--) {
if (zoomRatio > ZOOM_STEPS[i]) { if (zoomRatio > ZOOM_STEPS[i]) {
@ -717,11 +730,16 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}//GEN-LAST:event_zoomOutButtonActionPerformed }//GEN-LAST:event_zoomOutButtonActionPerformed
private void zoomResetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomResetButtonActionPerformed private void zoomResetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomResetButtonActionPerformed
autoResize = true;
resetView(); resetView();
}//GEN-LAST:event_zoomResetButtonActionPerformed }//GEN-LAST:event_zoomResetButtonActionPerformed
private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
updateView(); if (autoResize) {
resetView();
} else {
updateView();
}
}//GEN-LAST:event_formComponentResized }//GEN-LAST:event_formComponentResized
/** /**
@ -801,6 +819,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
* Creates an ImageTag instance from the ContentViewerTag. * Creates an ImageTag instance from the ContentViewerTag.
* *
* @param contentViewerTag * @param contentViewerTag
*
* @return * @return
*/ */
private ImageTag buildImageTag(ContentViewerTag<ImageTagRegion> contentViewerTag) { private ImageTag buildImageTag(ContentViewerTag<ImageTagRegion> contentViewerTag) {
@ -883,17 +902,17 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
List<ContentTag> tags = Case.getCurrentCase().getServices() List<ContentTag> tags = Case.getCurrentCase().getServices()
.getTagsManager().getContentTagsByContent(file); .getTagsManager().getContentTagsByContent(file);
List<ContentViewerTag<ImageTagRegion>> contentViewerTags = getContentViewerTags(tags); List<ContentViewerTag<ImageTagRegion>> contentViewerTags = getContentViewerTags(tags);
//Pull out image tag regions //Pull out image tag regions
Collection<ImageTagRegion> regions = contentViewerTags.stream() Collection<ImageTagRegion> regions = contentViewerTags.stream()
.map(cvTag -> cvTag.getDetails()).collect(Collectors.toList()); .map(cvTag -> cvTag.getDetails()).collect(Collectors.toList());
//Apply tags to image and write to file //Apply tags to image and write to file
BufferedImage taggedImage = ImageTagsUtil.getImageWithTags(file, regions); BufferedImage taggedImage = ImageTagsUtil.getImageWithTags(file, regions);
Path output = Paths.get(exportChooser.getSelectedFile().getPath(), Path output = Paths.get(exportChooser.getSelectedFile().getPath(),
FilenameUtils.getBaseName(file.getName()) + "-with_tags.png"); //NON-NLS FilenameUtils.getBaseName(file.getName()) + "-with_tags.png"); //NON-NLS
ImageIO.write(taggedImage, "png", output.toFile()); ImageIO.write(taggedImage, "png", output.toFile());
JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport()); JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport());
} catch (Exception ex) { //Runtime exceptions may spill out of ImageTagsUtil from JavaFX. } catch (Exception ex) { //Runtime exceptions may spill out of ImageTagsUtil from JavaFX.
//This ensures we (devs and users) have something when it doesn't work. //This ensures we (devs and users) have something when it doesn't work.

View File

@ -37,14 +37,14 @@ import org.sleuthkit.autopsy.texttranslation.ui.TranslateTextTask;
/** /**
* This is a panel for translation with a subcomponent that allows for translation. * This is a panel for translation with a subcomponent that allows for translation.
*/ */
class TranslatablePanel extends JPanel { public class TranslatablePanel extends JPanel {
/** /**
* This is an exception that can occur during the normal operation of the translatable * This is an exception that can occur during the normal operation of the translatable
* panel. For instance, this exception can be thrown if it is not possible to set the child * panel. For instance, this exception can be thrown if it is not possible to set the child
* content to the provided content string. * content to the provided content string.
*/ */
class TranslatablePanelException extends Exception { public class TranslatablePanelException extends Exception {
public static final long serialVersionUID = 1L; public static final long serialVersionUID = 1L;
TranslatablePanelException(String message) { TranslatablePanelException(String message) {
@ -61,7 +61,7 @@ class TranslatablePanel extends JPanel {
* This describes a child component to be placed as a child of this panel. The child received * This describes a child component to be placed as a child of this panel. The child received
* from {@link #getRootComponent() getRootComponent() } will listen for content updates from setContent(). * from {@link #getRootComponent() getRootComponent() } will listen for content updates from setContent().
*/ */
interface ContentComponent { public interface ContentComponent {
/** /**
* This method gets root component of the translation panel. * This method gets root component of the translation panel.
* @return the root component to insert into the translatable panel * @return the root component to insert into the translatable panel
@ -191,7 +191,7 @@ class TranslatablePanel extends JPanel {
@Messages({"TranslatablePanel.comboBoxOption.originalText=Original Text", @Messages({"TranslatablePanel.comboBoxOption.originalText=Original Text",
"TranslatablePanel.comboBoxOption.translatedText=Translated Text"}) "TranslatablePanel.comboBoxOption.translatedText=Translated Text"})
TranslatablePanel(ContentComponent contentComponent) { public TranslatablePanel(ContentComponent contentComponent) {
this( this(
contentComponent, contentComponent,
Bundle.TranslatablePanel_comboBoxOption_originalText(), Bundle.TranslatablePanel_comboBoxOption_originalText(),
@ -263,7 +263,7 @@ class TranslatablePanel extends JPanel {
* This resets the component to an empty state and sets the translation bar visibility * This resets the component to an empty state and sets the translation bar visibility
* based on whether there is a provider. * based on whether there is a provider.
*/ */
final void reset() { public final void reset() {
setContent(null, null); setContent(null, null);
} }
@ -272,7 +272,7 @@ class TranslatablePanel extends JPanel {
* @param content the content for the panel * @param content the content for the panel
* @param contentDescriptor the content descriptor to be used in error messages * @param contentDescriptor the content descriptor to be used in error messages
*/ */
void setContent(String content, String contentDescriptor) { public void setContent(String content, String contentDescriptor) {
cancelPendingTranslation(); cancelPendingTranslation();
setTranslationEnabled(); setTranslationEnabled();
this.translateComboBox.setSelectedIndex(0); this.translateComboBox.setSelectedIndex(0);

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.contentviewers; package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JLabel; import javax.swing.JLabel;

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.contentviewers; package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Component; import java.awt.Component;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;

View File

@ -0,0 +1,34 @@
# Copyright 2020 Basis Technology Corp.
#
# 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.
#
DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
MessageArtifactViewer.ccLabel.text=CC:
MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageArtifactViewer.toText.text=to list goes here
MessageArtifactViewer.toLabel.text=To:
MessageArtifactViewer.htmlPane.TabConstraints.tabTitle=HTML
MessageArtifactViewer.fromText.text=from address goes here
MessageArtifactViewer.datetimeText.text=date goes here
MessageArtifactViewer.headersScrollPane.TabConstraints.tabTitle=Headers
MessageArtifactViewer.fromLabel.text=From:
MessageArtifactViewer.accountsTab.TabConstraints.tabTitle=Accounts
MessageArtifactViewer.directionText.text=direction
MessageArtifactViewer.subjectText.text=subject goes here
MessageArtifactViewer.viewInNewWindowButton.text=View in New Window
MessageArtifactViewer.subjectLabel.text=Subject:
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
MessageArtifactViewer.ccText.text=cc list goes here
MessageArtifactViewer.textbodyScrollPane.TabConstraints.tabTitle=Text

View File

@ -0,0 +1,85 @@
# Copyright 2020 Basis Technology Corp.
#
# 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.
#
CalllogArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas.
CallLogArtifactViewer_heading_metadata=Metadata
CallLogArtifactViewer_heading_others=Other Attributes
CallLogArtifactViewer_heading_parties=Parties
CallLogArtifactViewer_heading_Source=Source
CallLogArtifactViewer_label_datasource=Data Source
CallLogArtifactViewer_label_date=Date
CallLogArtifactViewer_label_direction=Direction
CallLogArtifactViewer_label_duration=Duration
CallLogArtifactViewer_label_from=From
CallLogArtifactViewer_label_to=To
CallLogArtifactViewer_suffix_local=(Local)
CallLogArtifactViewer_value_unknown=Unknown
CommunicationArtifactViewerHelper_menuitem_copy=Copy
CommunicationArtifactViewerHelper_persona_button_create=Create
CommunicationArtifactViewerHelper_persona_button_view=View
CommunicationArtifactViewerHelper_persona_label=Persona:
CommunicationArtifactViewerHelper_persona_searching=Searching...
CommunicationArtifactViewerHelper_persona_unknown=Unknown
ContactArtifactViewer.contactImage.text=
ContactArtifactViewer_contactname_unknown=Unknown
ContactArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas.
ContactArtifactViewer_emails_header=Email
ContactArtifactViewer_found_all_accounts_label=All accounts found.
ContactArtifactViewer_heading_Source=Source
ContactArtifactViewer_label_datasource=Data Source
ContactArtifactViewer_missing_account_label=Missing contact account
ContactArtifactViewer_others_header=Other
ContactArtifactViewer_persona_account_justification=Account found in Contact artifact
ContactArtifactViewer_persona_button_new=Create
ContactArtifactViewer_persona_button_view=View
ContactArtifactViewer_persona_header=Persona
ContactArtifactViewer_persona_label=Persona
ContactArtifactViewer_persona_match_num=Match
ContactArtifactViewer_persona_no_match=No matches found
ContactArtifactViewer_persona_searching=Searching...
ContactArtifactViewer_persona_unknown=Unknown
ContactArtifactViewer_phones_header=Phone
ContactArtifactViewer_plural_suffix=s
DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database
DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database
DefaultArtifactContentViewer.attrsTableHeader.sources=Source(s)
DefaultArtifactContentViewer.attrsTableHeader.type=Type
DefaultArtifactContentViewer.attrsTableHeader.value=Value
DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
MessageAccountPanel_button_create_label=Create
MessageAccountPanel_button_view_label=View
MessageAccountPanel_persona_label=Persona:
MessageAccountPanel_unknown_label=Unknown
MessageArtifactViewer.AttachmentPanel.title=Attachments
MessageArtifactViewer.ccLabel.text=CC:
MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageArtifactViewer.toText.text=to list goes here
MessageArtifactViewer.toLabel.text=To:
MessageArtifactViewer.htmlPane.TabConstraints.tabTitle=HTML
MessageArtifactViewer.fromText.text=from address goes here
MessageArtifactViewer.datetimeText.text=date goes here
MessageArtifactViewer.headersScrollPane.TabConstraints.tabTitle=Headers
MessageArtifactViewer.fromLabel.text=From:
MessageArtifactViewer.accountsTab.TabConstraints.tabTitle=Accounts
MessageArtifactViewer.directionText.text=direction
MessageArtifactViewer.subjectText.text=subject goes here
MessageArtifactViewer.viewInNewWindowButton.text=View in New Window
MessageArtifactViewer.subjectLabel.text=Subject:
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
MessageArtifactViewer.ccText.text=cc list goes here
MessageArtifactViewer.textbodyScrollPane.TabConstraints.tabTitle=Text
# {0} - Persona count
PersonaDisplayTask_persona_count_suffix=(1 of {0})

View File

@ -0,0 +1,19 @@
DefaultArtifactContentViewer.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e
DefaultArtifactContentViewer.copyMenuItem.text=\u30b3\u30d4\u30fc
MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
MessageArtifactViewer.toText.text=\u5b9b\u5148\u306eTO\u30ea\u30b9\u30c8\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.toLabel.text=\u5b9b\u5148:
MessageArtifactViewer.htmlPane.TabConstraints.tabTitle=HTML
MessageArtifactViewer.fromText.text=\u9001\u4fe1\u5143\u30a2\u30c9\u30ec\u30b9\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.datetimeText.text=\u65e5\u4ed8\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.headersScrollPane.TabConstraints.tabTitle=\u30d8\u30c3\u30c0\u30fc
MessageArtifactViewer.fromLabel.text=\u5dee\u51fa\u4eba:
MessageArtifactViewer.directionText.text=\u9001\u53d7\u4fe1\u306e\u7a2e\u5225
MessageArtifactViewer.subjectText.text=\u4ef6\u540d\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.viewInNewWindowButton.text=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a
MessageArtifactViewer.subjectLabel.text=\u4ef6\u540d:
MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle=\u6dfb\u4ed8\u30d5\u30a1\u30a4\u30eb
MessageArtifactViewer.ccText.text=\u5b9b\u5148\u306eCC\u30ea\u30b9\u30c8\u3092\u3053\u3053\u306b\u8868\u793a
MessageArtifactViewer.ccLabel.text=CC:

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.contentviewers; package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Component; import java.awt.Component;
import java.awt.GridBagConstraints; import java.awt.GridBagConstraints;
@ -27,7 +27,6 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
@ -35,6 +34,7 @@ import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -66,8 +66,8 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
private GridBagLayout m_gridBagLayout = new GridBagLayout(); private GridBagLayout m_gridBagLayout = new GridBagLayout();
private GridBagConstraints m_constraints = new GridBagConstraints(); private GridBagConstraints m_constraints = new GridBagConstraints();
private final List<PersonaSearchAndDisplayTask> personaSearchtasks = new ArrayList<>(); private PersonaAccountFetcher currentAccountFetcher = null;
/** /**
* Creates new form CallLogArtifactViewer. * Creates new form CallLogArtifactViewer.
@ -92,6 +92,10 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
public void setArtifact(BlackboardArtifact artifact) { public void setArtifact(BlackboardArtifact artifact) {
resetComponent(); resetComponent();
if (artifact == null) {
return;
}
CallLogViewData callLogViewData = null; CallLogViewData callLogViewData = null;
try { try {
callLogViewData = getCallLogViewData(artifact); callLogViewData = getCallLogViewData(artifact);
@ -101,11 +105,16 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
// update the view with the call log data // update the view with the call log data
if (callLogViewData != null) { if (callLogViewData != null) {
updateView(callLogViewData); List<AccountPersonaSearcherData> personaSearchDataList = updateView(callLogViewData);
if(!personaSearchDataList.isEmpty()) {
currentAccountFetcher = new PersonaAccountFetcher(artifact, personaSearchDataList, this);
currentAccountFetcher.execute();
} else {
currentAccountFetcher = null;
}
} }
// repaint // repaint
this.revalidate(); this.revalidate();
} }
/** /**
@ -281,6 +290,8 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
* Update the viewer with the call log data. * Update the viewer with the call log data.
* *
* @param callLogViewData Call log data to update the view with. * @param callLogViewData Call log data to update the view with.
*
* @return List of AccountPersonaSearcherData objects.
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"CallLogArtifactViewer_heading_parties=Parties", "CallLogArtifactViewer_heading_parties=Parties",
@ -288,10 +299,11 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
"CallLogArtifactViewer_label_from=From", "CallLogArtifactViewer_label_from=From",
"CallLogArtifactViewer_label_to=To" "CallLogArtifactViewer_label_to=To"
}) })
private void updateView(CallLogViewData callLogViewData) { private List<AccountPersonaSearcherData> updateView(CallLogViewData callLogViewData) {
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_parties()); CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_parties());
List<AccountPersonaSearcherData> dataList = new ArrayList<>();
// Display From address // Display From address
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_from()); CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_from());
@ -301,10 +313,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString); CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
// show persona // show persona
Optional<PersonaSearchAndDisplayTask> task = CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount()); dataList.addAll( CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount()));
if (task.isPresent()) {
personaSearchtasks.add(task.get());
}
} else { } else {
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_value_unknown()); CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_value_unknown());
} }
@ -315,10 +324,8 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
String accountDisplayString = getAccountDisplayString(callLogViewData.getToAccount(), callLogViewData); String accountDisplayString = getAccountDisplayString(callLogViewData.getToAccount(), callLogViewData);
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString); CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
Optional<PersonaSearchAndDisplayTask> task = CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount()); dataList.addAll( CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount()));
if (task.isPresent()) {
personaSearchtasks.add(task.get());
}
} else { } else {
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_value_unknown()); CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_value_unknown());
} }
@ -328,20 +335,26 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_to()); CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_to());
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, otherParty); CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, otherParty);
Optional<PersonaSearchAndDisplayTask> task = CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, otherParty); dataList.addAll( CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, otherParty));
if (task.isPresent()) {
personaSearchtasks.add(task.get());
}
} }
updateMetadataView(callLogViewData); updateMetadataView(callLogViewData);
updateOtherAttributesView(callLogViewData);
updateSourceView(callLogViewData); updateSourceView(callLogViewData);
if (CentralRepository.isEnabled() == false) {
showCRDisabledMessage();
}
CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints); CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints);
this.setLayout(m_gridBagLayout); this.setLayout(m_gridBagLayout);
this.revalidate(); this.revalidate();
this.repaint(); this.repaint();
return dataList;
} }
/** /**
@ -392,6 +405,37 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDataSourceName()); CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDataSourceName());
} }
/**
* Update the other attributes section.
*
* @param callLogViewData Call log data.
*/
@NbBundle.Messages({
"CallLogArtifactViewer_heading_others=Other Attributes"
})
private void updateOtherAttributesView(CallLogViewData callLogViewData) {
if (callLogViewData.getOtherAttributes().isEmpty()) {
return;
}
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_others());
for (Map.Entry<String, String> entry : callLogViewData.getOtherAttributes().entrySet()) {
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, entry.getKey());
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, entry.getValue());
}
}
@NbBundle.Messages({
"CalllogArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas."
})
private void showCRDisabledMessage() {
CommunicationArtifactViewerHelper.addBlankLine(this, m_gridBagLayout, m_constraints);
m_constraints.gridy++;
CommunicationArtifactViewerHelper.addMessageRow(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
m_constraints.gridy++;
}
/** /**
* Returns display string for a account. Checks if the given account is the * Returns display string for a account. Checks if the given account is the
* local account, if it is known. If it is, it appends a "(Local)" suffix to * local account, if it is known. If it is, it appends a "(Local)" suffix to
@ -419,7 +463,9 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
@Override @Override
public boolean isSupported(BlackboardArtifact artifact) { public boolean isSupported(BlackboardArtifact artifact) {
return artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID();
return (artifact != null)
&& (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID());
} }
/** /**
@ -428,8 +474,10 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
private void resetComponent() { private void resetComponent() {
// cancel any outstanding persona searching threads. // cancel any outstanding persona searching threads.
personaSearchtasks.forEach(task -> task.cancel(Boolean.TRUE)); if(currentAccountFetcher != null && !currentAccountFetcher.isDone()) {
personaSearchtasks.clear(); currentAccountFetcher.cancel(true);
currentAccountFetcher = null;
}
// clear the panel // clear the panel
this.removeAll(); this.removeAll();
@ -441,9 +489,9 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
m_constraints.anchor = GridBagConstraints.FIRST_LINE_START; m_constraints.anchor = GridBagConstraints.FIRST_LINE_START;
m_constraints.gridy = 0; m_constraints.gridy = 0;
m_constraints.gridx = 0; m_constraints.gridx = 0;
m_constraints.weighty = 0.05; m_constraints.weighty = 0.0;
m_constraints.weightx = 0.05; m_constraints.weightx = 0.0; // keep components fixed horizontally.
m_constraints.insets = new java.awt.Insets(0, 0, 0, 0); m_constraints.insets = new java.awt.Insets(0, CommunicationArtifactViewerHelper.LEFT_INSET, 0, 0);
m_constraints.fill = GridBagConstraints.NONE; m_constraints.fill = GridBagConstraints.NONE;
} }

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.contentviewers; package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.contentviewers; package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
@ -27,12 +27,14 @@ import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JComponent;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import java.util.Optional;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
@ -41,20 +43,20 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
* A class to help display a communication artifact in a panel using a * A class to help display a communication artifact in a panel using a
* gridbaglayout. * gridbaglayout.
*/ */
public final class CommunicationArtifactViewerHelper { final class CommunicationArtifactViewerHelper {
// Number of columns in the gridbag layout. // Number of columns in the gridbag layout.
private final static int MAX_COLS = 4; private final static int MAX_COLS = 4;
private final static int LEFT_INDENT = 12; final static int LEFT_INSET = 12;
/** /**
* Empty private constructor * Empty private constructor
*/ */
private CommunicationArtifactViewerHelper() { private CommunicationArtifactViewerHelper() {
} }
/** /**
* Adds a new heading to the panel. * Adds a new heading to the panel.
* *
@ -62,8 +64,15 @@ public final class CommunicationArtifactViewerHelper {
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constrains to use.
* @param headerString Heading string to display. * @param headerString Heading string to display.
*
* @return JLabel Heading label added.
*/ */
static void addHeader(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String headerString) { static JLabel addHeader(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String headerString) {
Insets savedInsets = constraints.insets;
// create label for heading
javax.swing.JLabel headingLabel = new javax.swing.JLabel();
// add a blank line before the start of new section, unless it's // add a blank line before the start of new section, unless it's
// the first section // the first section
@ -75,9 +84,9 @@ public final class CommunicationArtifactViewerHelper {
// let the header span all of the row // let the header span all of the row
constraints.gridwidth = MAX_COLS; constraints.gridwidth = MAX_COLS;
constraints.insets = new Insets(0, 0, 0, 0); // No inset for header
// create label for heading // set text
javax.swing.JLabel headingLabel = new javax.swing.JLabel();
headingLabel.setText(headerString); headingLabel.setText(headerString);
// make it large and bold // make it large and bold
@ -92,6 +101,28 @@ public final class CommunicationArtifactViewerHelper {
// add line end glue // add line end glue
addLineEndGlue(panel, gridbagLayout, constraints); addLineEndGlue(panel, gridbagLayout, constraints);
//restore insets
constraints.insets = savedInsets;
return headingLabel;
}
/**
* Adds the given component to the panel.
*
* Caller must know what it's doing and set up all the constraints properly.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param component Component to add.
*/
static void addComponent(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, JComponent component) {
// add to panel
gridbagLayout.setConstraints(component, constraints);
panel.add(component);
} }
/** /**
@ -102,7 +133,7 @@ public final class CommunicationArtifactViewerHelper {
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constrains to use.
*/ */
private static void addLineEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) { static void addLineEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
// Place the filler just past the last column. // Place the filler just past the last column.
constraints.gridx = MAX_COLS; constraints.gridx = MAX_COLS;
@ -155,7 +186,7 @@ public final class CommunicationArtifactViewerHelper {
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constrains to use.
*/ */
private static void addBlankLine(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) { static void addBlankLine(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
constraints.gridy++; constraints.gridy++;
constraints.gridx = 0; constraints.gridx = 0;
@ -167,54 +198,85 @@ public final class CommunicationArtifactViewerHelper {
} }
/** /**
* Adds a label/key to the panel. * Adds a label/key to the panel at col 0.
* *
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constrains to use.
* @param keyString Key name to display. * @param keyString Key name to display.
*
* @return Label added.
*/ */
static void addKey(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String keyString) { static JLabel addKey(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String keyString) {
return addKeyAtCol(panel, gridbagLayout, constraints, keyString, 0);
}
/**
* Adds a label/key to the panel at specified column.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param keyString Key name to display.
* @param gridx column index, must be less than MAX_COLS - 1.
*
* @return Label added.
*/
static JLabel addKeyAtCol(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String keyString, int gridx) {
// create label
javax.swing.JLabel keyLabel = new javax.swing.JLabel();
constraints.gridy++; constraints.gridy++;
constraints.gridx = 0; constraints.gridx = gridx < MAX_COLS - 1 ? gridx : MAX_COLS - 2;
Insets savedInsets = constraints.insets; // set text
// Set inset to indent in
constraints.insets = new java.awt.Insets(0, LEFT_INDENT, 0, 0);
// create label,
javax.swing.JLabel keyLabel = new javax.swing.JLabel();
keyLabel.setText(keyString + ": "); keyLabel.setText(keyString + ": ");
// add to panel // add to panel
gridbagLayout.setConstraints(keyLabel, constraints); gridbagLayout.setConstraints(keyLabel, constraints);
panel.add(keyLabel); panel.add(keyLabel);
// restore inset return keyLabel;
constraints.insets = savedInsets;
} }
/** /**
* Adds a value string to the panel. * Adds a value string to the panel at col 1.
* *
* @param panel Panel to update. * @param panel Panel to update.
* @param gridbagLayout Layout to use. * @param gridbagLayout Layout to use.
* @param constraints Constrains to use. * @param constraints Constrains to use.
* @param keyString Value string to display. * @param keyString Value string to display.
*
* @return Label added.
*/ */
static void addValue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String valueString) { static JLabel addValue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String valueString) {
return addValueAtCol(panel, gridbagLayout, constraints, valueString, 1);
}
constraints.gridx = 1; /**
* Adds a value string to the panel at specified column.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @param keyString Value string to display.
* @param gridx Column index, must be less than MAX_COLS;
*
* @return Label added.
*/
static JLabel addValueAtCol(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String valueString, int gridx) {
// create label,
javax.swing.JLabel valueField = new javax.swing.JLabel();
constraints.gridx = gridx < MAX_COLS ? gridx : MAX_COLS - 1;
int savedGridwidth = constraints.gridwidth; int savedGridwidth = constraints.gridwidth;
// let the value span 2 cols // let the value span 2 cols
constraints.gridwidth = 2; constraints.gridwidth = 2;
// create label, // set text
javax.swing.JLabel valueField = new javax.swing.JLabel();
valueField.setText(valueString); valueField.setText(valueString);
// attach a right click menu with Copy option // attach a right click menu with Copy option
@ -234,6 +296,63 @@ public final class CommunicationArtifactViewerHelper {
// end the line // end the line
addLineEndGlue(panel, gridbagLayout, constraints); addLineEndGlue(panel, gridbagLayout, constraints);
return valueField;
}
/**
* Displays a message string, starting at column 0, and spanning the entire
* row.
*
* @param panel Panel to show.
* @param gridbagLayout Layout to use.
* @param constraints Constraints to use.
*
* @param messageString Message to display.
*
* @return Label for message added.
*/
static JLabel addMessageRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String messageString) {
return addMessageRow(panel, gridbagLayout, constraints, messageString, 0);
}
/**
* Displays a message string, starting at specified column, and spanning the
* entire row.
*
* @param panel Panel to show.
* @param gridbagLayout Layout to use.
* @param constraints Constraints to use.
*
* @param messageString Message to display.
*
* @return Label for message added.
*/
static JLabel addMessageRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String messageString, int gridx) {
// create label
javax.swing.JLabel messageLabel = new javax.swing.JLabel();
constraints.gridy++;
constraints.gridx = gridx < MAX_COLS - 1 ? gridx : MAX_COLS - 2;
int savedGridwidth = constraints.gridwidth;
constraints.gridwidth = 3;
// set text
messageLabel.setText(messageString);
// add to panel
gridbagLayout.setConstraints(messageLabel, constraints);
panel.add(messageLabel);
addLineEndGlue(panel, gridbagLayout, constraints);
// restore constraints
constraints.gridwidth = savedGridwidth;
return messageLabel;
} }
/** /**
@ -250,8 +369,7 @@ public final class CommunicationArtifactViewerHelper {
* @param constraints Constrains to use. * @param constraints Constrains to use.
* @param accountIdentifier Account identifier to search the persona. * @param accountIdentifier Account identifier to search the persona.
* *
* @return Optional PersonaSearchAndDisplayTask started to search for * @return List of AccountPersonaSearcherData objects.
* persona.
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"CommunicationArtifactViewerHelper_persona_label=Persona: ", "CommunicationArtifactViewerHelper_persona_label=Persona: ",
@ -260,17 +378,17 @@ public final class CommunicationArtifactViewerHelper {
"CommunicationArtifactViewerHelper_persona_button_view=View", "CommunicationArtifactViewerHelper_persona_button_view=View",
"CommunicationArtifactViewerHelper_persona_button_create=Create" "CommunicationArtifactViewerHelper_persona_button_create=Create"
}) })
static Optional<PersonaSearchAndDisplayTask> addPersonaRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String accountIdentifier) {
static List<AccountPersonaSearcherData> addPersonaRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String accountIdentifier) {
PersonaSearchAndDisplayTask personaTask = null; List<AccountPersonaSearcherData> dataList = new ArrayList<>();
constraints.gridy++; constraints.gridy++;
constraints.gridx = 1; constraints.gridx = 1;
Insets savedInsets = constraints.insets; Insets savedInsets = constraints.insets;
// Indent in // extra Indent in
constraints.insets = new java.awt.Insets(0, LEFT_INDENT, 0, 0); constraints.insets = new java.awt.Insets(0, 2 * LEFT_INSET, 0, 0);
// create label // create label
javax.swing.JLabel personaLabel = new javax.swing.JLabel(); javax.swing.JLabel personaLabel = new javax.swing.JLabel();
@ -293,23 +411,22 @@ public final class CommunicationArtifactViewerHelper {
// Place a button as place holder. It will be enabled when persona is available. // Place a button as place holder. It will be enabled when persona is available.
javax.swing.JButton personaButton = new javax.swing.JButton(); javax.swing.JButton personaButton = new javax.swing.JButton();
personaButton.setText(Bundle.CommunicationArtifactViewerHelper_persona_button_view()); personaButton.setText(Bundle.CommunicationArtifactViewerHelper_persona_button_view());
personaButton.setMargin(new Insets(0, 5, 0, 5));
personaButton.setEnabled(false); personaButton.setEnabled(false);
gridbagLayout.setConstraints(personaButton, constraints); gridbagLayout.setConstraints(personaButton, constraints);
panel.add(personaButton); panel.add(personaButton);
if (CentralRepository.isEnabled()) { if (CentralRepository.isEnabled()) {
// kick off a task to find the persona for this account // kick off a task to find the persona for this account
personaTask = new PersonaSearchAndDisplayTask(panel, new AccountPersonaSearcherData(accountIdentifier, personaLabel, personaButton)); dataList.add(new AccountPersonaSearcherData(accountIdentifier, personaLabel, personaButton));
personaTask.execute();
} else { } else {
personaLabel.setEnabled(false); personaLabel.setEnabled(false);
} }
addLineEndGlue(panel, gridbagLayout, constraints); addLineEndGlue(panel, gridbagLayout, constraints);
return Optional.ofNullable(personaTask); return dataList;
} }
/** /**

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="toolTipText" type="java.lang.String" value="" noResource="true"/>
</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"/>
<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,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
</Form>

View File

@ -0,0 +1,867 @@
/*
* 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.artifactviewers;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.SwingWorker;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* This class displays the TSK_CONTACT artifact.
*/
@ServiceProvider(service = ArtifactContentViewer.class)
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;
private GridBagLayout m_gridBagLayout = new GridBagLayout();
private GridBagConstraints m_constraints = new GridBagConstraints();
private JLabel personaSearchStatusLabel;
private BlackboardArtifact contactArtifact;
private String contactName;
private String datasourceName;
private List<BlackboardAttribute> phoneNumList = new ArrayList<>();
private List<BlackboardAttribute> emailList = new ArrayList<>();
private List<BlackboardAttribute> nameList = new ArrayList<>();
private List<BlackboardAttribute> otherList = new ArrayList<>();
private List<BlackboardAttribute> accountAttributesList = new ArrayList<>();
private final static String DEFAULT_IMAGE_PATH = "/org/sleuthkit/autopsy/images/defaultContact.png";
private final ImageIcon defaultImage;
// A list of unique accounts matching the attributes of the contact artifact.
private final List<CentralRepoAccount> contactUniqueAccountsList = new ArrayList<>();
// A list of all unique personas and their account, found by searching on the
// account identifier attributes of the Contact artifact.
private final Map<Persona, ArrayList<CentralRepoAccount>> contactUniquePersonasMap = new HashMap<>();
private ContactPersonaSearcherTask personaSearchTask;
/**
* Creates new form ContactArtifactViewer
*/
public ContactArtifactViewer() {
initComponents();
defaultImage = new ImageIcon(ContactArtifactViewer.class.getResource(DEFAULT_IMAGE_PATH));
}
/**
* 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() {
setToolTipText(""); // NOI18N
setLayout(new java.awt.GridBagLayout());
}// </editor-fold>//GEN-END:initComponents
@Override
public void setArtifact(BlackboardArtifact artifact) {
// Reset the panel.
resetComponent();
if (artifact == null) {
return;
}
try {
extractArtifactData(artifact);
} 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);
return;
}
updateView();
this.setLayout(this.m_gridBagLayout);
this.revalidate();
this.repaint();
}
@Override
public Component getComponent() {
// Slap a vertical scrollbar on the panel.
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
@Override
public boolean isSupported(BlackboardArtifact artifact) {
return (artifact != null)
&& (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID());
}
/**
* Extracts data from the artifact to be displayed in the panel.
*
* @param artifact Artifact to show.
* @throws TskCoreException
*/
private void extractArtifactData(BlackboardArtifact artifact) throws TskCoreException {
this.contactArtifact = artifact;
phoneNumList = new ArrayList<>();
emailList = new ArrayList<>();
nameList = new ArrayList<>();
otherList = new ArrayList<>();
accountAttributesList = new ArrayList<>();
// Get all the attributes and group them by the section panels they go in
for (BlackboardAttribute bba : contactArtifact.getAttributes()) {
if (bba.getAttributeType().getTypeName().startsWith("TSK_PHONE")) {
phoneNumList.add(bba);
accountAttributesList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_EMAIL")) {
emailList.add(bba);
accountAttributesList.add(bba);
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_NAME")) {
nameList.add(bba);
} else {
otherList.add(bba);
if (bba.getAttributeType().getTypeName().equalsIgnoreCase("TSK_ID")) {
accountAttributesList.add(bba);
}
}
}
datasourceName = contactArtifact.getDataSource().getName();
}
/**
* Updates the view with the data extracted from the artifact.
*/
private void updateView() {
// Update contact name, image, phone numbers
updateContactDetails();
// update artifact source panel
updateSource();
// show a empty Personas panel and kick off a serch for personas
initiatePersonasSearch();
}
/**
* Updates the view with contact's details.
*/
@NbBundle.Messages({
"ContactArtifactViewer_phones_header=Phone",
"ContactArtifactViewer_emails_header=Email",
"ContactArtifactViewer_others_header=Other",})
private void updateContactDetails() {
// update image and name.
updateContactImage(m_gridBagLayout, m_constraints);
updateContactName(m_gridBagLayout, m_constraints);
// update contact attributes sections
updateContactMethodSection(phoneNumList, Bundle.ContactArtifactViewer_phones_header(), m_gridBagLayout, m_constraints);
updateContactMethodSection(emailList, Bundle.ContactArtifactViewer_emails_header(), m_gridBagLayout, m_constraints);
updateContactMethodSection(otherList, Bundle.ContactArtifactViewer_others_header(), m_gridBagLayout, m_constraints);
}
/**
* Updates the contact image in the view.
*
* @param contactPanelLayout Panel layout.
* @param contactPanelConstraints Layout constraints.
*
*/
@NbBundle.Messages({
"ContactArtifactViewer.contactImage.text=",})
private void updateContactImage(GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints) {
// place the image on the top right corner
Insets savedInsets = contactPanelConstraints.insets;
contactPanelConstraints.gridy = 0;
contactPanelConstraints.gridx = 0;
contactPanelConstraints.insets = new Insets(0, 0, 0, 0);
javax.swing.JLabel contactImage = new javax.swing.JLabel();
contactImage.setIcon(getImageFromArtifact(contactArtifact));
contactImage.setText(Bundle.ContactArtifactViewer_contactImage_text());
// add image to top left corner of the page.
CommunicationArtifactViewerHelper.addComponent(this, contactPanelLayout, contactPanelConstraints, contactImage);
CommunicationArtifactViewerHelper.addLineEndGlue(this, contactPanelLayout, contactPanelConstraints);
contactPanelConstraints.gridy++;
contactPanelConstraints.insets = savedInsets;
}
/**
* Updates the contact name in the view.
*
* @param contactPanelLayout Panel layout.
* @param contactPanelConstraints Layout constraints.
*
*/
@NbBundle.Messages({
"ContactArtifactViewer_contactname_unknown=Unknown",})
private void updateContactName(GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints) {
boolean foundName = false;
for (BlackboardAttribute bba : this.nameList) {
if (StringUtils.isEmpty(bba.getValueString()) == false) {
contactName = bba.getDisplayString();
CommunicationArtifactViewerHelper.addHeader(this, contactPanelLayout, contactPanelConstraints, contactName);
foundName = true;
break;
}
}
if (foundName == false) {
CommunicationArtifactViewerHelper.addHeader(this, contactPanelLayout, contactPanelConstraints, Bundle.ContactArtifactViewer_contactname_unknown());
}
}
/**
* Updates the view by displaying the given list of attributes in the given
* section panel.
*
* @param sectionAttributesList List of attributes to display.
* @param sectionHeader Section name label.
* @param contactPanelLayout Panel layout.
* @param contactPanelConstraints Layout constraints.
*
*/
@NbBundle.Messages({
"ContactArtifactViewer_plural_suffix=s",})
private void updateContactMethodSection(List<BlackboardAttribute> sectionAttributesList, String sectionHeader, GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints) {
// If there are no attributes for this section, do nothing
if (sectionAttributesList.isEmpty()) {
return;
}
String sectionHeaderString = sectionHeader;
if (sectionAttributesList.size() > 1) {
sectionHeaderString = sectionHeaderString.concat(Bundle.ContactArtifactViewer_plural_suffix());
}
CommunicationArtifactViewerHelper.addHeader(this, contactPanelLayout, contactPanelConstraints, sectionHeaderString);
for (BlackboardAttribute bba : sectionAttributesList) {
CommunicationArtifactViewerHelper.addKey(this, contactPanelLayout, contactPanelConstraints, bba.getAttributeType().getDisplayName());
CommunicationArtifactViewerHelper.addValue(this, contactPanelLayout, contactPanelConstraints, bba.getDisplayString());
}
}
/**
* Updates the source section.
*/
@NbBundle.Messages({
"ContactArtifactViewer_heading_Source=Source",
"ContactArtifactViewer_label_datasource=Data Source",})
private void updateSource() {
CommunicationArtifactViewerHelper.addHeader(this, this.m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_heading_Source());
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_label_datasource());
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, m_constraints, datasourceName);
}
/**
* Initiates a search for Personas for the accounts associated with the
* Contact.
*
*/
@NbBundle.Messages({
"ContactArtifactViewer_persona_header=Persona",
"ContactArtifactViewer_persona_searching=Searching...",
"ContactArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas.",
"ContactArtifactViewer_persona_unknown=Unknown"
})
private void initiatePersonasSearch() {
// add a section header
JLabel personaHeader = CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_persona_header());
m_constraints.gridy++;
// add a status label
String personaStatusLabelText = CentralRepository.isEnabled()
? Bundle.ContactArtifactViewer_persona_searching()
: Bundle.ContactArtifactViewer_persona_unknown();
this.personaSearchStatusLabel = new javax.swing.JLabel();
personaSearchStatusLabel.setText(personaStatusLabelText);
m_constraints.gridx = 0;
CommunicationArtifactViewerHelper.addComponent(this, m_gridBagLayout, m_constraints, personaSearchStatusLabel);
if (CentralRepository.isEnabled()) {
// Kick off a background task to serach for personas for the contact
personaSearchTask = new ContactPersonaSearcherTask(contactArtifact);
personaSearchTask.execute();
} else {
personaHeader.setEnabled(false);
personaSearchStatusLabel.setEnabled(false);
CommunicationArtifactViewerHelper.addBlankLine(this, m_gridBagLayout, m_constraints);
m_constraints.gridy++;
CommunicationArtifactViewerHelper.addMessageRow(this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
m_constraints.gridy++;
CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints);
}
}
/**
* Updates the Persona panel with the gathered persona information.
*/
private void updatePersonas() {
// Remove the "Searching....." label
this.remove(personaSearchStatusLabel);
m_constraints.gridx = 0;
if (contactUniquePersonasMap.isEmpty()) {
// No persona found - show a button to create one.
showPersona(null, 0, Collections.emptyList(), this.m_gridBagLayout, this.m_constraints);
} else {
int matchCounter = 0;
for (Map.Entry<Persona, ArrayList<CentralRepoAccount>> entry : contactUniquePersonasMap.entrySet()) {
List<CentralRepoAccount> missingAccounts = new ArrayList<>();
ArrayList<CentralRepoAccount> personaAccounts = entry.getValue();
matchCounter++;
// create a list of accounts missing from this persona
for (CentralRepoAccount account : contactUniqueAccountsList) {
if (personaAccounts.contains(account) == false) {
missingAccounts.add(account);
}
}
showPersona(entry.getKey(), matchCounter, missingAccounts, m_gridBagLayout, m_constraints);
m_constraints.gridy += 2;
}
}
// add veritcal glue at the end
CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints);
// redraw the panel
this.setLayout(this.m_gridBagLayout);
this.revalidate();
this.repaint();
}
/**
* Displays the given persona in the persona panel.
*
* @param persona Persona to display.
* @param matchNumber Number of matches.
* @param missingAccountsList List of contact accounts this persona may be
* missing.
* @param gridBagLayout Layout to use.
* @param constraints layout constraints.
*
* @throws CentralRepoException
*/
@NbBundle.Messages({
"ContactArtifactViewer_persona_label=Persona ",
"ContactArtifactViewer_persona_no_match=No matches found",
"ContactArtifactViewer_persona_button_view=View",
"ContactArtifactViewer_persona_button_new=Create",
"ContactArtifactViewer_persona_match_num=Match ",
"ContactArtifactViewer_missing_account_label=Missing contact account",
"ContactArtifactViewer_found_all_accounts_label=All accounts found."
})
private void showPersona(Persona persona, int matchNumber, List<CentralRepoAccount> missingAccountsList, GridBagLayout gridBagLayout, GridBagConstraints constraints) {
// save the original insets
Insets savedInsets = constraints.insets;
// some label are indented 2x to appear indented w.r.t column above
Insets extraIndentInsets = new java.awt.Insets(0, 2 * CommunicationArtifactViewerHelper.LEFT_INSET, 0, 0);
// Add a Match X label in col 0.
constraints.gridx = 0;
javax.swing.JLabel matchNumberLabel = CommunicationArtifactViewerHelper.addKey(this, gridBagLayout, constraints, String.format("%s %d", Bundle.ContactArtifactViewer_persona_match_num(), matchNumber));
javax.swing.JLabel personaNameLabel = new javax.swing.JLabel();
javax.swing.JButton personaButton = new javax.swing.JButton();
String personaName;
String personaButtonText;
ActionListener personaButtonListener;
if (persona != null) {
personaName = persona.getName();
personaButtonText = Bundle.ContactArtifactViewer_persona_button_view();
personaButtonListener = new ViewPersonaButtonListener(this, persona);
} else {
matchNumberLabel.setVisible(false);
personaName = Bundle.ContactArtifactViewer_persona_no_match();
personaButtonText = Bundle.ContactArtifactViewer_persona_button_new();
personaButtonListener = new CreatePersonaButtonListener(this, new PersonaUIComponents(personaNameLabel, personaButton));
}
//constraints.gridwidth = 1; // TBD: this may not be needed if we use single panel
constraints.gridx++;
personaNameLabel.setText(personaName);
gridBagLayout.setConstraints(personaNameLabel, constraints);
CommunicationArtifactViewerHelper.addComponent(this, gridBagLayout, constraints, personaNameLabel);
//personasPanel.add(personaNameLabel);
// Add a Persona action button
constraints.gridx++;
//constraints.gridwidth = 1;
personaButton.setText(personaButtonText);
personaButton.addActionListener(personaButtonListener);
// Shirnk the button height.
personaButton.setMargin(new Insets(0, 5, 0, 5));
gridBagLayout.setConstraints(personaButton, constraints);
CommunicationArtifactViewerHelper.addComponent(this, gridBagLayout, constraints, personaButton);
CommunicationArtifactViewerHelper.addLineEndGlue(this, gridBagLayout, constraints);
constraints.insets = savedInsets;
// if we have a persona, indicate if any of the contact's accounts are missing from it.
if (persona != null) {
if (missingAccountsList.isEmpty()) {
constraints.gridy++;
constraints.gridx = 1;
//constraints.insets = labelInsets;
javax.swing.JLabel accountsStatus = new javax.swing.JLabel(Bundle.ContactArtifactViewer_found_all_accounts_label());
constraints.insets = extraIndentInsets;
CommunicationArtifactViewerHelper.addComponent(this, gridBagLayout, constraints, accountsStatus);
constraints.insets = savedInsets;
CommunicationArtifactViewerHelper.addLineEndGlue(this, gridBagLayout, constraints);
} else {
// show missing accounts.
for (CentralRepoAccount missingAccount : missingAccountsList) {
//constraints.weightx = 0;
constraints.gridx = 0;
constraints.gridy++;
// this needs an extra indent
constraints.insets = extraIndentInsets;
CommunicationArtifactViewerHelper.addKeyAtCol(this, gridBagLayout, constraints, Bundle.ContactArtifactViewer_missing_account_label(), 1);
constraints.insets = savedInsets;
CommunicationArtifactViewerHelper.addValueAtCol(this, gridBagLayout, constraints, missingAccount.getIdentifier(), 2);
}
}
}
// restore insets
constraints.insets = savedInsets;
}
/**
* Resets all artifact specific state.
*/
private void resetComponent() {
contactArtifact = null;
contactName = null;
datasourceName = null;
contactUniqueAccountsList.clear();
contactUniquePersonasMap.clear();
phoneNumList.clear();
emailList.clear();
nameList.clear();
otherList.clear();
accountAttributesList.clear();
if (personaSearchTask != null) {
personaSearchTask.cancel(Boolean.TRUE);
personaSearchTask = null;
}
// clear the panel
this.removeAll();
this.setLayout(null);
m_gridBagLayout = new GridBagLayout();
m_constraints = new GridBagConstraints();
m_constraints.anchor = GridBagConstraints.FIRST_LINE_START;
m_constraints.gridy = 0;
m_constraints.gridx = 0;
m_constraints.weighty = 0.0;
m_constraints.weightx = 0.0; // keep components fixed horizontally.
m_constraints.insets = new java.awt.Insets(0, CommunicationArtifactViewerHelper.LEFT_INSET, 0, 0);
m_constraints.fill = GridBagConstraints.NONE;
}
/**
* Gets an image from a TSK_CONTACT artifact.
*
* @param artifact
*
* @return Image from a TSK_CONTACT artifact or default image if none was
* found or the artifact is not a TSK_CONTACT
*/
private ImageIcon getImageFromArtifact(BlackboardArtifact artifact) {
ImageIcon imageIcon = defaultImage;
if (artifact == null) {
return imageIcon;
}
BlackboardArtifact.ARTIFACT_TYPE artifactType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
if (artifactType != BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT) {
return imageIcon;
}
try {
for (Content content : artifact.getChildren()) {
if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile) content;
try {
BufferedImage image = ImageIO.read(new File(file.getLocalAbsPath()));
imageIcon = new ImageIcon(image);
break;
} catch (IOException ex) {
// ImageIO.read will throw an IOException if file is not an image
// therefore we don't need to report this exception just try
// the next file.
}
}
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, String.format("Unable to load image for contact: %d", artifact.getId()), ex);
}
return imageIcon;
}
/**
* Thread to search for a personas for all account identifier attributes for
* a contact.
*/
private class ContactPersonaSearcherTask extends SwingWorker<Map<Persona, ArrayList<CentralRepoAccount>>, Void> {
private final BlackboardArtifact artifact;
private final List<CentralRepoAccount> uniqueAccountsList = new ArrayList<>();
/**
* Creates a persona searcher task.
*
* @param accountAttributesList List of attributes that may map to
* accounts.
*/
ContactPersonaSearcherTask(BlackboardArtifact artifact) {
this.artifact = artifact;
}
@Override
protected Map<Persona, ArrayList<CentralRepoAccount>> doInBackground() throws Exception {
Map<Persona, ArrayList<CentralRepoAccount>> uniquePersonas = new HashMap<>();
CommunicationsManager commManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager();
List<Account> contactAccountsList = commManager.getAccountsRelatedToArtifact(artifact);
for (Account account : contactAccountsList) {
if (isCancelled()) {
return new HashMap<>();
}
Collection<PersonaAccount> personaAccounts = PersonaAccount.getPersonaAccountsForAccount(account);
if (personaAccounts != null && !personaAccounts.isEmpty()) {
// look for unique accounts
Collection<CentralRepoAccount> accountCandidates
= personaAccounts
.stream()
.map(PersonaAccount::getAccount)
.collect(Collectors.toList());
for (CentralRepoAccount crAccount : accountCandidates) {
if (uniqueAccountsList.contains(crAccount) == false) {
uniqueAccountsList.add(crAccount);
}
}
// get personas for the account
Collection<Persona> personas
= personaAccounts
.stream()
.map(PersonaAccount::getPersona)
.collect(Collectors.toList());
// make a list of unique personas, along with all their accounts
for (Persona persona : personas) {
if (uniquePersonas.containsKey(persona) == false) {
Collection<CentralRepoAccount> accounts = persona.getPersonaAccounts()
.stream()
.map(PersonaAccount::getAccount)
.collect(Collectors.toList());
ArrayList<CentralRepoAccount> personaAccountsList = new ArrayList<>(accounts);
uniquePersonas.put(persona, personaAccountsList);
}
}
}
}
return uniquePersonas;
}
@Override
protected void done() {
Map<Persona, ArrayList<CentralRepoAccount>> personasMap;
try {
personasMap = super.get();
if (this.isCancelled()) {
return;
}
contactUniquePersonasMap.clear();
contactUniquePersonasMap.putAll(personasMap);
contactUniqueAccountsList.clear();
contactUniqueAccountsList.addAll(uniqueAccountsList);
updatePersonas();
} catch (CancellationException ex) {
logger.log(Level.INFO, "Persona searching was canceled."); //NON-NLS
} catch (InterruptedException ex) {
logger.log(Level.INFO, "Persona searching was interrupted."); //NON-NLS
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, "Fatal error during Persona search.", ex); //NON-NLS
}
}
}
/**
* A wrapper class that bags the UI components that need to be updated when
* a persona search task or a create dialog returns.
*/
private class PersonaUIComponents {
private final JLabel personaNameLabel;
private final JButton personaActionButton;
/**
* Constructor.
*
* @param personaNameLabel Persona name label.
* @param personaActionButton Persona action button.
*/
PersonaUIComponents(JLabel personaNameLabel, JButton personaActionButton) {
this.personaNameLabel = personaNameLabel;
this.personaActionButton = personaActionButton;
}
/**
* Returns persona name label.
*
* @return Persona name label.
*/
public JLabel getPersonaNameLabel() {
return personaNameLabel;
}
/**
* Returns persona action button.
*
* @return Persona action button.
*/
public JButton getPersonaActionButton() {
return personaActionButton;
}
}
/**
* Action listener for Create persona button.
*/
private class CreatePersonaButtonListener implements ActionListener {
private final Component parentComponent;
private final PersonaUIComponents personaUIComponents;
/**
* Constructs a listener for Create persona button..
*
* @param personaUIComponents UI components.
*/
CreatePersonaButtonListener(Component parentComponent, PersonaUIComponents personaUIComponents) {
this.personaUIComponents = personaUIComponents;
this.parentComponent = parentComponent;
}
@NbBundle.Messages({
"ContactArtifactViewer_persona_account_justification=Account found in Contact artifact"
})
@Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
// Launch the Persona Create dialog - do not display immediately
PersonaDetailsDialog createPersonaDialog = new PersonaDetailsDialog(parentComponent,
PersonaDetailsMode.CREATE, null, new PersonaCreateCallbackImpl(parentComponent, personaUIComponents), false);
// Pre populate the persona name and accounts if we have them.
PersonaDetailsPanel personaPanel = createPersonaDialog.getDetailsPanel();
if (contactName != null) {
personaPanel.setPersonaName(contactName);
}
// pass the list of accounts to the dialog
for (CentralRepoAccount account : contactUniqueAccountsList) {
personaPanel.addAccount(account, Bundle.ContactArtifactViewer_persona_account_justification(), Persona.Confidence.HIGH);
}
// display the dialog now
createPersonaDialog.display();
}
}
/**
* Action listener for View persona button.
*/
private class ViewPersonaButtonListener implements ActionListener {
private final Persona persona;
private final Component parentComponent;
/**
* Creates listener for View persona button.
*
* @param persona
*/
ViewPersonaButtonListener(Component parentComponent, Persona persona) {
this.persona = persona;
this.parentComponent = parentComponent;
}
@Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
new PersonaDetailsDialog(parentComponent,
PersonaDetailsMode.VIEW, persona, new PersonaViewCallbackImpl());
}
}
/**
* Callback method for the create mode of the PersonaDetailsDialog
*/
class PersonaCreateCallbackImpl implements PersonaDetailsDialogCallback {
private final Component parentComponent;
private final PersonaUIComponents personaUIComponents;
/**
* Creates a callback to handle new persona creation.
*
* @param personaUIComponents UI Components.
*/
PersonaCreateCallbackImpl(Component parentComponent, PersonaUIComponents personaUIComponents) {
this.parentComponent = parentComponent;
this.personaUIComponents = personaUIComponents;
}
@Override
public void callback(Persona persona) {
JButton personaButton = personaUIComponents.getPersonaActionButton();
if (persona != null) {
// update the persona name label with newly created persona,
// and change the button to a "View" button
personaUIComponents.getPersonaNameLabel().setText(persona.getName());
personaUIComponents.getPersonaActionButton().setText(Bundle.ContactArtifactViewer_persona_button_view());
// replace action listener with a View button listener
for (ActionListener act : personaButton.getActionListeners()) {
personaButton.removeActionListener(act);
}
personaButton.addActionListener(new ViewPersonaButtonListener(parentComponent, persona));
}
personaButton.getParent().revalidate();
personaButton.getParent().repaint();
}
}
/**
* Callback method for the view mode of the PersonaDetailsDialog
*/
class PersonaViewCallbackImpl implements PersonaDetailsDialogCallback {
@Override
public void callback(Persona persona) {
// nothing to do
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -11,14 +11,14 @@
<MenuItem class="javax.swing.JMenuItem" name="copyMenuItem"> <MenuItem class="javax.swing.JMenuItem" name="copyMenuItem">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="DefaultArtifactContentViewer.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="DefaultArtifactContentViewer.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</MenuItem> </MenuItem>
<MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem"> <MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="DefaultArtifactContentViewer.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="DefaultArtifactContentViewer.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</MenuItem> </MenuItem>

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.contentviewers; package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Component; import java.awt.Component;
import java.awt.Cursor; import java.awt.Cursor;
@ -54,6 +54,7 @@ import com.google.gson.JsonArray;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
//import org.sleuthkit.autopsy.contentviewers.Bundle;
/** /**
* This class displays a Blackboard artifact as a table listing all it's * This class displays a Blackboard artifact as a table listing all it's

View File

@ -17,7 +17,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.contentviewers; package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;

View File

@ -120,7 +120,7 @@
<Component class="javax.swing.JLabel" name="fromLabel"> <Component class="javax.swing.JLabel" name="fromLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.fromLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.fromLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -128,28 +128,28 @@
<Properties> <Properties>
<Property name="horizontalAlignment" type="int" value="4"/> <Property name="horizontalAlignment" type="int" value="4"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.datetimeText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.datetimeText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="fromText"> <Component class="javax.swing.JLabel" name="fromText">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.fromText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.fromText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="toLabel"> <Component class="javax.swing.JLabel" name="toLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.toLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.toLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="toText"> <Component class="javax.swing.JLabel" name="toText">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.toText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.toText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="autoscrolls" type="boolean" value="true"/> <Property name="autoscrolls" type="boolean" value="true"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
@ -160,14 +160,14 @@
<Component class="javax.swing.JLabel" name="ccLabel"> <Component class="javax.swing.JLabel" name="ccLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.ccLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.ccLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="ccText"> <Component class="javax.swing.JLabel" name="ccText">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.ccText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.ccText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 14]"/> <Dimension value="[27, 14]"/>
@ -177,14 +177,14 @@
<Component class="javax.swing.JLabel" name="subjectLabel"> <Component class="javax.swing.JLabel" name="subjectLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.subjectLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.subjectLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="subjectText"> <Component class="javax.swing.JLabel" name="subjectText">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.subjectText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.subjectText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[26, 14]"/> <Dimension value="[26, 14]"/>
@ -195,7 +195,7 @@
<Properties> <Properties>
<Property name="horizontalAlignment" type="int" value="4"/> <Property name="horizontalAlignment" type="int" value="4"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.directionText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.directionText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
@ -214,7 +214,7 @@
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
<JTabbedPaneConstraints tabName="Headers"> <JTabbedPaneConstraints tabName="Headers">
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.headersScrollPane.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.headersScrollPane.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</JTabbedPaneConstraints> </JTabbedPaneConstraints>
</Constraint> </Constraint>
@ -238,7 +238,7 @@
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
<JTabbedPaneConstraints tabName="HTML"> <JTabbedPaneConstraints tabName="HTML">
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.htmlPane.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.htmlPane.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</JTabbedPaneConstraints> </JTabbedPaneConstraints>
</Constraint> </Constraint>
@ -254,7 +254,7 @@
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
<JTabbedPaneConstraints tabName="RTF"> <JTabbedPaneConstraints tabName="RTF">
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.rtfbodyScrollPane.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</JTabbedPaneConstraints> </JTabbedPaneConstraints>
</Constraint> </Constraint>
@ -274,7 +274,7 @@
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
<JTabbedPaneConstraints tabName="Attachments"> <JTabbedPaneConstraints tabName="Attachments">
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.attachmentsPanel.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</JTabbedPaneConstraints> </JTabbedPaneConstraints>
</Constraint> </Constraint>
@ -311,7 +311,7 @@
<Component class="javax.swing.JButton" name="viewInNewWindowButton"> <Component class="javax.swing.JButton" name="viewInNewWindowButton">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.viewInNewWindowButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.viewInNewWindowButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events> <Events>
@ -329,7 +329,7 @@
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
<JTabbedPaneConstraints tabName="Accounts"> <JTabbedPaneConstraints tabName="Accounts">
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageArtifactViewer.accountsTab.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties" key="MessageArtifactViewer.accountsTab.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</JTabbedPaneConstraints> </JTabbedPaneConstraints>
</Constraint> </Constraint>

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.contentviewers; package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import org.sleuthkit.autopsy.datamodel.AttachmentNode; import org.sleuthkit.autopsy.datamodel.AttachmentNode;
import java.awt.Color; import java.awt.Color;
@ -41,7 +41,9 @@ import org.openide.nodes.Node;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.contentviewers.TranslatablePanel;
import org.sleuthkit.autopsy.contentviewers.TranslatablePanel.TranslatablePanelException; import org.sleuthkit.autopsy.contentviewers.TranslatablePanel.TranslatablePanelException;
import org.sleuthkit.autopsy.contentviewers.Utilities;
import org.sleuthkit.autopsy.corecomponents.AutoWrappingJTextPane; import org.sleuthkit.autopsy.corecomponents.AutoWrappingJTextPane;
import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.corecomponents.TableFilterNode;

View File

@ -1,4 +1,4 @@
/** /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020 Basis Technology Corp.
@ -16,122 +16,150 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.contentviewers; package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Component; import java.awt.Component;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog; import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback; import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback;
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode; import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.CommunicationsManager;
/** /**
* Background task to search for a persona for a given account. * SwingWorker for fetching and updating Persona controls.
* */
* When the search is complete, it updates the UI components class PersonaAccountFetcher extends SwingWorker<Map<String, Collection<Persona>>, Void> {
* for the persona appropriately.
*
*/
@NbBundle.Messages({ private final static Logger logger = Logger.getLogger(PersonaAccountFetcher.class.getName());
private final BlackboardArtifact artifact;
private final List<AccountPersonaSearcherData> personaSearchDataList;
private final Component parentComponent;
/**
* Construct the SwingWorker.
*
* @param artifact The artifact to search account for.
* @param personaSearchDataList List of PersonaSerarcherData objects.
* @param parentComponent The parent panel.
*/
PersonaAccountFetcher(BlackboardArtifact artifact, List<AccountPersonaSearcherData> personaSearchDataList, Component parentComponent) {
this.artifact = artifact;
this.personaSearchDataList = personaSearchDataList;
this.parentComponent = parentComponent;
}
@Override
protected Map<String, Collection<Persona>> doInBackground() throws Exception {
Map<String, Collection<Persona>> accountMap = new HashMap<>();
CommunicationsManager commManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager();
List<Account> relatedAccountList = commManager.getAccountsRelatedToArtifact(artifact);
for (Account account : relatedAccountList) {
if (isCancelled()) {
return new HashMap<>();
}
Collection<PersonaAccount> personaAccountList = PersonaAccount.getPersonaAccountsForAccount(account);
Collection<Persona> personaList = new ArrayList<>();
for (PersonaAccount pAccount : personaAccountList) {
personaList.add(pAccount.getPersona());
}
accountMap.put(account.getTypeSpecificID(), personaList);
}
return accountMap;
}
@Override
protected void done() {
if (isCancelled()) {
return;
}
try {
Map<String, Collection<Persona>> accountMap = get();
for (AccountPersonaSearcherData searcherData : personaSearchDataList) {
Collection<Persona> persona = accountMap.get(searcherData.getAccountIdentifer());
updatePersonaControls(searcherData, persona);
}
} catch (CancellationException ex) {
logger.log(Level.INFO, "Persona searching was canceled."); //NON-NLS
} catch (InterruptedException ex) {
logger.log(Level.INFO, "Persona searching was interrupted."); //NON-NLS
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, "Fatal error during Persona search.", ex); //NON-NLS
}
parentComponent.repaint();
}
@Messages({
"# {0} - Persona count", "# {0} - Persona count",
"PersonaDisplayTask_persona_count_suffix=(1 of {0})" "PersonaDisplayTask_persona_count_suffix=(1 of {0})"
}) })
class PersonaSearchAndDisplayTask extends SwingWorker<Collection<Persona>, Void> {
private final static Logger logger = Logger.getLogger(PersonaSearchAndDisplayTask.class.getName()); /**
* Update the Persona gui controls.
private final Component parentComponent; *
private final AccountPersonaSearcherData personaSearcherData; * @param personaSearcherData The data objects with persona controls
* @param personas Collection of persona objects
*/
private void updatePersonaControls(AccountPersonaSearcherData personaSearcherData, Collection<Persona> personas) {
//Update the Persona label and button based on the search result
String personaLabelText = Bundle.CommunicationArtifactViewerHelper_persona_label();
String personaButtonText;
ActionListener buttonActionListener;
PersonaSearchAndDisplayTask(Component parentComponent, AccountPersonaSearcherData personaSearcherData) { if (personas == null || personas.isEmpty()) {
this.parentComponent = parentComponent; // No persona found
this.personaSearcherData = personaSearcherData; personaLabelText += Bundle.CommunicationArtifactViewerHelper_persona_unknown();
}
@Override // show a 'Create' button
protected Collection<Persona> doInBackground() throws Exception { personaButtonText = Bundle.CommunicationArtifactViewerHelper_persona_button_create();
buttonActionListener = new CreatePersonaButtonListener(parentComponent, personaSearcherData);
Collection<Persona> personas = new ArrayList<>(); } else {
Persona persona = personas.iterator().next();
if (CentralRepository.isEnabled()) { personaLabelText += persona.getName();
Collection<CentralRepoAccount> accountCandidates if (personas.size() > 1) {
= CentralRepoAccount.getAccountsWithIdentifier(personaSearcherData.getAccountIdentifer()); personaLabelText += Bundle.PersonaDisplayTask_persona_count_suffix(Integer.toString(personas.size()));
if (accountCandidates.isEmpty() == false) {
CentralRepoAccount account = accountCandidates.iterator().next();
// get personas for the account
Collection<PersonaAccount> personaAccountsList = PersonaAccount.getPersonaAccountsForAccount(account.getId());
personas = personaAccountsList.stream().map(PersonaAccount::getPersona)
.collect(Collectors.toList());
}
} }
return personas; // Show a 'View' button
personaButtonText = Bundle.CommunicationArtifactViewerHelper_persona_button_view();
buttonActionListener = new ViewPersonaButtonListener(parentComponent, persona);
} }
@Override personaSearcherData.getPersonaNameLabel().setText(personaLabelText);
protected void done() { personaSearcherData.getPersonaActionButton().setText(personaButtonText);
Collection<Persona> personas; personaSearcherData.getPersonaActionButton().setEnabled(true);
try {
personas = super.get();
if (this.isCancelled()) { // set button action
return; personaSearcherData.getPersonaActionButton().addActionListener(buttonActionListener);
} }
//Update the Persona label and button based on the search result
String personaLabelText = Bundle.CommunicationArtifactViewerHelper_persona_label();
String personaButtonText;
ActionListener buttonActionListener;
if (personas.isEmpty()) {
// No persona found
personaLabelText += Bundle.CommunicationArtifactViewerHelper_persona_unknown();
// show a 'Create' button
personaButtonText = Bundle.CommunicationArtifactViewerHelper_persona_button_create();
buttonActionListener = new CreatePersonaButtonListener(parentComponent, personaSearcherData);
} else {
Persona persona = personas.iterator().next();
personaLabelText += persona.getName();
if (personas.size() > 1) {
personaLabelText += Bundle.PersonaDisplayTask_persona_count_suffix(Integer.toString(personas.size()));
}
// Show a 'View' button
personaButtonText = Bundle.CommunicationArtifactViewerHelper_persona_button_view();
buttonActionListener = new ViewPersonaButtonListener(parentComponent, persona);
}
personaSearcherData.getPersonaNameLabel().setText(personaLabelText);
personaSearcherData.getPersonaActionButton().setText(personaButtonText);
personaSearcherData.getPersonaActionButton().setEnabled(true);
// set button action
personaSearcherData.getPersonaActionButton().addActionListener(buttonActionListener);
} catch (CancellationException ex) {
logger.log(Level.INFO, "Persona searching was canceled."); //NON-NLS
} catch (InterruptedException ex) {
logger.log(Level.INFO, "Persona searching was interrupted."); //NON-NLS
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, "Fatal error during Persona search.", ex); //NON-NLS
}
}
/** /**
* Action listener for Create persona button. * Action listener for Create persona button.
*/ */
@ -177,7 +205,7 @@ class PersonaSearchAndDisplayTask extends SwingWorker<Collection<Persona>, Void>
* Callback method for the create mode of the PersonaDetailsDialog * Callback method for the create mode of the PersonaDetailsDialog
*/ */
class PersonaCreateCallbackImpl implements PersonaDetailsDialogCallback { class PersonaCreateCallbackImpl implements PersonaDetailsDialogCallback {
private final Component parentComponent; private final Component parentComponent;
private final AccountPersonaSearcherData personaSearcherData; private final AccountPersonaSearcherData personaSearcherData;
@ -217,4 +245,5 @@ class PersonaSearchAndDisplayTask extends SwingWorker<Collection<Persona>, Void>
// nothing to do // nothing to do
} }
} }
} }

View File

@ -33,6 +33,8 @@ import java.util.logging.Handler;
import java.util.logging.Level; import java.util.logging.Level;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.embed.swing.JFXPanel; import javafx.embed.swing.JFXPanel;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.modules.InstalledFileLocator; import org.openide.modules.InstalledFileLocator;
@ -336,8 +338,8 @@ public class Installer extends ModuleInstall {
} }
/** /**
* Make a folder in the config directory for object detection classifiers if one does not * Make a folder in the config directory for object detection classifiers if
* exist. * one does not exist.
*/ */
private static void ensureClassifierFolderExists() { private static void ensureClassifierFolderExists() {
File objectDetectionClassifierDir = new File(PlatformUtil.getObjectDetectionClassifierPath()); File objectDetectionClassifierDir = new File(PlatformUtil.getObjectDetectionClassifierPath());
@ -381,6 +383,7 @@ public class Installer extends ModuleInstall {
ensureClassifierFolderExists(); ensureClassifierFolderExists();
ensureOcrLanguagePacksFolderExists(); ensureOcrLanguagePacksFolderExists();
initJavaFx(); initJavaFx();
initializeSevenZip();
for (ModuleInstall mi : packageInstallers) { for (ModuleInstall mi : packageInstallers) {
try { try {
mi.restored(); mi.restored();
@ -393,8 +396,21 @@ public class Installer extends ModuleInstall {
logger.log(Level.INFO, "Autopsy Core restore completed"); //NON-NLS logger.log(Level.INFO, "Autopsy Core restore completed"); //NON-NLS
preloadJython(); preloadJython();
} }
/**
* Initializes 7zip-java bindings. We are performing initialization once
* because we encountered issues related to file locking when initialization
* was performed closer to where the bindings are used. See JIRA-6528.
*/
private void initializeSevenZip() {
try {
SevenZip.initSevenZipFromPlatformJAR();
logger.log(Level.INFO, "7zip-java bindings loaded"); //NON-NLS
} catch (SevenZipNativeInitializationException e) {
logger.log(Level.SEVERE, "Error loading 7zip-java bindings", e); //NON-NLS
}
}
/** /**
* Runs an initial load of the Jython modules to speed up subsequent loads. * Runs an initial load of the Jython modules to speed up subsequent loads.
*/ */
@ -403,13 +419,12 @@ public class Installer extends ModuleInstall {
try { try {
JythonModuleLoader.getIngestModuleFactories(); JythonModuleLoader.getIngestModuleFactories();
JythonModuleLoader.getGeneralReportModules(); JythonModuleLoader.getGeneralReportModules();
} } catch (Exception ex) {
catch (Exception ex) {
// This is a firewall exception to ensure that any possible exception caused // This is a firewall exception to ensure that any possible exception caused
// by this initial load of the Jython modules are caught and logged. // by this initial load of the Jython modules are caught and logged.
logger.log(Level.SEVERE, "There was an error while doing an initial load of python plugins.", ex); logger.log(Level.SEVERE, "There was an error while doing an initial load of python plugins.", ex);
} }
}; };
new Thread(loader).start(); new Thread(loader).start();
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-2015 Basis Technology Corp. * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -42,65 +42,29 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* This class periodically checks availability of collaboration resources - * Monitors the status of services and publishes events and user notifications
* remote database, remote keyword search server, messaging service - and * when the status of a service changes. The database server, keyword search
* reports status updates to the user in case of a gap in service. * server, and messaging service are considered to be core services in a
* collaborative, multi-user case environment. Additional services can provide
* current status by calling the setServiceStatus() method.
*/ */
public class ServicesMonitor { public class ServicesMonitor {
private AutopsyEventPublisher eventPublisher;
private static final Logger logger = Logger.getLogger(ServicesMonitor.class.getName());
private final ScheduledThreadPoolExecutor periodicTasksExecutor;
private static final String PERIODIC_TASK_THREAD_NAME = "services-monitor-periodic-task-%d"; //NON-NLS
private static final int NUMBER_OF_PERIODIC_TASK_THREADS = 1;
private static final long CRASH_DETECTION_INTERVAL_MINUTES = 15;
private static final Set<String> servicesList = Stream.of(ServicesMonitor.Service.values())
.map(Service::toString)
.collect(Collectors.toSet());
/** /**
* The service monitor maintains a mapping of each service to it's last * An enumeration of the core services in a collaborative, multi-user case
* status update. * environment. The display names provided here can be used to identify the
*/ * service status events published for these services and to directly query
private final ConcurrentHashMap<String, String> statusByService; * the ServicesMonitor for the current status of these services.
/**
* Call constructor on start-up so that the first check of services is done
* as soon as possible.
*/
private static ServicesMonitor instance = new ServicesMonitor();
/**
* List of services that are being monitored. The service names should be
* representative of the service functionality and readable as they get
* logged when service outage occurs.
*/ */
public enum Service { public enum Service {
/**
* Property change event fired when remote case database service status
* changes. New value is set to updated ServiceStatus, old value is
* null.
*/
REMOTE_CASE_DATABASE(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.remoteCaseDatabase.displayName.text")), REMOTE_CASE_DATABASE(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.remoteCaseDatabase.displayName.text")),
/**
* Property change event fired when remote keyword search service status
* changes. New value is set to updated ServiceStatus, old value is
* null.
*/
REMOTE_KEYWORD_SEARCH(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.remoteKeywordSearch.displayName.text")), REMOTE_KEYWORD_SEARCH(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.remoteKeywordSearch.displayName.text")),
/**
* Property change event fired when messaging service status changes.
* New value is set to updated ServiceStatus, old value is null.
*/
MESSAGING(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.messaging.displayName.text")); MESSAGING(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.messaging.displayName.text"));
private final String displayName; private final String displayName;
private Service(String name) { private Service(String displayName) {
this.displayName = name; this.displayName = displayName;
} }
public String getDisplayName() { public String getDisplayName() {
@ -109,121 +73,249 @@ public class ServicesMonitor {
}; };
/** /**
* List of possible service statuses. * An enumeration of the standard service statuses.
*/ */
public enum ServiceStatus { public enum ServiceStatus {
/**
* Service is currently up.
*/
UP, UP,
/**
* Service is currently down.
*/
DOWN DOWN
}; };
private static final Logger logger = Logger.getLogger(ServicesMonitor.class.getName());
private static final String PERIODIC_TASK_THREAD_NAME = "services-monitor-periodic-task-%d"; //NON-NLS
private static final int NUMBER_OF_PERIODIC_TASK_THREADS = 1;
private static final long CRASH_DETECTION_INTERVAL_MINUTES = 15;
private static final Set<String> coreServices = Stream.of(ServicesMonitor.Service.values()).map(Service::toString).collect(Collectors.toSet());
private static ServicesMonitor servicesMonitor = new ServicesMonitor();
private final ScheduledThreadPoolExecutor periodicTasksExecutor;
private final ConcurrentHashMap<String, String> statusByService;
private final AutopsyEventPublisher eventPublisher;
/**
* Gets the services monitor that monitors the status of services and
* publishes events and user notifications when the status of a service
* changes.
*
* @return The services monitor singleton.
*/
public synchronized static ServicesMonitor getInstance() { public synchronized static ServicesMonitor getInstance() {
if (instance == null) { if (servicesMonitor == null) {
instance = new ServicesMonitor(); servicesMonitor = new ServicesMonitor();
} }
return instance; return servicesMonitor;
}
private ServicesMonitor() {
this.eventPublisher = new AutopsyEventPublisher();
this.statusByService = new ConcurrentHashMap<>();
// First check is triggered immediately on current thread.
checkAllServices();
/**
* Start periodic task that check the availability of key collaboration
* services.
*/
periodicTasksExecutor = new ScheduledThreadPoolExecutor(NUMBER_OF_PERIODIC_TASK_THREADS, new ThreadFactoryBuilder().setNameFormat(PERIODIC_TASK_THREAD_NAME).build());
periodicTasksExecutor.scheduleWithFixedDelay(new CrashDetectionTask(), CRASH_DETECTION_INTERVAL_MINUTES, CRASH_DETECTION_INTERVAL_MINUTES, TimeUnit.MINUTES);
} }
/** /**
* Updates service status and publishes the service status update if it is * Constructs a services monitor that monitors the status of services and
* different from previous status. Event is published locally. Logs status * publishes events and user notifications when the status of a service
* changes. * changes.
*/
private ServicesMonitor() {
eventPublisher = new AutopsyEventPublisher();
statusByService = new ConcurrentHashMap<>();
/*
* The first service statuses check is performed immediately in the
* current thread.
*/
checkAllServices();
/**
* Start a periodic task to do ongoing service status checks.
*/
periodicTasksExecutor = new ScheduledThreadPoolExecutor(NUMBER_OF_PERIODIC_TASK_THREADS, new ThreadFactoryBuilder().setNameFormat(PERIODIC_TASK_THREAD_NAME).build());
periodicTasksExecutor.scheduleWithFixedDelay(new ServicesMonitoringTask(), CRASH_DETECTION_INTERVAL_MINUTES, CRASH_DETECTION_INTERVAL_MINUTES, TimeUnit.MINUTES);
}
/**
* Records the status of a service and publishes a service status event if
* the current status is different from the previously reported status.
* *
* @param service Name of the service. * @param service Name of the service.
* @param status Updated status for the service. * @param status Current status of the service.
* @param details Details of the event. * @param details Additional status details.
* *
*/ */
public void setServiceStatus(String service, String status, String details) { public void setServiceStatus(String service, String status, String details) {
// if the status update is for an existing service who's status hasn't changed - do nothing.
if (statusByService.containsKey(service) && status.equals(statusByService.get(service))) { if (statusByService.containsKey(service) && status.equals(statusByService.get(service))) {
return; return;
} }
// new service or status has changed - identify service's display name statusByService.put(service, status);
String serviceDisplayName; String serviceDisplayName;
try { try {
serviceDisplayName = ServicesMonitor.Service.valueOf(service).getDisplayName(); serviceDisplayName = ServicesMonitor.Service.valueOf(service).getDisplayName();
} catch (IllegalArgumentException ignore) { } catch (IllegalArgumentException ignore) {
// custom service that is not listed in ServicesMonitor.Service enum. Use service name as display name.
serviceDisplayName = service; serviceDisplayName = service;
} }
if (status.equals(ServiceStatus.UP.toString())) { if (status.equals(ServiceStatus.UP.toString())) {
logger.log(Level.INFO, "Connection to {0} is up", serviceDisplayName); //NON-NLS logger.log(Level.INFO, "Connection to {0} is up", serviceDisplayName); //NON-NLS
MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"), MessageNotifyUtil.Notify.info(
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"),
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.msg", serviceDisplayName)); NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.msg", serviceDisplayName));
} else if (status.equals(ServiceStatus.DOWN.toString())) { } else if (status.equals(ServiceStatus.DOWN.toString())) {
logger.log(Level.SEVERE, "Failed to connect to {0}. Reason: {1}", new Object[]{serviceDisplayName, details}); //NON-NLS logger.log(Level.SEVERE, "Failed to connect to {0}. Reason: {1}", new Object[]{serviceDisplayName, details}); //NON-NLS
MessageNotifyUtil.Notify.error(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"), MessageNotifyUtil.Notify.error(
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"),
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.msg", serviceDisplayName)); NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.msg", serviceDisplayName));
} else { } else {
logger.log(Level.INFO, "Status for {0} is {1}", new Object[]{serviceDisplayName, status}); //NON-NLS logger.log(Level.INFO, "Status for {0} is {1} ({2})", new Object[]{serviceDisplayName, status}); //NON-NLS
MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.title"), MessageNotifyUtil.Notify.info(
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.msg", new Object[]{serviceDisplayName, status})); NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.title"),
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.msg", new Object[]{serviceDisplayName, status, details}));
} }
// update and publish new status
statusByService.put(service, status);
eventPublisher.publishLocally(new ServiceEvent(service, status, details)); eventPublisher.publishLocally(new ServiceEvent(service, status, details));
} }
/** /**
* Get last status update for a service. * Get last recorded status for a service.
* *
* @param service Name of the service. * @param service Name of the service.
* *
* @return ServiceStatus Status for the service. * @return ServiceStatus Status for the service.
* *
* @throws ServicesMonitorException If service name is null or service * @throws ServicesMonitorException If the service name is unknown to the
* doesn't exist. * services monitor.
*/ */
public String getServiceStatus(String service) throws ServicesMonitorException { public String getServiceStatus(String service) throws ServicesMonitorException {
if (service == null) { if (service == null || service.isEmpty()) {
throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.nullServiceName.excepton.txt")); throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.nullServiceName.excepton.txt"));
} }
// if request is for one of our "core" services - perform an on demand check /*
// to make sure we have the latest status. * If the request is for a core service, perform an "on demand" check to
if (servicesList.contains(service)) { * get the current status.
*/
if (coreServices.contains(service)) {
checkServiceStatus(service); checkServiceStatus(service);
} }
String status = statusByService.get(service); String status = statusByService.get(service);
if (status == null) { if (status == null) {
// no such service
throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.unknownServiceName.excepton.txt", service)); throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.unknownServiceName.excepton.txt", service));
} }
return status; return status;
} }
/** /**
* Performs service availability status check. * Adds a subscriber to service status events for the core services.
* *
* @param service Name of the service. * @param subscriber The subscriber to add.
*/
public void addSubscriber(PropertyChangeListener subscriber) {
eventPublisher.addSubscriber(coreServices, subscriber);
}
/**
* Adds a subscriber to service status events for a subset of the services
* known to the services monitor.
*
* @param services The services the subscriber is interested in.
* @param subscriber The subscriber to add.
*/
public void addSubscriber(Set<String> services, PropertyChangeListener subscriber) {
eventPublisher.addSubscriber(services, subscriber);
}
/**
* Adds a subscriber to service status events for a specific service known
* to the services monitor.
*
* @param service The service the subscriber is interested in.
* @param subscriber The subscriber to add.
*/
public void addSubscriber(String service, PropertyChangeListener subscriber) {
eventPublisher.addSubscriber(service, subscriber);
}
/**
* Removes a subscriber to service status events for the core services.
*
* @param subscriber The subscriber to remove.
*/
public void removeSubscriber(PropertyChangeListener subscriber) {
eventPublisher.removeSubscriber(coreServices, subscriber);
}
/**
* Removes a subscriber to service status events for a subset of the
* services known to the services monitor.
*
* @param services The services the subscriber is no longer interested in.
* @param subscriber The subscriber to remove.
*/
public void removeSubscriber(Set<String> services, PropertyChangeListener subscriber) {
eventPublisher.removeSubscriber(services, subscriber);
}
/**
* Adds a subscriber to service status events for a specific service known
* to the services monitor.
*
* @param service The service the subscriber is no longer interested in.
* @param subscriber The subscriber to remove.
*/
public void removeSubscriber(String service, PropertyChangeListener subscriber) {
eventPublisher.removeSubscriber(service, subscriber);
}
/**
* Checks the status of the core services in a collaborative, multi-user
* case environment: the database server, the keyword search server and the
* messaging service. Publishes a service event and user notification if the
* status of a service has changed since the last check.
*/
private void checkAllServices() {
if (!UserPreferences.getIsMultiUserModeEnabled()) {
return;
}
for (String service : coreServices) {
checkServiceStatus(service);
}
}
/**
* A task that checks the status of the core services in a collaborative,
* multi-user case environment: the database server, the keyword search
* server and the messaging service. Publishes a service event and user
* notification if the status of a service has changed since the last check.
*/
private final class ServicesMonitoringTask implements Runnable {
@Override
public void run() {
try {
checkAllServices();
} catch (Exception ex) { // Exception firewall
logger.log(Level.SEVERE, "An error occurred during services monitoring", ex); //NON-NLS
}
}
}
/**
* Exception thrown if an error occurs during a service status query.
*/
public class ServicesMonitorException extends Exception {
private static final long serialVersionUID = 1L;
public ServicesMonitorException(String message) {
super(message);
}
public ServicesMonitorException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* Performs a core service availability status check.
*
* @param service Name of the service to check.
*/ */
private void checkServiceStatus(String service) { private void checkServiceStatus(String service) {
if (service.equals(Service.REMOTE_CASE_DATABASE.toString())) { if (service.equals(Service.REMOTE_CASE_DATABASE.toString())) {
@ -236,7 +328,7 @@ public class ServicesMonitor {
} }
/** /**
* Performs case database service availability status check. * Performs a database server availability status check.
*/ */
private void checkDatabaseConnectionStatus() { private void checkDatabaseConnectionStatus() {
CaseDbConnectionInfo info; CaseDbConnectionInfo info;
@ -256,7 +348,7 @@ public class ServicesMonitor {
} }
/** /**
* Performs keyword search service availability status check. * Performs a keyword search service availability status check.
*/ */
private void checkKeywordSearchServerConnectionStatus() { private void checkKeywordSearchServerConnectionStatus() {
KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
@ -303,113 +395,4 @@ public class ServicesMonitor {
} }
} }
/**
* Adds an event subscriber to this publisher. Subscriber will be subscribed
* to all events from this publisher.
*
* @param subscriber The subscriber to add.
*/
public void addSubscriber(PropertyChangeListener subscriber) {
eventPublisher.addSubscriber(servicesList, subscriber);
}
/**
* Adds an event subscriber to this publisher.
*
* @param eventNames The events the subscriber is interested in.
* @param subscriber The subscriber to add.
*/
public void addSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
eventPublisher.addSubscriber(eventNames, subscriber);
}
/**
* Adds an event subscriber to this publisher.
*
* @param eventName The event the subscriber is interested in.
* @param subscriber The subscriber to add.
*/
public void addSubscriber(String eventName, PropertyChangeListener subscriber) {
eventPublisher.addSubscriber(eventName, subscriber);
}
/**
* Removes an event subscriber from this publisher.
*
* @param eventNames The events the subscriber is no longer interested in.
* @param subscriber The subscriber to remove.
*/
public void removeSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
eventPublisher.removeSubscriber(eventNames, subscriber);
}
/**
* Removes an event subscriber from this publisher.
*
* @param eventName The event the subscriber is no longer interested in.
* @param subscriber The subscriber to remove.
*/
public void removeSubscriber(String eventName, PropertyChangeListener subscriber) {
eventPublisher.removeSubscriber(eventName, subscriber);
}
/**
* Removes an event subscriber to this publisher. Subscriber will be removed
* from all event notifications from this publisher.
*
* @param subscriber The subscriber to remove.
*/
public void removeSubscriber(PropertyChangeListener subscriber) {
eventPublisher.removeSubscriber(servicesList, subscriber);
}
/**
* Verifies connectivity to all services.
*/
private void checkAllServices() {
if (!UserPreferences.getIsMultiUserModeEnabled()) {
return;
}
for (String service : servicesList) {
checkServiceStatus(service);
}
}
/**
* A Runnable task that periodically checks the availability of
* collaboration resources (remote database, remote keyword search service,
* message broker) and reports status to the user in case of a gap in
* service.
*/
private final class CrashDetectionTask implements Runnable {
/**
* Monitor the availability of collaboration resources
*/
@Override
public void run() {
try {
checkAllServices();
} catch (Exception ex) {
logger.log(Level.SEVERE, "Unexpected exception in CrashDetectionTask", ex); //NON-NLS
}
}
}
/**
* Exception thrown when service status query results in an error.
*/
public class ServicesMonitorException extends Exception {
private static final long serialVersionUID = 1L;
public ServicesMonitorException(String message) {
super(message);
}
public ServicesMonitorException(String message, Throwable cause) {
super(message, cause);
}
}
} }

View File

@ -450,6 +450,7 @@
<file name="cvt.wsmode" url="cvtWsmode.xml"/> <file name="cvt.wsmode" url="cvtWsmode.xml"/>
<file name="discovery.wsmode" url="discoveryWsmode.xml"/> <file name="discovery.wsmode" url="discoveryWsmode.xml"/>
<file name="geolocation.wsmode" url="geolocationWsmode.xml"/> <file name="geolocation.wsmode" url="geolocationWsmode.xml"/>
<file name="personas.wsmode" url="personasWsmode.xml"/>
</folder> </folder>
</folder> </folder>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<mode version="2.4">
<name unique="personas"/>
<kind type="editor"/>
<state type="separated"/>
<bounds x="76" y="68" width="1200" height="800"/>
<frame state="0"/>
<empty-behavior permanent="false"/>
</mode>

View File

@ -40,8 +40,8 @@ import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.TskException;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import org.sleuthkit.autopsy.contentviewers.ArtifactContentViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.ArtifactContentViewer;
import org.sleuthkit.autopsy.contentviewers.DefaultArtifactContentViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.DefaultArtifactContentViewer;
/** /**
* Instances of this class display the BlackboardArtifacts associated with the * Instances of this class display the BlackboardArtifacts associated with the

View File

@ -63,7 +63,6 @@ import org.sleuthkit.datamodel.TskCoreException;
public class ExtractedContent implements AutopsyVisitableItem { public class ExtractedContent implements AutopsyVisitableItem {
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED); private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text"); public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text");
private final long filteringDSObjId; // 0 if not filtering/grouping by data source private final long filteringDSObjId; // 0 if not filtering/grouping by data source
private SleuthkitCase skCase; // set to null after case has been closed private SleuthkitCase skCase; // set to null after case has been closed
@ -145,12 +144,19 @@ public class ExtractedContent implements AutopsyVisitableItem {
* This area has all of the blackboard artifacts that are not displayed in a * This area has all of the blackboard artifacts that are not displayed in a
* more specific form elsewhere in the tree. * more specific form elsewhere in the tree.
*/ */
private class TypeFactory extends ChildFactory.Detachable<BlackboardArtifact.Type> { private class TypeFactory extends ChildFactory.Detachable<BlackboardArtifact.Type> implements RefreshThrottler.Refresher {
private final ArrayList<BlackboardArtifact.Type> doNotShow = new ArrayList<>(); private final ArrayList<BlackboardArtifact.Type> doNotShow = new ArrayList<>();
// maps the artifact type to its child node // maps the artifact type to its child node
private final HashMap<BlackboardArtifact.Type, TypeNode> typeNodeList = new HashMap<>(); private final HashMap<BlackboardArtifact.Type, TypeNode> typeNodeList = new HashMap<>();
/**
* RefreshThrottler is used to limit the number of refreshes performed
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
* received.
*/
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
TypeFactory() { TypeFactory() {
super(); super();
@ -173,27 +179,11 @@ public class ExtractedContent implements AutopsyVisitableItem {
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
/** // case was closed. Remove listeners so that we don't get called with a stale case handle
* This is a stop gap measure until a different way of handling if (evt.getNewValue() == null) {
* the closing of cases is worked out. Currently, remote events removeNotify();
* may be received for a case that is already closed. skCase = null;
*/
try {
Case.getCurrentCaseThrows();
/**
* Due to some unresolved issues with how cases are closed,
* it is possible for the event to have a null oldValue if
* the event is a remote event.
*/
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
if (null != event && !(this.doNotShow.contains(event.getBlackboardArtifactType()))) {
refresh(true);
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
} }
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
@ -204,32 +194,26 @@ public class ExtractedContent implements AutopsyVisitableItem {
*/ */
try { try {
Case.getCurrentCaseThrows(); Case.getCurrentCaseThrows();
refresh(true); refresh(false);
} catch (NoCurrentCaseException notUsed) { } catch (NoCurrentCaseException notUsed) {
/** /**
* Case is closed, do nothing. * Case is closed, do nothing.
*/ */
} }
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
// case was closed. Remove listeners so that we don't get called with a stale case handle
if (evt.getNewValue() == null) {
removeNotify();
skCase = null;
}
} }
}; };
@Override @Override
protected void addNotify() { protected void addNotify() {
refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
} }
@Override @Override
protected void removeNotify() { protected void removeNotify() {
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
typeNodeList.clear(); typeNodeList.clear();
} }
@ -273,6 +257,40 @@ public class ExtractedContent implements AutopsyVisitableItem {
typeNodeList.put(key, node); typeNodeList.put(key, node);
return node; return node;
} }
@Override
public void refresh() {
refresh(false);
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
/**
* This is a stop gap measure until a different way of handling
* the closing of cases is worked out. Currently, remote events
* may be received for a case that is already closed.
*/
try {
Case.getCurrentCaseThrows();
/**
* Due to some unresolved issues with how cases are closed,
* it is possible for the event to have a null oldValue if
* the event is a remote event.
*/
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
if (null != event && !(this.doNotShow.contains(event.getBlackboardArtifactType()))) {
return true;
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
return false;
}
} }
/** /**
@ -355,73 +373,53 @@ public class ExtractedContent implements AutopsyVisitableItem {
/** /**
* Creates children for a given artifact type * Creates children for a given artifact type
*/ */
private class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> { private class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> implements RefreshThrottler.Refresher {
private BlackboardArtifact.Type type; private final BlackboardArtifact.Type type;
/**
* RefreshThrottler is used to limit the number of refreshes performed
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
* received.
*/
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
ArtifactFactory(BlackboardArtifact.Type type) { ArtifactFactory(BlackboardArtifact.Type type) {
super(type.getTypeName()); super(type.getTypeName());
this.type = type; this.type = type;
} }
private final PropertyChangeListener pcl = new PropertyChangeListener() { private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
@Override String eventType = evt.getPropertyName();
public void propertyChange(PropertyChangeEvent evt) { if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
String eventType = evt.getPropertyName(); || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { /**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked out.
* Currently, remote events may be received for a case that is
* already closed.
*/
try {
Case.getCurrentCaseThrows();
refresh(false);
} catch (NoCurrentCaseException notUsed) {
/** /**
* Checking for a current case is a stop gap measure until a * Case is closed, do nothing.
* different way of handling the closing of cases is worked
* out. Currently, remote events may be received for a case
* that is already closed.
*/ */
try {
Case.getCurrentCaseThrows();
/**
* Even with the check above, it is still possible that
* the case will be closed in a different thread before
* this code executes. If that happens, it is possible
* for the event to have a null oldValue.
*/
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
if (null != event && event.getBlackboardArtifactType().equals(type)) {
refresh(true);
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked
* out. Currently, remote events may be received for a case
* that is already closed.
*/
try {
Case.getCurrentCaseThrows();
refresh(true);
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
} }
} }
}; };
@Override @Override
protected void onAdd() { protected void onAdd() {
refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
} }
@Override @Override
protected void onRemove() { protected void onRemove() {
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
} }
@Override @Override
@ -451,5 +449,43 @@ public class ExtractedContent implements AutopsyVisitableItem {
} }
return Collections.emptyList(); return Collections.emptyList();
} }
@Override
public void refresh() {
refresh(false);
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked out.
* Currently, remote events may be received for a case that is
* already closed.
*/
try {
Case.getCurrentCaseThrows();
/**
* Even with the check above, it is still possible that the
* case will be closed in a different thread before this
* code executes. If that happens, it is possible for the
* event to have a null oldValue.
*/
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
if (null != event && event.getBlackboardArtifactType().equals(type)) {
return true;
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
return false;
}
} }
} }

View File

@ -82,39 +82,26 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
* Listens for case and ingest invest. Updates observers when events are * Listens for case and ingest invest. Updates observers when events are
* fired. FileType and FileTypes nodes are all listening to this. * fired. FileType and FileTypes nodes are all listening to this.
*/ */
private class FileTypesByExtObservable extends Observable { private class FileTypesByExtObservable extends Observable implements RefreshThrottler.Refresher {
private final PropertyChangeListener pcl; private final PropertyChangeListener pcl;
private final Set<Case.Events> CASE_EVENTS_OF_INTEREST; private final Set<Case.Events> CASE_EVENTS_OF_INTEREST;
/**
* RefreshThrottler is used to limit the number of refreshes performed
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
* received.
*/
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
private FileTypesByExtObservable() { private FileTypesByExtObservable() {
super(); super();
this.CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE); this.CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE);
this.pcl = (PropertyChangeEvent evt) -> { this.pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString()) if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())
|| eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
/**
* If a new file has been added but does not have an extension
* there is nothing to do.
*/
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) {
if ((evt.getOldValue() instanceof ModuleContentEvent) == false) {
return;
}
ModuleContentEvent moduleContentEvent = (ModuleContentEvent) evt.getOldValue();
if ((moduleContentEvent.getSource() instanceof AbstractFile) == false) {
return;
}
AbstractFile abstractFile = (AbstractFile) moduleContentEvent.getSource();
if (abstractFile.getNameExtension().isEmpty()) {
return;
}
}
/** /**
* Checking for a current case is a stop gap measure until a * Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked * different way of handling the closing of cases is worked
@ -139,14 +126,14 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
}; };
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl); refreshThrottler.registerForIngestModuleEvents();
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
} }
private void removeListeners() { private void removeListeners() {
deleteObservers(); deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl); refreshThrottler.unregisterEventListener();
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
} }
@ -154,7 +141,52 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
setChanged(); setChanged();
notifyObservers(); notifyObservers();
} }
@Override
public void refresh() {
typesRoot.updateShowCounts();
update();
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked out.
* Currently, remote events may be received for a case that is
* already closed.
*/
try {
Case.getCurrentCaseThrows();
/**
* If a new file has been added but does not have an
* extension there is nothing to do.
*/
if ((evt.getOldValue() instanceof ModuleContentEvent) == false) {
return false;
}
ModuleContentEvent moduleContentEvent = (ModuleContentEvent) evt.getOldValue();
if ((moduleContentEvent.getSource() instanceof AbstractFile) == false) {
return false;
}
AbstractFile abstractFile = (AbstractFile) moduleContentEvent.getSource();
if (!abstractFile.getNameExtension().isEmpty()) {
return true;
}
} catch (NoCurrentCaseException ex) {
/**
* Case is closed, no refresh needed.
*/
return false;
}
}
return false;
}
} }
private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesByExtNode.fname.text"); private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesByExtNode.fname.text");
/** /**

View File

@ -83,13 +83,19 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE); private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE);
/**
* RefreshThrottler is used to limit the number of refreshes performed when
* CONTENT_CHANGED and DATA_ADDED ingest module events are received.
*/
private final RefreshThrottler refreshThrottler;
/** /**
* Create the base expression used as the where clause in the queries for * Create the base expression used as the where clause in the queries for
* files by mime type. Filters out certain kinds of files and directories, * files by mime type. Filters out certain kinds of files and directories,
* and known/slack files based on user preferences. * and known/slack files based on user preferences.
* *
* @return The base expression to be used in the where clause of queries for * @return The base expression to be used in the where clause of queries for
* files by mime type. * files by mime type.
*/ */
private String createBaseWhereExpr() { private String createBaseWhereExpr() {
return "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")" return "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")"
@ -108,6 +114,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
private void removeListeners() { private void removeListeners() {
deleteObservers(); deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl); IngestManager.getInstance().removeIngestJobEventListener(pcl);
refreshThrottler.unregisterEventListener();
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
} }
@ -155,32 +162,20 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
this.typesRoot = typesRoot; this.typesRoot = typesRoot;
this.pcl = (PropertyChangeEvent evt) -> { this.pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString()) if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())
|| eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
/**
* Checking for a current case is a stop gap measure until a refreshMimeTypes();
* different way of handling the closing of cases is worked out.
* Currently, remote events may be received for a case that is
* already closed.
*/
try {
Case.getCurrentCaseThrows();
typesRoot.updateShowCounts();
populateHashMap();
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
if (evt.getNewValue() == null) { if (evt.getNewValue() == null) {
removeListeners(); removeListeners();
} }
} }
}; };
refreshThrottler = new RefreshThrottler(new FileTypesByMimeTypeRefresher());
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl); IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
refreshThrottler.registerForIngestModuleEvents();
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl); Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
populateHashMap(); populateHashMap();
} }
@ -201,7 +196,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
* @param node the Node which you wish to check. * @param node the Node which you wish to check.
* *
* @return True if originNode is an instance of ByMimeTypeNode and is empty, * @return True if originNode is an instance of ByMimeTypeNode and is empty,
* false otherwise. * false otherwise.
*/ */
public static boolean isEmptyMimeTypeNode(Node node) { public static boolean isEmptyMimeTypeNode(Node node) {
boolean isEmptyMimeNode = false; boolean isEmptyMimeNode = false;
@ -212,6 +207,41 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
} }
private void refreshMimeTypes() {
/**
* Checking for a current case is a stop gap measure until a different
* way of handling the closing of cases is worked out. Currently, remote
* events may be received for a case that is already closed.
*/
try {
Case.getCurrentCaseThrows();
typesRoot.updateShowCounts();
populateHashMap();
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
/**
* Responsible for updating the 'By Mime Type' view in the UI. See
* RefreshThrottler for more details.
*/
private class FileTypesByMimeTypeRefresher implements RefreshThrottler.Refresher {
@Override
public void refresh() {
refreshMimeTypes();
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
return true;
}
}
/** /**
* Class which represents the root node of the "By MIME Type" tree, will * Class which represents the root node of the "By MIME Type" tree, will
* have children of each media type present in the database or no children * have children of each media type present in the database or no children

View File

@ -0,0 +1,125 @@
/*
* 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.datamodel;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.sleuthkit.autopsy.ingest.IngestManager;
/**
* Utility class that can be used by UI nodes to reduce the number of
* potentially expensive UI refresh events when DATA_ADDED and CONTENT_CHANGED
* ingest manager events are received.
*/
class RefreshThrottler {
/**
* The Refresher interface needs to be implemented by ChildFactory instances
* that wish to take advantage of throttled refresh functionality.
*/
interface Refresher {
/**
* The RefreshThrottler calls this method when the RefreshTask runs.
*
*/
void refresh();
/**
* Determine whether the given event should result in a refresh.
*
* @param evt
*
* @return true if event should trigger a refresh, otherwise false.
*/
boolean isRefreshRequired(PropertyChangeEvent evt);
}
static ScheduledThreadPoolExecutor refreshExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("Node Refresh Thread").build());
// Keep a thread safe reference to the current refresh task (if any)
private final AtomicReference<RefreshTask> refreshTaskRef;
// The factory instance that will be called when a refresh is due.
private final Refresher refresher;
private static final long MIN_SECONDS_BETWEEN_REFRESH = 5;
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED, IngestManager.IngestModuleEvent.CONTENT_CHANGED);
/**
* A RefreshTask is scheduled to run when an event arrives and there isn't
* one already scheduled.
*/
private final class RefreshTask implements Runnable {
@Override
public void run() {
// Call refresh on the factory
refresher.refresh();
// Clear the refresh task reference
refreshTaskRef.set(null);
}
}
/**
* PropertyChangeListener that reacts to DATA_ADDED and CONTENT_CHANGED
* events and schedules a refresh task if one is not already scheduled.
*/
private final PropertyChangeListener pcl;
RefreshThrottler(Refresher r) {
this.refreshTaskRef = new AtomicReference<>(null);
refresher = r;
pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())
|| eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) {
if (!refresher.isRefreshRequired(evt)) {
return;
}
RefreshTask task = new RefreshTask();
if (refreshTaskRef.compareAndSet(null, task)) {
refreshExecutor.schedule(task, MIN_SECONDS_BETWEEN_REFRESH, TimeUnit.SECONDS);
}
}
};
}
/**
* Set up listener for ingest module events of interest.
*/
void registerForIngestModuleEvents() {
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
}
/**
* Remove ingest module event listener.
*/
void unregisterEventListener() {
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
}
}

View File

@ -101,7 +101,7 @@ abstract class AbstractFiltersPanel extends JPanel implements ActionListener, Li
constraints.weightx = LABEL_WEIGHT; constraints.weightx = LABEL_WEIGHT;
constraints.weighty = LABEL_WEIGHT; constraints.weighty = LABEL_WEIGHT;
constraints.gridwidth = LABEL_WIDTH; constraints.gridwidth = LABEL_WIDTH;
addToGridBagLayout(filterPanel.getCheckbox(), null, column); addToGridBagLayout(filterPanel.getCheckbox(), filterPanel.getAdditionalLabel(), column);
if (filterPanel.hasPanel()) { if (filterPanel.hasPanel()) {
constraints.gridx += constraints.gridwidth; constraints.gridx += constraints.gridwidth;
constraints.fill = GridBagConstraints.BOTH; constraints.fill = GridBagConstraints.BOTH;

View File

@ -101,3 +101,6 @@ DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which
ImageFilterPanel.imageFiltersSplitPane.border.title=Step 2: Filter which images to show ImageFilterPanel.imageFiltersSplitPane.border.title=Step 2: Filter which images to show
VideoFilterPanel.videoFiltersSplitPane.border.title=Step 2: Filter which videos to show VideoFilterPanel.videoFiltersSplitPane.border.title=Step 2: Filter which videos to show
DiscoveryDialog.step1Label.text=Step 1: Choose result type DiscoveryDialog.step1Label.text=Step 1: Choose result type
ResultsSplitPaneDivider.hideButton.text=
ResultsSplitPaneDivider.showButton.text=
ResultsSplitPaneDivider.detailsLabel.text=Details Area

View File

@ -11,7 +11,7 @@ DiscoveryTopComponent.name=\ Discovery
DiscoveryTopComponent.newSearch.text=New Search DiscoveryTopComponent.newSearch.text=New Search
DiscoveryTopComponent.searchCancelled.text=Search has been cancelled. DiscoveryTopComponent.searchCancelled.text=Search has been cancelled.
# {0} - search # {0} - search
DiscoveryTopComponent.searchComplete.text=Results for {0} DiscoveryTopComponent.searchComplete.text=Results with {0}
# {0} - searchType # {0} - searchType
DiscoveryTopComponent.searchInProgress.text=Performing search for results of type {0}. Please wait. DiscoveryTopComponent.searchInProgress.text=Performing search for results of type {0}. Please wait.
DiscoveryUiUtility.bytes.text=bytes DiscoveryUiUtility.bytes.text=bytes
@ -57,18 +57,24 @@ FileSearch.InterestingItemGroupKey.noSets=None
FileSearch.KeywordListGroupKey.noKeywords=None FileSearch.KeywordListGroupKey.noKeywords=None
FileSearch.NoGroupingGroupKey.allFiles=All Files FileSearch.NoGroupingGroupKey.allFiles=All Files
FileSearch.ObjectDetectedGroupKey.noSets=None FileSearch.ObjectDetectedGroupKey.noSets=None
FileSearchData.FileSize.LARGE_IMAGE.displayName=Large: 1-50MB FileSearchData.FileSize.100kbto1mb=: 100KB-1MB
FileSearchData.FileSize.LARGE_VIDEO.displayName=Large: 1-5GB FileSearchData.FileSize.100mbto1gb=: 100MB-1GB
FileSearchData.FileSize.MEDIUM_IMAGE.displayName=Medium: 100KB-1MB FileSearchData.FileSize.10PlusGb=: 10GB+
FileSearchData.FileSize.MEDIUM_VIDEO.displayName=Medium: 100MB-1GB FileSearchData.FileSize.16kbto100kb=: 16-100KB
FileSearchData.FileSize.SMALL_IMAGE.displayName=Small: 16-100KB FileSearchData.FileSize.1gbto5gb=: 1-5GB
FileSearchData.FileSize.SMALL_VIDEO.displayName=Small: 500KB-100MB FileSearchData.FileSize.1mbto50mb=: 1-50MB
FileSearchData.FileSize.XLARGE_IMAGE.displayName=XLarge: 50-200MB FileSearchData.FileSize.200PlusMb=: 200MB+
FileSearchData.FileSize.XLARGE_VIDEO.displayName=XLarge: 5-10GB FileSearchData.FileSize.500kbto100mb=: 500KB-100MB
FileSearchData.FileSize.XSMALL_IMAGE.displayName=XSmall: 0-16KB FileSearchData.FileSize.50mbto200mb=: 50-200MB
FileSearchData.FileSize.XSMALL_VIDEO.displayName=XSmall: 0-500KB FileSearchData.FileSize.5gbto10gb=: 5-10GB
FileSearchData.FileSize.XXLARGE_IMAGE.displayName=XXLarge: 200MB+ FileSearchData.FileSize.LARGE.displayName=Large
FileSearchData.FileSize.XXLARGE_VIDEO.displayName=XXLarge: 10GB+ FileSearchData.FileSize.MEDIUM.displayName=Medium
FileSearchData.FileSize.SMALL.displayName=Small
FileSearchData.FileSize.upTo16kb=: 0-16KB
FileSearchData.FileSize.upTo500kb=: 0-500KB
FileSearchData.FileSize.XLARGE.displayName=XLarge
FileSearchData.FileSize.XSMALL.displayName=XSmall
FileSearchData.FileSize.XXLARGE.displayName=XXLarge
FileSearchData.FileType.Audio.displayName=Audio FileSearchData.FileType.Audio.displayName=Audio
FileSearchData.FileType.Documents.displayName=Documents FileSearchData.FileType.Documents.displayName=Documents
FileSearchData.FileType.Executables.displayName=Executables FileSearchData.FileType.Executables.displayName=Executables
@ -106,45 +112,44 @@ FileSearchFiltering.concatenateSetNamesForDisplay.comma=,
# {1} - Data source ID # {1} - Data source ID
FileSearchFiltering.DataSourceFilter.datasource={0}({1}) FileSearchFiltering.DataSourceFilter.datasource={0}({1})
# {0} - filters # {0} - filters
FileSearchFiltering.DataSourceFilter.desc=Files in data source(s): {0} FileSearchFiltering.DataSourceFilter.desc=Data source(s): {0}
FileSearchFiltering.DataSourceFilter.or=\ or FileSearchFiltering.DataSourceFilter.or=,
# {0} - filters # {0} - filters
FileSearchFiltering.FileTypeFilter.desc=Files with type: {0} FileSearchFiltering.FileTypeFilter.desc=Type: {0}
FileSearchFiltering.FileTypeFilter.or=\ or FileSearchFiltering.FileTypeFilter.or=,
# {0} - filters # {0} - filters
FileSearchFiltering.FrequencyFilter.desc=Files with frequency: {0} FileSearchFiltering.FrequencyFilter.desc=Past occurrences: {0}
FileSearchFiltering.FrequencyFilter.or=\ or FileSearchFiltering.FrequencyFilter.or=,
# {0} - filters # {0} - filters
FileSearchFiltering.HashSetFilter.desc=Files with hash set hits in set(s): {0} FileSearchFiltering.HashSetFilter.desc=Hash set hits in set(s): {0}
# {0} - filters # {0} - filters
FileSearchFiltering.InterestingItemSetFilter.desc=Files with interesting item hits in set(s): {0} FileSearchFiltering.InterestingItemSetFilter.desc=Interesting item hits in set(s): {0}
# {0} - filters # {0} - filters
FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0} FileSearchFiltering.KeywordListFilter.desc=Keywords in list(s): {0}
FileSearchFiltering.KnownFilter.desc=Files which are not known FileSearchFiltering.KnownFilter.desc=which are not known
# {0} - filters # {0} - filters
FileSearchFiltering.ObjectDetectionFilter.desc=Files with objects detected in set(s): {0} FileSearchFiltering.ObjectDetectionFilter.desc=Objects detected in set(s): {0}
# {0} - filters # {0} - filters
FileSearchFiltering.ParentFilter.desc=Files with paths matching: {0} FileSearchFiltering.ParentFilter.desc=Paths matching: {0}
FileSearchFiltering.ParentFilter.exact=(exact match) FileSearchFiltering.ParentFilter.exact=(exact match)
FileSearchFiltering.ParentFilter.or=\ or FileSearchFiltering.ParentFilter.excluded=(excluded)
FileSearchFiltering.ParentFilter.included=(included)
FileSearchFiltering.ParentFilter.or=,
FileSearchFiltering.ParentFilter.substring=(substring) FileSearchFiltering.ParentFilter.substring=(substring)
FileSearchFiltering.ParentSearchTerm.excludeString=\ (exclude) FileSearchFiltering.ParentSearchTerm.excludeString=\ (exclude)
FileSearchFiltering.ParentSearchTerm.fullString=\ (exact) FileSearchFiltering.ParentSearchTerm.fullString=\ (exact)
FileSearchFiltering.ParentSearchTerm.includeString=\ (include) FileSearchFiltering.ParentSearchTerm.includeString=\ (include)
FileSearchFiltering.ParentSearchTerm.subString=\ (substring) FileSearchFiltering.ParentSearchTerm.subString=\ (substring)
FileSearchFiltering.PreviouslyNotableFilter.desc=Files that were previously marked as notable FileSearchFiltering.PreviouslyNotableFilter.desc=that were previously marked as notable
# {0} - filters # {0} - filters
FileSearchFiltering.ScoreFilter.desc=Files with score(s) of : {0} FileSearchFiltering.ScoreFilter.desc=Score(s) of : {0}
# {0} - filters # {0} - filters
FileSearchFiltering.SizeFilter.desc=Files with size in range(s): {0} FileSearchFiltering.SizeFilter.desc=Size(s): {0}
FileSearchFiltering.SizeFilter.or=\ or FileSearchFiltering.SizeFilter.or=,
# {0} - Minimum bytes
# {1} - Maximum bytes
FileSearchFiltering.SizeFilter.range=({0} to {1})
# {0} - tag names # {0} - tag names
FileSearchFiltering.TagsFilter.desc=Files that have been tagged {0} FileSearchFiltering.TagsFilter.desc=Tagged {0}
FileSearchFiltering.TagsFilter.or=\ or FileSearchFiltering.TagsFilter.or=,
FileSearchFiltering.UserCreatedFilter.desc=Files that contain EXIF data FileSearchFiltering.UserCreatedFilter.desc=that contain EXIF data
FileSearchPanel.sortingPanel.border.title=Grouping FileSearchPanel.sortingPanel.border.title=Grouping
FileSearchPanel.addButton.text=Add FileSearchPanel.addButton.text=Add
FileSearchPanel.substringRadioButton.text=Substring FileSearchPanel.substringRadioButton.text=Substring
@ -186,7 +191,7 @@ GroupsListPanel.noResults.title.text=No results found
ImageThumbnailPanel.isDeleted.text=All instances of file are deleted. ImageThumbnailPanel.isDeleted.text=All instances of file are deleted.
# {0} - otherInstanceCount # {0} - otherInstanceCount
ImageThumbnailPanel.nameLabel.more.text=\ and {0} more ImageThumbnailPanel.nameLabel.more.text=\ and {0} more
OpenDiscoveryAction.resultsIncomplete.text=Results may be incomplete OpenDiscoveryAction.resultsIncomplete.text=Discovery results may be incomplete
ResultFile.score.interestingResult.description=At least one instance of the file has an interesting result associated with it. ResultFile.score.interestingResult.description=At least one instance of the file has an interesting result associated with it.
ResultFile.score.notableFile.description=At least one instance of the file was recognized as notable. ResultFile.score.notableFile.description=At least one instance of the file was recognized as notable.
ResultFile.score.notableTaggedFile.description=At least one instance of the file is tagged with a notable tag. ResultFile.score.notableTaggedFile.description=At least one instance of the file is tagged with a notable tag.
@ -259,6 +264,9 @@ DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which
ImageFilterPanel.imageFiltersSplitPane.border.title=Step 2: Filter which images to show ImageFilterPanel.imageFiltersSplitPane.border.title=Step 2: Filter which images to show
VideoFilterPanel.videoFiltersSplitPane.border.title=Step 2: Filter which videos to show VideoFilterPanel.videoFiltersSplitPane.border.title=Step 2: Filter which videos to show
DiscoveryDialog.step1Label.text=Step 1: Choose result type DiscoveryDialog.step1Label.text=Step 1: Choose result type
ResultsSplitPaneDivider.hideButton.text=
ResultsSplitPaneDivider.showButton.text=
ResultsSplitPaneDivider.detailsLabel.text=Details Area
VideoThumbnailPanel.bytes.text=bytes VideoThumbnailPanel.bytes.text=bytes
VideoThumbnailPanel.deleted.text=All instances of file are deleted. VideoThumbnailPanel.deleted.text=All instances of file are deleted.
VideoThumbnailPanel.gigaBytes.text=GB VideoThumbnailPanel.gigaBytes.text=GB

View File

@ -43,23 +43,25 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace pref="196" max="32767" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="filler1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="step1Label" min="-2" pref="243" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Component id="imagesButton" min="-2" pref="110" max="-2" attributes="0"/> <Component id="imagesButton" min="-2" pref="110" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="videosButton" min="-2" pref="110" max="-2" attributes="0"/> <Component id="videosButton" min="-2" pref="110" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="documentsButton" min="-2" max="-2" attributes="0"/> <Component id="documentsButton" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="370" max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="step1Label" min="-2" pref="243" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="filler1" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="391" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace pref="196" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>

View File

@ -275,19 +275,21 @@ final class DiscoveryDialog extends javax.swing.JDialog {
toolBarPanelLayout.setHorizontalGroup( toolBarPanelLayout.setHorizontalGroup(
toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(toolBarPanelLayout.createSequentialGroup() .addGroup(toolBarPanelLayout.createSequentialGroup()
.addContainerGap(196, Short.MAX_VALUE) .addContainerGap()
.addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, toolBarPanelLayout.createSequentialGroup()
.addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(step1Label, javax.swing.GroupLayout.PREFERRED_SIZE, 243, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(toolBarPanelLayout.createSequentialGroup() .addGroup(toolBarPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addComponent(imagesButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(imagesButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(videosButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(videosButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(documentsButton))) .addComponent(documentsButton)
.addContainerGap(196, Short.MAX_VALUE)) .addContainerGap(370, Short.MAX_VALUE))
.addGroup(toolBarPanelLayout.createSequentialGroup()
.addComponent(step1Label, javax.swing.GroupLayout.PREFERRED_SIZE, 243, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(filler1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(391, Short.MAX_VALUE))))
); );
toolBarPanelLayout.setVerticalGroup( toolBarPanelLayout.setVerticalGroup(
toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)

View File

@ -41,7 +41,7 @@
<SubComponents> <SubComponents>
<Container class="javax.swing.JSplitPane" name="rightSplitPane"> <Container class="javax.swing.JSplitPane" name="rightSplitPane">
<Properties> <Properties>
<Property name="dividerSize" type="int" value="15"/> <Property name="dividerSize" type="int" value="35"/>
<Property name="orientation" type="int" value="0"/> <Property name="orientation" type="int" value="0"/>
<Property name="resizeWeight" type="double" value="1.0"/> <Property name="resizeWeight" type="double" value="1.0"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">

View File

@ -19,11 +19,14 @@
package org.sleuthkit.autopsy.discovery; package org.sleuthkit.autopsy.discovery;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics; import java.awt.Graphics;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.swing.JSplitPane; import javax.swing.JSplitPane;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.windows.Mode; import org.openide.windows.Mode;
@ -67,6 +70,34 @@ public final class DiscoveryTopComponent extends TopComponent {
mainSplitPane.setLeftComponent(groupListPanel); mainSplitPane.setLeftComponent(groupListPanel);
rightSplitPane.setTopComponent(resultsPanel); rightSplitPane.setTopComponent(resultsPanel);
rightSplitPane.setBottomComponent(detailsPanel); rightSplitPane.setBottomComponent(detailsPanel);
//set color of divider
rightSplitPane.setUI(new BasicSplitPaneUI() {
@Override
public BasicSplitPaneDivider createDefaultDivider() {
return new BasicSplitPaneDividerImpl(this);
}
});
}
/**
* Private class for replacing the divider for the results split pane.
*/
private final class BasicSplitPaneDividerImpl extends BasicSplitPaneDivider {
/**
* Construct a new BasicSplitPaneDividerImpl.
*
* @param ui The component which contains the split pane this divider is
* in.
*/
BasicSplitPaneDividerImpl(BasicSplitPaneUI ui) {
super(ui);
this.setLayout(new BorderLayout());
this.add(new ResultsSplitPaneDivider());
}
private static final long serialVersionUID = 1L;
} }
/** /**
@ -129,7 +160,7 @@ public final class DiscoveryTopComponent extends TopComponent {
mainSplitPane.setDividerLocation(250); mainSplitPane.setDividerLocation(250);
mainSplitPane.setPreferredSize(new java.awt.Dimension(1100, 700)); mainSplitPane.setPreferredSize(new java.awt.Dimension(1100, 700));
rightSplitPane.setDividerSize(15); rightSplitPane.setDividerSize(35);
rightSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); rightSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
rightSplitPane.setResizeWeight(1.0); rightSplitPane.setResizeWeight(1.0);
rightSplitPane.setPreferredSize(new java.awt.Dimension(800, 700)); rightSplitPane.setPreferredSize(new java.awt.Dimension(800, 700));
@ -249,11 +280,11 @@ public final class DiscoveryTopComponent extends TopComponent {
@Subscribe @Subscribe
@Messages({"DiscoveryTopComponent.newSearch.text=New Search", @Messages({"DiscoveryTopComponent.newSearch.text=New Search",
"# {0} - search", "# {0} - search",
"DiscoveryTopComponent.searchComplete.text=Results for {0}"}) "DiscoveryTopComponent.searchComplete.text=Results with {0}"})
void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) { void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) {
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text()); newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
progressMessageTextArea.setForeground(Color.black); progressMessageTextArea.setForeground(Color.black);
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(searchCompleteEvent.getFilters().stream().map(FileFilter::getDesc).collect(Collectors.joining(", ")))); progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(searchCompleteEvent.getFilters().stream().map(FileFilter::getDesc).collect(Collectors.joining("; "))));
progressMessageTextArea.setCaretPosition(0); progressMessageTextArea.setCaretPosition(0);
} }
@ -269,6 +300,7 @@ public final class DiscoveryTopComponent extends TopComponent {
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text()); newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
progressMessageTextArea.setForeground(Color.red); progressMessageTextArea.setForeground(Color.red);
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchCancelled_text()); progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchCancelled_text());
} }
/** /**

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.discovery; package org.sleuthkit.autopsy.discovery;
import java.awt.Component; import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point; import java.awt.Point;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -29,6 +30,8 @@ import java.util.logging.Level;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import org.openide.util.ImageUtilities; import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
@ -224,7 +227,16 @@ final class DiscoveryUiUtils {
message += dsmodulesWrapper.getMessage(); message += dsmodulesWrapper.getMessage();
} }
if (!message.isEmpty()) { if (!message.isEmpty()) {
JOptionPane.showMessageDialog(dialog, message, Bundle.OpenDiscoveryAction_resultsIncomplete_text(), JOptionPane.INFORMATION_MESSAGE); JScrollPane messageScrollPane = new JScrollPane();
JTextPane messageTextPane = new JTextPane();
messageTextPane.setText(message);
messageTextPane.setVisible(true);
messageTextPane.setEditable(false);
messageTextPane.setCaretPosition(0);
messageScrollPane.setMaximumSize(new Dimension(600, 100));
messageScrollPane.setPreferredSize(new Dimension(600, 100));
messageScrollPane.setViewportView(messageTextPane);
JOptionPane.showMessageDialog(dialog, messageScrollPane, Bundle.OpenDiscoveryAction_resultsIncomplete_text(), JOptionPane.PLAIN_MESSAGE);
} }
} catch (NoCurrentCaseException | TskCoreException ex) { } catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.WARNING, "Exception while determining which modules have been run for Discovery", ex); logger.log(Level.WARNING, "Exception while determining which modules have been run for Discovery", ex);

View File

@ -45,7 +45,6 @@ final class DocumentFilterPanel extends AbstractFiltersPanel {
addFilter(new PastOccurrencesFilterPanel(), true, pastOccurrencesIndices, 0); addFilter(new PastOccurrencesFilterPanel(), true, pastOccurrencesIndices, 0);
addFilter(new HashSetFilterPanel(), false, null, 1); addFilter(new HashSetFilterPanel(), false, null, 1);
addFilter(new InterestingItemsFilterPanel(), false, null, 1); addFilter(new InterestingItemsFilterPanel(), false, null, 1);
addFilter(new ObjectDetectedFilterPanel(), false, null, 1);
addFilter(new ParentFolderFilterPanel(), false, null, 1); addFilter(new ParentFolderFilterPanel(), false, null, 1);
addPanelsToScrollPane(documentsFiltersSplitPane); addPanelsToScrollPane(documentsFiltersSplitPane);
} }

View File

@ -126,39 +126,46 @@ final class FileSearchData {
* Enum representing the file size * Enum representing the file size
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"FileSearchData.FileSize.XXLARGE_IMAGE.displayName=XXLarge: 200MB+", "FileSearchData.FileSize.XXLARGE.displayName=XXLarge",
"FileSearchData.FileSize.XLARGE_IMAGE.displayName=XLarge: 50-200MB", "FileSearchData.FileSize.XLARGE.displayName=XLarge",
"FileSearchData.FileSize.LARGE_IMAGE.displayName=Large: 1-50MB", "FileSearchData.FileSize.LARGE.displayName=Large",
"FileSearchData.FileSize.MEDIUM_IMAGE.displayName=Medium: 100KB-1MB", "FileSearchData.FileSize.MEDIUM.displayName=Medium",
"FileSearchData.FileSize.SMALL_IMAGE.displayName=Small: 16-100KB", "FileSearchData.FileSize.SMALL.displayName=Small",
"FileSearchData.FileSize.XSMALL_IMAGE.displayName=XSmall: 0-16KB", "FileSearchData.FileSize.XSMALL.displayName=XSmall",
"FileSearchData.FileSize.XXLARGE_VIDEO.displayName=XXLarge: 10GB+", "FileSearchData.FileSize.10PlusGb=: 10GB+",
"FileSearchData.FileSize.XLARGE_VIDEO.displayName=XLarge: 5-10GB", "FileSearchData.FileSize.5gbto10gb=: 5-10GB",
"FileSearchData.FileSize.LARGE_VIDEO.displayName=Large: 1-5GB", "FileSearchData.FileSize.1gbto5gb=: 1-5GB",
"FileSearchData.FileSize.MEDIUM_VIDEO.displayName=Medium: 100MB-1GB", "FileSearchData.FileSize.100mbto1gb=: 100MB-1GB",
"FileSearchData.FileSize.SMALL_VIDEO.displayName=Small: 500KB-100MB", "FileSearchData.FileSize.200PlusMb=: 200MB+",
"FileSearchData.FileSize.XSMALL_VIDEO.displayName=XSmall: 0-500KB",}) "FileSearchData.FileSize.50mbto200mb=: 50-200MB",
"FileSearchData.FileSize.500kbto100mb=: 500KB-100MB",
"FileSearchData.FileSize.1mbto50mb=: 1-50MB",
"FileSearchData.FileSize.100kbto1mb=: 100KB-1MB",
"FileSearchData.FileSize.16kbto100kb=: 16-100KB",
"FileSearchData.FileSize.upTo500kb=: 0-500KB",
"FileSearchData.FileSize.upTo16kb=: 0-16KB",})
enum FileSize { enum FileSize {
XXLARGE_VIDEO(0, 10000 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_VIDEO_displayName()), XXLARGE_VIDEO(0, 10000 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_displayName(), Bundle.FileSearchData_FileSize_10PlusGb()),
XLARGE_VIDEO(1, 5000 * BYTES_PER_MB, 10000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_VIDEO_displayName()), XLARGE_VIDEO(1, 5000 * BYTES_PER_MB, 10000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_displayName(), Bundle.FileSearchData_FileSize_5gbto10gb()),
LARGE_VIDEO(2, 1000 * BYTES_PER_MB, 5000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_VIDEO_displayName()), LARGE_VIDEO(2, 1000 * BYTES_PER_MB, 5000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_displayName(), Bundle.FileSearchData_FileSize_1gbto5gb()),
MEDIUM_VIDEO(3, 100 * BYTES_PER_MB, 1000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_VIDEO_displayName()), MEDIUM_VIDEO(3, 100 * BYTES_PER_MB, 1000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_displayName(), Bundle.FileSearchData_FileSize_100mbto1gb()),
SMALL_VIDEO(4, 500000, 100 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_SMALL_VIDEO_displayName()), SMALL_VIDEO(4, 500000, 100 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_SMALL_displayName(), Bundle.FileSearchData_FileSize_500kbto100mb()),
XSMALL_VIDEO(5, 0, 500000, Bundle.FileSearchData_FileSize_XSMALL_VIDEO_displayName()), XSMALL_VIDEO(5, 0, 500000, Bundle.FileSearchData_FileSize_XSMALL_displayName(), Bundle.FileSearchData_FileSize_upTo500kb()),
XXLARGE_IMAGE(6, 200 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_IMAGE_displayName()), XXLARGE_IMAGE(6, 200 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_displayName(), Bundle.FileSearchData_FileSize_200PlusMb()),
XLARGE_IMAGE(7, 50 * BYTES_PER_MB, 200 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_IMAGE_displayName()), XLARGE_IMAGE(7, 50 * BYTES_PER_MB, 200 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_displayName(), Bundle.FileSearchData_FileSize_50mbto200mb()),
LARGE_IMAGE(8, 1 * BYTES_PER_MB, 50 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_IMAGE_displayName()), LARGE_IMAGE(8, 1 * BYTES_PER_MB, 50 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_displayName(), Bundle.FileSearchData_FileSize_1mbto50mb()),
MEDIUM_IMAGE(9, 100000, 1 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_IMAGE_displayName()), MEDIUM_IMAGE(9, 100000, 1 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_displayName(), Bundle.FileSearchData_FileSize_100kbto1mb()),
SMALL_IMAGE(10, 16000, 100000, Bundle.FileSearchData_FileSize_SMALL_IMAGE_displayName()), SMALL_IMAGE(10, 16000, 100000, Bundle.FileSearchData_FileSize_SMALL_displayName(), Bundle.FileSearchData_FileSize_16kbto100kb()),
XSMALL_IMAGE(11, 0, 16000, Bundle.FileSearchData_FileSize_XSMALL_IMAGE_displayName()); XSMALL_IMAGE(11, 0, 16000, Bundle.FileSearchData_FileSize_XSMALL_displayName(), Bundle.FileSearchData_FileSize_upTo16kb());
private final int ranking; // Must be unique for each value private final int ranking; // Must be unique for each value
private final long minBytes; // Note that the size must be strictly greater than this to match private final long minBytes; // Note that the size must be strictly greater than this to match
private final long maxBytes; private final long maxBytes;
private final String displayName; private final String sizeGroup;
private final String displaySize;
final static long NO_MAXIMUM = -1; final static long NO_MAXIMUM = -1;
FileSize(int ranking, long minB, long maxB, String displayName) { FileSize(int ranking, long minB, long maxB, String displayName, String displaySize) {
this.ranking = ranking; this.ranking = ranking;
this.minBytes = minB; this.minBytes = minB;
if (maxB >= 0) { if (maxB >= 0) {
@ -166,7 +173,8 @@ final class FileSearchData {
} else { } else {
this.maxBytes = NO_MAXIMUM; this.maxBytes = NO_MAXIMUM;
} }
this.displayName = displayName; this.sizeGroup = displayName;
this.displaySize = displaySize;
} }
/** /**
@ -246,7 +254,11 @@ final class FileSearchData {
@Override @Override
public String toString() { public String toString() {
return displayName; return sizeGroup + displaySize;
}
String getSizeGroup(){
return sizeGroup;
} }
/** /**

View File

@ -207,11 +207,8 @@ class FileSearchFiltering {
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - filters", "# {0} - filters",
"FileSearchFiltering.SizeFilter.desc=Files with size in range(s): {0}", "FileSearchFiltering.SizeFilter.desc=Size(s): {0}",
"FileSearchFiltering.SizeFilter.or= or ", "FileSearchFiltering.SizeFilter.or=, "})
"# {0} - Minimum bytes",
"# {1} - Maximum bytes",
"FileSearchFiltering.SizeFilter.range=({0} to {1})",})
@Override @Override
String getDesc() { String getDesc() {
String desc = ""; // NON-NLS String desc = ""; // NON-NLS
@ -219,7 +216,7 @@ class FileSearchFiltering {
if (!desc.isEmpty()) { if (!desc.isEmpty()) {
desc += Bundle.FileSearchFiltering_SizeFilter_or(); desc += Bundle.FileSearchFiltering_SizeFilter_or();
} }
desc += Bundle.FileSearchFiltering_SizeFilter_range(size.getMinBytes(), size.getMaxBytes()); desc += size.getSizeGroup();
} }
desc = Bundle.FileSearchFiltering_SizeFilter_desc(desc); desc = Bundle.FileSearchFiltering_SizeFilter_desc(desc);
return desc; return desc;
@ -364,10 +361,12 @@ class FileSearchFiltering {
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - filters", "# {0} - filters",
"FileSearchFiltering.ParentFilter.desc=Files with paths matching: {0}", "FileSearchFiltering.ParentFilter.desc=Paths matching: {0}",
"FileSearchFiltering.ParentFilter.or= or ", "FileSearchFiltering.ParentFilter.or=, ",
"FileSearchFiltering.ParentFilter.exact=(exact match)", "FileSearchFiltering.ParentFilter.exact=(exact match)",
"FileSearchFiltering.ParentFilter.substring=(substring)",}) "FileSearchFiltering.ParentFilter.substring=(substring)",
"FileSearchFiltering.ParentFilter.included=(included)",
"FileSearchFiltering.ParentFilter.excluded=(excluded)"})
@Override @Override
String getDesc() { String getDesc() {
String desc = ""; // NON-NLS String desc = ""; // NON-NLS
@ -380,6 +379,11 @@ class FileSearchFiltering {
} else { } else {
desc += searchTerm.getSearchStr() + Bundle.FileSearchFiltering_ParentFilter_substring(); desc += searchTerm.getSearchStr() + Bundle.FileSearchFiltering_ParentFilter_substring();
} }
if (searchTerm.isIncluded()) {
desc += Bundle.FileSearchFiltering_ParentFilter_included();
} else {
desc += Bundle.FileSearchFiltering_ParentFilter_excluded();
}
} }
desc = Bundle.FileSearchFiltering_ParentFilter_desc(desc); desc = Bundle.FileSearchFiltering_ParentFilter_desc(desc);
return desc; return desc;
@ -417,8 +421,8 @@ class FileSearchFiltering {
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - filters", "# {0} - filters",
"FileSearchFiltering.DataSourceFilter.desc=Files in data source(s): {0}", "FileSearchFiltering.DataSourceFilter.desc=Data source(s): {0}",
"FileSearchFiltering.DataSourceFilter.or= or ", "FileSearchFiltering.DataSourceFilter.or=, ",
"# {0} - Data source name", "# {0} - Data source name",
"# {1} - Data source ID", "# {1} - Data source ID",
"FileSearchFiltering.DataSourceFilter.datasource={0}({1})",}) "FileSearchFiltering.DataSourceFilter.datasource={0}({1})",})
@ -466,7 +470,7 @@ class FileSearchFiltering {
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - filters", "# {0} - filters",
"FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0}",}) "FileSearchFiltering.KeywordListFilter.desc=Keywords in list(s): {0}",})
@Override @Override
String getDesc() { String getDesc() {
return Bundle.FileSearchFiltering_KeywordListFilter_desc(concatenateSetNamesForDisplay(listNames)); return Bundle.FileSearchFiltering_KeywordListFilter_desc(concatenateSetNamesForDisplay(listNames));
@ -516,8 +520,8 @@ class FileSearchFiltering {
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - filters", "# {0} - filters",
"FileSearchFiltering.FileTypeFilter.desc=Files with type: {0}", "FileSearchFiltering.FileTypeFilter.desc=Type: {0}",
"FileSearchFiltering.FileTypeFilter.or= or ",}) "FileSearchFiltering.FileTypeFilter.or=, ",})
@Override @Override
String getDesc() { String getDesc() {
String desc = ""; String desc = "";
@ -586,8 +590,8 @@ class FileSearchFiltering {
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - filters", "# {0} - filters",
"FileSearchFiltering.FrequencyFilter.desc=Files with frequency: {0}", "FileSearchFiltering.FrequencyFilter.desc=Past occurrences: {0}",
"FileSearchFiltering.FrequencyFilter.or= or ",}) "FileSearchFiltering.FrequencyFilter.or=, ",})
@Override @Override
String getDesc() { String getDesc() {
String desc = ""; // NON-NLS String desc = ""; // NON-NLS
@ -595,7 +599,7 @@ class FileSearchFiltering {
if (!desc.isEmpty()) { if (!desc.isEmpty()) {
desc += Bundle.FileSearchFiltering_FrequencyFilter_or(); desc += Bundle.FileSearchFiltering_FrequencyFilter_or();
} }
desc += freq.name(); desc += freq.toString();
} }
return Bundle.FileSearchFiltering_FrequencyFilter_desc(desc); return Bundle.FileSearchFiltering_FrequencyFilter_desc(desc);
} }
@ -632,7 +636,7 @@ class FileSearchFiltering {
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - filters", "# {0} - filters",
"FileSearchFiltering.HashSetFilter.desc=Files with hash set hits in set(s): {0}",}) "FileSearchFiltering.HashSetFilter.desc=Hash set hits in set(s): {0}",})
@Override @Override
String getDesc() { String getDesc() {
return Bundle.FileSearchFiltering_HashSetFilter_desc(concatenateSetNamesForDisplay(setNames)); return Bundle.FileSearchFiltering_HashSetFilter_desc(concatenateSetNamesForDisplay(setNames));
@ -670,7 +674,7 @@ class FileSearchFiltering {
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - filters", "# {0} - filters",
"FileSearchFiltering.InterestingItemSetFilter.desc=Files with interesting item hits in set(s): {0}",}) "FileSearchFiltering.InterestingItemSetFilter.desc=Interesting item hits in set(s): {0}",})
@Override @Override
String getDesc() { String getDesc() {
return Bundle.FileSearchFiltering_InterestingItemSetFilter_desc(concatenateSetNamesForDisplay(setNames)); return Bundle.FileSearchFiltering_InterestingItemSetFilter_desc(concatenateSetNamesForDisplay(setNames));
@ -708,7 +712,7 @@ class FileSearchFiltering {
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - filters", "# {0} - filters",
"FileSearchFiltering.ObjectDetectionFilter.desc=Files with objects detected in set(s): {0}",}) "FileSearchFiltering.ObjectDetectionFilter.desc=Objects detected in set(s): {0}",})
@Override @Override
String getDesc() { String getDesc() {
return Bundle.FileSearchFiltering_ObjectDetectionFilter_desc(concatenateSetNamesForDisplay(typeNames)); return Bundle.FileSearchFiltering_ObjectDetectionFilter_desc(concatenateSetNamesForDisplay(typeNames));
@ -784,7 +788,7 @@ class FileSearchFiltering {
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - filters", "# {0} - filters",
"FileSearchFiltering.ScoreFilter.desc=Files with score(s) of : {0}",}) "FileSearchFiltering.ScoreFilter.desc=Score(s) of : {0}",})
@Override @Override
String getDesc() { String getDesc() {
return Bundle.FileSearchFiltering_ScoreFilter_desc( return Bundle.FileSearchFiltering_ScoreFilter_desc(
@ -826,8 +830,8 @@ class FileSearchFiltering {
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - tag names", "# {0} - tag names",
"FileSearchFiltering.TagsFilter.desc=Files that have been tagged {0}", "FileSearchFiltering.TagsFilter.desc=Tagged {0}",
"FileSearchFiltering.TagsFilter.or= or ",}) "FileSearchFiltering.TagsFilter.or=, ",})
@Override @Override
String getDesc() { String getDesc() {
String desc = ""; // NON-NLS String desc = ""; // NON-NLS
@ -862,7 +866,7 @@ class FileSearchFiltering {
} }
@NbBundle.Messages({ @NbBundle.Messages({
"FileSearchFiltering.UserCreatedFilter.desc=Files that contain EXIF data",}) "FileSearchFiltering.UserCreatedFilter.desc=that contain EXIF data",})
@Override @Override
String getDesc() { String getDesc() {
return Bundle.FileSearchFiltering_UserCreatedFilter_desc(); return Bundle.FileSearchFiltering_UserCreatedFilter_desc();
@ -931,7 +935,7 @@ class FileSearchFiltering {
} }
@NbBundle.Messages({ @NbBundle.Messages({
"FileSearchFiltering.PreviouslyNotableFilter.desc=Files that were previously marked as notable",}) "FileSearchFiltering.PreviouslyNotableFilter.desc=that were previously marked as notable",})
@Override @Override
String getDesc() { String getDesc() {
return Bundle.FileSearchFiltering_PreviouslyNotableFilter_desc(); return Bundle.FileSearchFiltering_PreviouslyNotableFilter_desc();
@ -949,7 +953,7 @@ class FileSearchFiltering {
} }
@NbBundle.Messages({ @NbBundle.Messages({
"FileSearchFiltering.KnownFilter.desc=Files which are not known"}) "FileSearchFiltering.KnownFilter.desc=which are not known"})
@Override @Override
String getDesc() { String getDesc() {
return Bundle.FileSearchFiltering_KnownFilter_desc(); return Bundle.FileSearchFiltering_KnownFilter_desc();

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.discovery;
import java.awt.Component; import java.awt.Component;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.SwingUtilities;
import org.openide.awt.ActionID; import org.openide.awt.ActionID;
import org.openide.awt.ActionReference; import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences; import org.openide.awt.ActionReferences;
@ -61,14 +62,16 @@ public final class OpenDiscoveryAction extends CallableSystemAction implements P
return Case.isCaseOpen(); return Case.isCaseOpen();
} }
@NbBundle.Messages({"OpenDiscoveryAction.resultsIncomplete.text=Results may be incomplete"}) @NbBundle.Messages({"OpenDiscoveryAction.resultsIncomplete.text=Discovery results may be incomplete"})
@Override @Override
public void performAction() { public void performAction() {
final DiscoveryDialog discDialog = DiscoveryDialog.getDiscoveryDialogInstance(); SwingUtilities.invokeLater(() -> {
discDialog.cancelSearch(); final DiscoveryDialog discDialog = DiscoveryDialog.getDiscoveryDialogInstance();
discDialog.setVisible(true); discDialog.cancelSearch();
DiscoveryUiUtils.displayErrorMessage(discDialog); DiscoveryUiUtils.displayErrorMessage(discDialog);
discDialog.setVisible(true);
});
} }
/** /**

View File

@ -37,6 +37,18 @@
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.ButtonGroup" name="includeButtonGroup">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.ButtonGroup" name="pathTypeButtonGroup">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
</NonVisualComponents> </NonVisualComponents>
<Properties> <Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
@ -143,6 +155,9 @@
</Container> </Container>
<Component class="javax.swing.JRadioButton" name="fullRadioButton"> <Component class="javax.swing.JRadioButton" name="fullRadioButton">
<Properties> <Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="pathTypeButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/> <Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/Bundle.properties" key="ParentFolderFilterPanel.fullRadioButton.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/discovery/Bundle.properties" key="ParentFolderFilterPanel.fullRadioButton.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
@ -152,6 +167,9 @@
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="includeRadioButton"> <Component class="javax.swing.JRadioButton" name="includeRadioButton">
<Properties> <Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="includeButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/> <Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/Bundle.properties" key="ParentFolderFilterPanel.includeRadioButton.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/discovery/Bundle.properties" key="ParentFolderFilterPanel.includeRadioButton.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
@ -161,6 +179,9 @@
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="substringRadioButton"> <Component class="javax.swing.JRadioButton" name="substringRadioButton">
<Properties> <Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="pathTypeButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/Bundle.properties" key="ParentFolderFilterPanel.substringRadioButton.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/discovery/Bundle.properties" key="ParentFolderFilterPanel.substringRadioButton.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
@ -169,6 +190,9 @@
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="excludeRadioButton"> <Component class="javax.swing.JRadioButton" name="excludeRadioButton">
<Properties> <Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="includeButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/Bundle.properties" key="ParentFolderFilterPanel.excludeRadioButton.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/discovery/Bundle.properties" key="ParentFolderFilterPanel.excludeRadioButton.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>

View File

@ -66,6 +66,8 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
parentCheckbox = new javax.swing.JCheckBox(); parentCheckbox = new javax.swing.JCheckBox();
parentLabel = new javax.swing.JLabel(); parentLabel = new javax.swing.JLabel();
javax.swing.ButtonGroup includeButtonGroup = new javax.swing.ButtonGroup();
javax.swing.ButtonGroup pathTypeButtonGroup = new javax.swing.ButtonGroup();
parentScrollPane = new javax.swing.JScrollPane(); parentScrollPane = new javax.swing.JScrollPane();
parentList = new javax.swing.JList<>(); parentList = new javax.swing.JList<>();
fullRadioButton = new javax.swing.JRadioButton(); fullRadioButton = new javax.swing.JRadioButton();
@ -106,17 +108,21 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
}); });
parentScrollPane.setViewportView(parentList); parentScrollPane.setViewportView(parentList);
pathTypeButtonGroup.add(fullRadioButton);
fullRadioButton.setSelected(true); fullRadioButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(fullRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.fullRadioButton.text_1")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(fullRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.fullRadioButton.text_1")); // NOI18N
fullRadioButton.setEnabled(false); fullRadioButton.setEnabled(false);
includeButtonGroup.add(includeRadioButton);
includeRadioButton.setSelected(true); includeRadioButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(includeRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.includeRadioButton.text_1")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(includeRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.includeRadioButton.text_1")); // NOI18N
includeRadioButton.setEnabled(false); includeRadioButton.setEnabled(false);
pathTypeButtonGroup.add(substringRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(substringRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.substringRadioButton.text_1")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(substringRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.substringRadioButton.text_1")); // NOI18N
substringRadioButton.setEnabled(false); substringRadioButton.setEnabled(false);
includeButtonGroup.add(excludeRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(excludeRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.excludeRadioButton.text_1")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(excludeRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.excludeRadioButton.text_1")); // NOI18N
excludeRadioButton.setEnabled(false); excludeRadioButton.setEnabled(false);
@ -188,7 +194,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void parentCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_parentCheckboxActionPerformed private void parentCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_parentCheckboxActionPerformed
// parentFilterSettings(true, true, parentCheckbox.isSelected(), null); configurePanel(parentCheckbox.isSelected(), null);
}//GEN-LAST:event_parentCheckboxActionPerformed }//GEN-LAST:event_parentCheckboxActionPerformed
private void parentListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_parentListValueChanged private void parentListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_parentListValueChanged
@ -235,6 +241,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
parentCheckbox.setSelected(selected); parentCheckbox.setSelected(selected);
if (parentCheckbox.isEnabled() && parentCheckbox.isSelected()) { if (parentCheckbox.isEnabled() && parentCheckbox.isSelected()) {
parentScrollPane.setEnabled(true); parentScrollPane.setEnabled(true);
parentLabel.setEnabled(true);
includeRadioButton.setEnabled(true); includeRadioButton.setEnabled(true);
excludeRadioButton.setEnabled(true); excludeRadioButton.setEnabled(true);
fullRadioButton.setEnabled(true); fullRadioButton.setEnabled(true);
@ -248,6 +255,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
} }
} else { } else {
parentScrollPane.setEnabled(false); parentScrollPane.setEnabled(false);
parentLabel.setEnabled(false);
parentList.setEnabled(false); parentList.setEnabled(false);
includeRadioButton.setEnabled(false); includeRadioButton.setEnabled(false);
excludeRadioButton.setEnabled(false); excludeRadioButton.setEnabled(false);

View File

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="aa" green="aa" red="aa" type="rgb"/>
</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">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="detailsLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="251" max="32767" attributes="0"/>
<Component id="showButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="hideButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Component id="filler2" alignment="1" max="32767" attributes="0"/>
<Component id="filler1" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="filler2" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="hideButton" max="32767" attributes="0"/>
<Component id="showButton" max="32767" attributes="0"/>
<Component id="detailsLabel" alignment="0" min="-2" pref="25" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="filler1" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="detailsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/Bundle.properties" key="ResultsSplitPaneDivider.detailsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.JButton" name="hideButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/discovery/arrow-down.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/Bundle.properties" key="ResultsSplitPaneDivider.hideButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[0, 0, 0, 0]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="hideButtonActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="hideButton.setCursor(Cursor.getDefaultCursor());"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.JButton" name="showButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/discovery/arrow-up.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/Bundle.properties" key="ResultsSplitPaneDivider.showButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[0, 0, 0, 0]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showButtonActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="showButton.setCursor(Cursor.getDefaultCursor());"/>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<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="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalGlue"/>
</AuxValues>
</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="[0, 32767]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalGlue"/>
</AuxValues>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,121 @@
/*
* Autopsy
*
* 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.discovery;
import java.awt.Cursor;
/**
* Panel for separating the results list from the details area.
*/
final class ResultsSplitPaneDivider extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
/**
* Creates new form LabeledSplitPaneDivider.
*/
ResultsSplitPaneDivider() {
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() {
javax.swing.JLabel detailsLabel = new javax.swing.JLabel();
javax.swing.JButton hideButton = new javax.swing.JButton();
javax.swing.JButton showButton = new javax.swing.JButton();
javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767));
setBackground(new java.awt.Color(170, 170, 170));
org.openide.awt.Mnemonics.setLocalizedText(detailsLabel, org.openide.util.NbBundle.getMessage(ResultsSplitPaneDivider.class, "ResultsSplitPaneDivider.detailsLabel.text")); // NOI18N
detailsLabel.setFocusable(false);
hideButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/discovery/arrow-down.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(hideButton, org.openide.util.NbBundle.getMessage(ResultsSplitPaneDivider.class, "ResultsSplitPaneDivider.hideButton.text")); // NOI18N
hideButton.setBorder(null);
hideButton.setFocusable(false);
hideButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
hideButton.setCursor(Cursor.getDefaultCursor());
hideButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
hideButtonActionPerformed(evt);
}
});
showButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/discovery/arrow-up.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(showButton, org.openide.util.NbBundle.getMessage(ResultsSplitPaneDivider.class, "ResultsSplitPaneDivider.showButton.text")); // NOI18N
showButton.setBorder(null);
showButton.setFocusable(false);
showButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
showButton.setCursor(Cursor.getDefaultCursor());
showButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
showButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(detailsLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 251, Short.MAX_VALUE)
.addComponent(showButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(hideButton)
.addContainerGap())
.addComponent(filler2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(filler1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(filler2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(0, 0, 0)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(hideButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(showButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(detailsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 0, 0)
.addComponent(filler1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
private void showButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showButtonActionPerformed
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(true));
}//GEN-LAST:event_showButtonActionPerformed
private void hideButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hideButtonActionPerformed
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(false));
}//GEN-LAST:event_hideButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -11,12 +11,17 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> <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,73,0,0,1,24"/> <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,102,0,0,1,56"/>
</AuxValues> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents> <SubComponents>
<Container class="javax.swing.JScrollPane" name="videoFiltersScrollPane"> <Container class="javax.swing.JScrollPane" name="videoFiltersScrollPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[312, 102]"/>
</Property>
</Properties>
<AuxValues> <AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/> <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/> <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
@ -32,7 +37,7 @@
<Container class="javax.swing.JPanel" name="videoFiltersPanel"> <Container class="javax.swing.JPanel" name="videoFiltersPanel">
<Properties> <Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[223, 66]"/> <Dimension value="[310, 100]"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
@ -45,7 +50,7 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/> <EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<Component id="videoFiltersSplitPane" max="32767" attributes="0"/> <Component id="videoFiltersSplitPane" pref="294" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/> <EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
@ -54,7 +59,7 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/> <EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<Component id="videoFiltersSplitPane" max="32767" attributes="0"/> <Component id="videoFiltersSplitPane" pref="84" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/> <EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>

View File

@ -66,7 +66,9 @@ final class VideoFilterPanel extends AbstractFiltersPanel {
setLayout(new java.awt.BorderLayout()); setLayout(new java.awt.BorderLayout());
videoFiltersPanel.setPreferredSize(new java.awt.Dimension(223, 66)); videoFiltersScrollPane.setPreferredSize(new java.awt.Dimension(312, 102));
videoFiltersPanel.setPreferredSize(new java.awt.Dimension(310, 100));
videoFiltersSplitPane.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(VideoFilterPanel.class, "VideoFilterPanel.videoFiltersSplitPane.border.title"))); // NOI18N videoFiltersSplitPane.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(VideoFilterPanel.class, "VideoFilterPanel.videoFiltersSplitPane.border.title"))); // NOI18N
videoFiltersSplitPane.setResizeWeight(0.5); videoFiltersSplitPane.setResizeWeight(0.5);
@ -77,14 +79,14 @@ final class VideoFilterPanel extends AbstractFiltersPanel {
videoFiltersPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) videoFiltersPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(videoFiltersPanelLayout.createSequentialGroup() .addGroup(videoFiltersPanelLayout.createSequentialGroup()
.addGap(8, 8, 8) .addGap(8, 8, 8)
.addComponent(videoFiltersSplitPane) .addComponent(videoFiltersSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 294, Short.MAX_VALUE)
.addGap(8, 8, 8)) .addGap(8, 8, 8))
); );
videoFiltersPanelLayout.setVerticalGroup( videoFiltersPanelLayout.setVerticalGroup(
videoFiltersPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) videoFiltersPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(videoFiltersPanelLayout.createSequentialGroup() .addGroup(videoFiltersPanelLayout.createSequentialGroup()
.addGap(8, 8, 8) .addGap(8, 8, 8)
.addComponent(videoFiltersSplitPane) .addComponent(videoFiltersSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 84, Short.MAX_VALUE)
.addGap(8, 8, 8)) .addGap(8, 8, 8))
); );

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -78,7 +78,7 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH.getTypeID(), Color.GREEN); artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH.getTypeID(), Color.GREEN);
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID(), Color.ORANGE); artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID(), Color.ORANGE);
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID(), Color.ORANGE); artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID(), Color.ORANGE);
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID(), Color.CYAN); artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID(), Color.MAGENTA);
} }
private final Waypoint dataModelWaypoint; private final Waypoint dataModelWaypoint;

View File

@ -993,7 +993,7 @@ public final class HealthMonitor implements PropertyChangeListener {
} }
String[] metricNames = {"Disk Reads: Hash calculation", "Database: getImages query", "Solr: Index chunk", "Solr: Connectivity check", String[] metricNames = {"Disk Reads: Hash calculation", "Database: getImages query", "Solr: Index chunk", "Solr: Connectivity check",
"Correlation Engine: Notable artifact query", "Correlation Engine: Bulk insert"}; // NON-NLS "Central Repository: Notable artifact query", "Central Repository: Bulk insert"}; // NON-NLS
Random rand = new Random(); Random rand = new Random();

View File

@ -471,6 +471,9 @@ public final class IngestJobSettings {
case "Archive Extractor": //NON-NLS case "Archive Extractor": //NON-NLS
moduleNames.add("Embedded File Extractor"); //NON-NLS moduleNames.add("Embedded File Extractor"); //NON-NLS
break; break;
case "Correlation Engine": //NON-NLS
moduleNames.add("Central Repository"); //NON-NLS
break;
default: default:
moduleNames.add(name); moduleNames.add(name);
} }

View File

@ -135,7 +135,7 @@ public class IngestOptionsPanel extends IngestModuleGlobalSettingsPanel implemen
tabbedPane.setEnabled(!ingestIsRunning); tabbedPane.setEnabled(!ingestIsRunning);
settingsPanel.enableButtons(!ingestIsRunning); settingsPanel.enableButtons(!ingestIsRunning);
profilePanel.enableButtons(!ingestIsRunning); profilePanel.enableButtons(!ingestIsRunning);
filterPanel.enableButtons(!ingestIsRunning); filterPanel.enableButtons();
} }

View File

@ -22,7 +22,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
/** /**
* Ingest job settings panel for the Correlation Engine module. * Ingest job settings panel for the Central Repository module.
*/ */
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class DataSourceIntegrityIngestSettingsPanel extends IngestModuleIngestJobSettingsPanel { final class DataSourceIntegrityIngestSettingsPanel extends IngestModuleIngestJobSettingsPanel {

View File

@ -139,9 +139,10 @@ class SevenZipExtractor {
} }
SevenZipExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) throws SevenZipNativeInitializationException { SevenZipExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) throws SevenZipNativeInitializationException {
if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) { if (!SevenZip.isInitializedSuccessfully()) {
SevenZip.initSevenZipFromPlatformJAR(); throw new SevenZipNativeInitializationException("SevenZip has not been previously initialized.");
} }
this.context = context; this.context = context;
this.fileTypeDetector = fileTypeDetector; this.fileTypeDetector = fileTypeDetector;
this.moduleDirRelative = moduleDirRelative; this.moduleDirRelative = moduleDirRelative;

View File

@ -6,6 +6,7 @@ FilesIdentifierIngestModule.indexError.message=Failed to index interesting file
FilesSet.rule.dateRule.toString=(modified within {0} day(s)) FilesSet.rule.dateRule.toString=(modified within {0} day(s))
FilesSetDefsPanel.bytes=Bytes FilesSetDefsPanel.bytes=Bytes
FilesSetDefsPanel.cancelImportMsg=Cancel import FilesSetDefsPanel.cancelImportMsg=Cancel import
FilesSetDefsPanel.cancelNewSetMsg=Cancel
# {0} - file name # {0} - file name
FilesSetDefsPanel.exportButtonActionPerformed.fileExistPrompt=File {0} exists, overwrite? FilesSetDefsPanel.exportButtonActionPerformed.fileExistPrompt=File {0} exists, overwrite?
FilesSetDefsPanel.gigaBytes=Gigabytes FilesSetDefsPanel.gigaBytes=Gigabytes
@ -23,8 +24,12 @@ FilesSetDefsPanel.interesting.fileExtensionFilterLbl=Autopsy Interesting File Se
FilesSetDefsPanel.interesting.importButtonAction.featureName=Interesting Files Set Import FilesSetDefsPanel.interesting.importButtonAction.featureName=Interesting Files Set Import
FilesSetDefsPanel.interesting.importOwConflict=Import Interesting files set conflict FilesSetDefsPanel.interesting.importOwConflict=Import Interesting files set conflict
FilesSetDefsPanel.interesting.importSetButton.text=Import Set FilesSetDefsPanel.interesting.importSetButton.text=Import Set
FilesSetDefsPanel.interesting.newOwConflict=Interesting files set conflict
# {0} - FilesSet name # {0} - FilesSet name
FilesSetDefsPanel.interesting.overwriteSetPrompt=Interesting files set <{0}> already exists locally, overwrite? FilesSetDefsPanel.interesting.overwriteSetPrompt=Interesting files set "{0}" already exists locally, overwrite?
# {0} - FilesSet name
# {1} - New FilesSet name
FilesSetDefsPanel.interesting.standardFileConflict=A standard interesting file set already exists with the name "{0}." Would you like to rename the set to "{1}?"
FilesSetDefsPanel.Interesting.Title=Global Interesting Items Settings FilesSetDefsPanel.Interesting.Title=Global Interesting Items Settings
FilesSetDefsPanel.kiloBytes=Kilobytes FilesSetDefsPanel.kiloBytes=Kilobytes
FilesSetDefsPanel.loadError=Error loading interesting files sets from file. FilesSetDefsPanel.loadError=Error loading interesting files sets from file.
@ -32,6 +37,7 @@ FilesSetDefsPanel.megaBytes=Megabytes
FilesSetDefsPanel.noSkipMsg=No, skip FilesSetDefsPanel.noSkipMsg=No, skip
FilesSetDefsPanel.saveError=Error saving interesting files sets to file. FilesSetDefsPanel.saveError=Error saving interesting files sets to file.
FilesSetDefsPanel.yesOwMsg=Yes, overwrite FilesSetDefsPanel.yesOwMsg=Yes, overwrite
FilesSetDefsPanel.yesStandardFileConflictCreate=Yes, create
FilesSetPanel.filter.title=File Filter FilesSetPanel.filter.title=File Filter
FilesSetPanel.ingest.createNewFilter=Create/edit file ingest filters... FilesSetPanel.ingest.createNewFilter=Create/edit file ingest filters...
FilesSetPanel.ingest.messages.filtersMustBeNamed=File ingest filters must be named. FilesSetPanel.ingest.messages.filtersMustBeNamed=File ingest filters must be named.
@ -140,3 +146,5 @@ FilesSetDefsPanel.ruleLabel.text=Rule Details
FilesSetDefsPanel.pathLabel.text=Path Substring: FilesSetDefsPanel.pathLabel.text=Path Substring:
FilesSetDefsPanel.mimeTypeLabel.text=MIME Type: FilesSetDefsPanel.mimeTypeLabel.text=MIME Type:
FilesSetDefsPanel.fileSizeLabel.text=File Size: FilesSetDefsPanel.fileSizeLabel.text=File Size:
# {0} - filesSetName
StandardInterestingFileSetsLoader.customSuffixed={0} (Custom)

View File

@ -44,6 +44,10 @@ public final class FilesSet implements Serializable {
private final String description; private final String description;
private final boolean ignoreKnownFiles; private final boolean ignoreKnownFiles;
private final boolean ignoreUnallocatedSpace; private final boolean ignoreUnallocatedSpace;
private final boolean standardSet;
private final int versionNumber;
private final Map<String, Rule> rules = new HashMap<>(); private final Map<String, Rule> rules = new HashMap<>();
/** /**
@ -59,9 +63,39 @@ public final class FilesSet implements Serializable {
* but a set with no rules is the empty set. * but a set with no rules is the empty set.
*/ */
public FilesSet(String name, String description, boolean ignoreKnownFiles, boolean ignoreUnallocatedSpace, Map<String, Rule> rules) { public FilesSet(String name, String description, boolean ignoreKnownFiles, boolean ignoreUnallocatedSpace, Map<String, Rule> rules) {
this(name, description, ignoreKnownFiles, ignoreUnallocatedSpace, rules, false, 0);
}
/**
* Constructs an interesting files set.
*
* @param name The name of the set.
* @param description A description of the set, may be null.
* @param ignoreKnownFiles Whether or not to exclude known files from
* the set.
* @param ignoreUnallocatedSpace Whether or not to exclude unallocated space
* from the set.
* @param standardSet Whether or not the FilesSet is considered a
* standard interesting set file.
* @param versionNumber The versionNumber for the FilesSet so that
* older versions can be replaced with newer
* versions.
* @param rules The rules that define the set. May be null,
* but a set with no rules is the empty set.
*/
public FilesSet(String name, String description, boolean ignoreKnownFiles, boolean ignoreUnallocatedSpace, Map<String, Rule> rules,
boolean standardSet, int versionNumber) {
if ((name == null) || (name.isEmpty())) { if ((name == null) || (name.isEmpty())) {
throw new IllegalArgumentException("Interesting files set name cannot be null or empty"); throw new IllegalArgumentException("Interesting files set name cannot be null or empty");
} }
if (versionNumber < 0) {
throw new IllegalArgumentException("version number must be >= 0");
}
this.standardSet = standardSet;
this.versionNumber = versionNumber;
this.name = name; this.name = name;
this.description = (description != null ? description : ""); this.description = (description != null ? description : "");
this.ignoreKnownFiles = ignoreKnownFiles; this.ignoreKnownFiles = ignoreKnownFiles;
@ -71,6 +105,22 @@ public final class FilesSet implements Serializable {
} }
} }
/**
* @return Whether or not the FilesSet is considered a standard interesting
* set file.
*/
boolean isStandardSet() {
return standardSet;
}
/**
* @return The versionNumber for the FilesSet so that older versions can be
* replaced with newer versions.
*/
int getVersionNumber() {
return versionNumber;
}
/** /**
* Gets the name of this interesting files set. * Gets the name of this interesting files set.
* *
@ -624,7 +674,6 @@ public final class FilesSet implements Serializable {
* To ensure compatibility with existing serialized configuration * To ensure compatibility with existing serialized configuration
* settings, this class cannot have a 'serialVersionUID'. * settings, this class cannot have a 'serialVersionUID'.
*/ */
private final TextMatcher textMatcher; private final TextMatcher textMatcher;
/** /**
@ -648,10 +697,10 @@ public final class FilesSet implements Serializable {
AbstractTextCondition(Pattern regex) { AbstractTextCondition(Pattern regex) {
this.textMatcher = new FilesSet.Rule.RegexMatcher(regex); this.textMatcher = new FilesSet.Rule.RegexMatcher(regex);
} }
/** /**
* Construct a case-insensitive multi-value text condition. * Construct a case-insensitive multi-value text condition.
* *
* @param values The list of values in which to look for a match. * @param values The list of values in which to look for a match.
*/ */
AbstractTextCondition(List<String> values) { AbstractTextCondition(List<String> values) {
@ -783,7 +832,6 @@ public final class FilesSet implements Serializable {
* To ensure compatibility with existing serialized configuration * To ensure compatibility with existing serialized configuration
* settings, this class cannot have a 'serialVersionUID'. * settings, this class cannot have a 'serialVersionUID'.
*/ */
private final static long SECS_PER_DAY = 60 * 60 * 24; private final static long SECS_PER_DAY = 60 * 60 * 24;
private int daysIncluded; private int daysIncluded;
@ -834,8 +882,8 @@ public final class FilesSet implements Serializable {
// AbstractFile.getFileNameExtension() returns just the // AbstractFile.getFileNameExtension() returns just the
// extension chars and not the dot. // extension chars and not the dot.
super(normalize(extension), false); super(normalize(extension), false);
} }
/** /**
* Construct a case-insensitive file name extension condition. * Construct a case-insensitive file name extension condition.
* *
@ -862,29 +910,29 @@ public final class FilesSet implements Serializable {
public boolean passes(AbstractFile file) { public boolean passes(AbstractFile file) {
return this.textMatches(file.getNameExtension()); return this.textMatches(file.getNameExtension());
} }
/** /**
* Strip "." from the start of extensions in the provided list. * Strip "." from the start of extensions in the provided list.
* *
* @param extensions The list of extensions to be processed. * @param extensions The list of extensions to be processed.
* *
* @return A post-processed list of extensions. * @return A post-processed list of extensions.
*/ */
private static List<String> normalize(List<String> extensions) { private static List<String> normalize(List<String> extensions) {
List<String> values = new ArrayList<>(extensions); List<String> values = new ArrayList<>(extensions);
for (int i=0; i < values.size(); i++) { for (int i = 0; i < values.size(); i++) {
values.set(i, normalize(values.get(i))); values.set(i, normalize(values.get(i)));
} }
return values; return values;
} }
/** /**
* Strip "." from the start of the provided extension. * Strip "." from the start of the provided extension.
* *
* @param extension The extension to be processed. * @param extension The extension to be processed.
* *
* @return A post-processed extension. * @return A post-processed extension.
*/ */
private static String normalize(String extension) { private static String normalize(String extension) {

View File

@ -30,6 +30,7 @@ import java.util.Map;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.DefaultListModel; import javax.swing.DefaultListModel;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
@ -37,12 +38,15 @@ import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.commons.lang3.tuple.Pair;
import org.netbeans.spi.options.OptionsPanelController; import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
import org.sleuthkit.autopsy.ingest.IngestProfiles; import org.sleuthkit.autopsy.ingest.IngestProfiles;
import org.sleuthkit.autopsy.ingest.IngestProfiles.IngestProfile; import org.sleuthkit.autopsy.ingest.IngestProfiles.IngestProfile;
@ -100,6 +104,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.panelType = panelType; this.panelType = panelType;
this.initComponents(); this.initComponents();
this.customInit(); this.customInit();
this.setsList.setModel(setsListModel); this.setsList.setModel(setsListModel);
this.setsList.addListSelectionListener(new FilesSetDefsPanel.SetsListSelectionListener()); this.setsList.addListSelectionListener(new FilesSetDefsPanel.SetsListSelectionListener());
this.rulesList.setModel(rulesListModel); this.rulesList.setModel(rulesListModel);
@ -133,6 +138,13 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.ruleDialogTitle = "FilesSetPanel.interesting.title"; this.ruleDialogTitle = "FilesSetPanel.interesting.title";
this.ingoreUnallocCheckbox.setVisible(false); this.ingoreUnallocCheckbox.setVisible(false);
} }
IngestManager.getInstance().addIngestJobEventListener((ignored) -> {
canBeEnabled
= !IngestManager.getInstance().isIngestRunning();
enableButtons();
});
canBeEnabled = !IngestManager.getInstance().isIngestRunning();
} }
@NbBundle.Messages({"FilesSetDefsPanel.Interesting.Title=Global Interesting Items Settings", @NbBundle.Messages({"FilesSetDefsPanel.Interesting.Title=Global Interesting Items Settings",
@ -172,20 +184,23 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
} }
} }
public void enableButtons(boolean isEnabled) { public void enableButtons() {
boolean setSelected = (FilesSetDefsPanel.this.setsList.getSelectedValue() != null); FilesSet selectedFilesSet = this.setsList.getSelectedValue();
boolean setSelected = (selectedFilesSet != null);
boolean isStandardSet = (selectedFilesSet != null && selectedFilesSet.isStandardSet());
boolean ruleSelected = (FilesSetDefsPanel.this.rulesList.getSelectedValue() != null); boolean ruleSelected = (FilesSetDefsPanel.this.rulesList.getSelectedValue() != null);
canBeEnabled = isEnabled;
newRuleButton.setEnabled(isEnabled); newRuleButton.setEnabled(canBeEnabled && !isStandardSet);
copySetButton.setEnabled(isEnabled && setSelected); copySetButton.setEnabled(canBeEnabled && setSelected);
newSetButton.setEnabled(isEnabled); newSetButton.setEnabled(canBeEnabled);
editRuleButton.setEnabled(isEnabled && ruleSelected); editRuleButton.setEnabled(canBeEnabled && ruleSelected && !isStandardSet);
editSetButton.setEnabled(isEnabled && setSelected); editSetButton.setEnabled(canBeEnabled && setSelected && !isStandardSet);
exportSetButton.setEnabled(setSelected); exportSetButton.setEnabled(setSelected);
importSetButton.setEnabled(isEnabled); importSetButton.setEnabled(canBeEnabled);
deleteRuleButton.setEnabled(isEnabled && ruleSelected); deleteRuleButton.setEnabled(canBeEnabled && ruleSelected && !isStandardSet);
deleteSetButton.setEnabled(isEnabled && setSelected); deleteSetButton.setEnabled(canBeEnabled && setSelected && !isStandardSet);
ingestWarningLabel.setVisible(!isEnabled); ingestWarningLabel.setVisible(!canBeEnabled);
} }
@Override @Override
@ -231,17 +246,11 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
* Clears the list models and resets all of the components. * Clears the list models and resets all of the components.
*/ */
private void resetComponents() { private void resetComponents() {
this.resetRuleComponents();
this.setsListModel.clear(); this.setsListModel.clear();
this.setDescriptionTextArea.setText(""); this.setDescriptionTextArea.setText("");
this.ignoreKnownFilesCheckbox.setSelected(true); this.ignoreKnownFilesCheckbox.setSelected(true);
this.ingoreUnallocCheckbox.setSelected(true); this.ingoreUnallocCheckbox.setSelected(true);
this.newSetButton.setEnabled(true && canBeEnabled); this.resetRuleComponents();
this.editSetButton.setEnabled(false);
this.copySetButton.setEnabled(false);
this.exportSetButton.setEnabled(false);
this.importSetButton.setEnabled(true && canBeEnabled);
this.deleteSetButton.setEnabled(false);
} }
/** /**
@ -260,9 +269,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.equalitySignComboBox.setSelectedIndex(2); this.equalitySignComboBox.setSelectedIndex(2);
this.fileSizeUnitComboBox.setSelectedIndex(1); this.fileSizeUnitComboBox.setSelectedIndex(1);
this.fileSizeSpinner.setValue(0); this.fileSizeSpinner.setValue(0);
this.newRuleButton.setEnabled(!this.setsListModel.isEmpty() && canBeEnabled); enableButtons();
this.editRuleButton.setEnabled(false);
this.deleteRuleButton.setEnabled(false);
} }
/** /**
@ -275,12 +282,10 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
if (e.getValueIsAdjusting()) { if (e.getValueIsAdjusting()) {
return; return;
} }
FilesSetDefsPanel.this.rulesListModel.clear(); FilesSetDefsPanel.this.rulesListModel.clear();
FilesSetDefsPanel.this.resetRuleComponents(); FilesSetDefsPanel.this.resetRuleComponents();
//enable the new button
FilesSetDefsPanel.this.newSetButton.setEnabled(canBeEnabled);
FilesSetDefsPanel.this.importSetButton.setEnabled(canBeEnabled);
// Get the selected interesting files set and populate the set // Get the selected interesting files set and populate the set
// components. // components.
FilesSet selectedSet = FilesSetDefsPanel.this.setsList.getSelectedValue(); FilesSet selectedSet = FilesSetDefsPanel.this.setsList.getSelectedValue();
@ -291,11 +296,6 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
FilesSetDefsPanel.this.setDescriptionTextArea.setText(selectedSet.getDescription()); FilesSetDefsPanel.this.setDescriptionTextArea.setText(selectedSet.getDescription());
FilesSetDefsPanel.this.ignoreKnownFilesCheckbox.setSelected(selectedSet.ignoresKnownFiles()); FilesSetDefsPanel.this.ignoreKnownFilesCheckbox.setSelected(selectedSet.ignoresKnownFiles());
FilesSetDefsPanel.this.ingoreUnallocCheckbox.setSelected(selectedSet.ingoresUnallocatedSpace()); FilesSetDefsPanel.this.ingoreUnallocCheckbox.setSelected(selectedSet.ingoresUnallocatedSpace());
// Enable the copy, export, edit and delete set buttons.
FilesSetDefsPanel.this.editSetButton.setEnabled(canBeEnabled);
FilesSetDefsPanel.this.deleteSetButton.setEnabled(canBeEnabled);
FilesSetDefsPanel.this.copySetButton.setEnabled(canBeEnabled);
FilesSetDefsPanel.this.exportSetButton.setEnabled(true);
// Populate the rule definitions list, sorted by name. // Populate the rule definitions list, sorted by name.
List<FilesSet.Rule> rules = new ArrayList<>(selectedSet.getRules().values()); List<FilesSet.Rule> rules = new ArrayList<>(selectedSet.getRules().values());
Collections.sort(rules, new Comparator<FilesSet.Rule>() { Collections.sort(rules, new Comparator<FilesSet.Rule>() {
@ -311,15 +311,8 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
if (!FilesSetDefsPanel.this.rulesListModel.isEmpty()) { if (!FilesSetDefsPanel.this.rulesListModel.isEmpty()) {
FilesSetDefsPanel.this.rulesList.setSelectedIndex(0); FilesSetDefsPanel.this.rulesList.setSelectedIndex(0);
} }
} else {
// Disable the edit, delete, copy, and export buttons
FilesSetDefsPanel.this.editSetButton.setEnabled(false);
FilesSetDefsPanel.this.deleteSetButton.setEnabled(false);
FilesSetDefsPanel.this.copySetButton.setEnabled(false);
FilesSetDefsPanel.this.exportSetButton.setEnabled(false);
} }
} }
} }
/** /**
@ -389,18 +382,14 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
FilesSetDefsPanel.this.equalitySignComboBox.setSelectedIndex(2); FilesSetDefsPanel.this.equalitySignComboBox.setSelectedIndex(2);
FilesSetDefsPanel.this.fileSizeSpinner.setValue(0); FilesSetDefsPanel.this.fileSizeSpinner.setValue(0);
} }
if (dateCondition != null){ if (dateCondition != null) {
FilesSetDefsPanel.this.daysIncludedTextField.setText(Integer.toString(dateCondition.getDaysIncluded())); FilesSetDefsPanel.this.daysIncludedTextField.setText(Integer.toString(dateCondition.getDaysIncluded()));
} else {
FilesSetDefsPanel.this.daysIncludedTextField.setText("");
} }
else { enableButtons();
FilesSetDefsPanel.this.daysIncludedTextField.setText("");
}
// Enable the new, edit and delete rule buttons.
FilesSetDefsPanel.this.newRuleButton.setEnabled(true && canBeEnabled);
FilesSetDefsPanel.this.editRuleButton.setEnabled(true && canBeEnabled);
FilesSetDefsPanel.this.deleteRuleButton.setEnabled(true && canBeEnabled);
} else { } else {
FilesSetDefsPanel.this.resetRuleComponents(); resetRuleComponents();
} }
} }
@ -436,15 +425,6 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
option = JOptionPane.showConfirmDialog(this, panel, NbBundle.getMessage(FilesSetPanel.class, filterDialogTitle), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); option = JOptionPane.showConfirmDialog(this, panel, NbBundle.getMessage(FilesSetPanel.class, filterDialogTitle), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
} while (option == JOptionPane.OK_OPTION && !panel.isValidDefinition()); } while (option == JOptionPane.OK_OPTION && !panel.isValidDefinition());
// While adding new ruleset(selectedSet == null), if rule set with same name already exists, do not add to the filesSets hashMap.
// In case of editing an existing ruleset(selectedSet != null), following check is not performed.
if (this.filesSets.containsKey(panel.getFilesSetName()) && shouldCreateNew) {
MessageNotifyUtil.Message.error(NbBundle.getMessage(this.getClass(),
"FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text",
panel.getFilesSetName()));
return;
}
if (option == JOptionPane.OK_OPTION) { if (option == JOptionPane.OK_OPTION) {
Map<String, FilesSet.Rule> rules = new HashMap<>(); Map<String, FilesSet.Rule> rules = new HashMap<>();
if (selectedSet != null) { if (selectedSet != null) {
@ -453,10 +433,25 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
// Preserve the existing rules from the set being edited. // Preserve the existing rules from the set being edited.
rules.putAll(selectedSet.getRules()); rules.putAll(selectedSet.getRules());
} }
if (shouldCreateNew) {
this.replaceFilesSet(null, panel.getFilesSetName(), panel.getFilesSetDescription(), panel.getFileSetIgnoresKnownFiles(), panel.getFileSetIgnoresUnallocatedSpace(), rules); FilesSet filesSet = new FilesSet(
} else { panel.getFilesSetName(),
this.replaceFilesSet(selectedSet, panel.getFilesSetName(), panel.getFilesSetDescription(), panel.getFileSetIgnoresKnownFiles(), panel.getFileSetIgnoresUnallocatedSpace(), rules); panel.getFilesSetDescription(),
panel.getFileSetIgnoresKnownFiles(),
panel.getFileSetIgnoresUnallocatedSpace(),
rules
);
Pair<FilesSet, Integer> result = handleConflict(filesSet, false);
option = result.getRight();
FilesSet toAddOrUpdate = result.getLeft();
if (result.getRight() == JOptionPane.OK_OPTION) {
if (shouldCreateNew) {
this.replaceFilesSet(null, toAddOrUpdate, null);
} else {
this.replaceFilesSet(selectedSet, toAddOrUpdate, null);
}
} }
} }
} }
@ -504,7 +499,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
// Add the new/edited files set definition, replacing any previous // Add the new/edited files set definition, replacing any previous
// definition with the same name and refreshing the display. // definition with the same name and refreshing the display.
this.replaceFilesSet(selectedSet, selectedSet.getName(), selectedSet.getDescription(), selectedSet.ignoresKnownFiles(), selectedSet.ingoresUnallocatedSpace(), rules); this.replaceFilesSet(selectedSet, selectedSet, rules);
// Select the new/edited rule. Queue it up so it happens after the // Select the new/edited rule. Queue it up so it happens after the
// selection listeners react to the selection of the "new" files // selection listeners react to the selection of the "new" files
@ -520,27 +515,36 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
* owned by this panel. If there is a definition with the same name, it will * owned by this panel. If there is a definition with the same name, it will
* be replaced, so this is an add/edit operation. * be replaced, so this is an add/edit operation.
* *
* @param oldSet A set to replace, null if the new set is * @param oldSet A set to replace, null if the new set is not a
* not a replacement. * replacement.
* @param name The name of the files set. * @param newSet The new set of rules.
* @param description The description of the files set. * @param rules The set membership rules for the set. If null,
* @param ignoresKnownFiles Whether or not the files set ignores * the rules in the new set will be used.
* known files.
* @param rules The set membership rules for the set.
* @param processesUnallocatedSpace Whether or not this set of rules
* processes unallocated space
*/ */
void replaceFilesSet(FilesSet oldSet, String name, String description, boolean ignoresKnownFiles, boolean ignoresUnallocatedSpace, Map<String, FilesSet.Rule> rules) { private void replaceFilesSet(FilesSet oldSet, FilesSet newSet, Map<String, FilesSet.Rule> rules) {
if (oldSet != null) { if (oldSet != null) {
// Remove the set to be replaced from the working copy if the files // Remove the set to be replaced from the working copy if the files
// set definitions. // set definitions.
this.filesSets.remove(oldSet.getName()); this.filesSets.remove(oldSet.getName());
} }
FilesSet setToAdd = newSet;
// Make the new/edited set definition and add it to the working copy of // Make the new/edited set definition and add it to the working copy of
// the files set definitions. // the files set definitions.
FilesSet newSet = new FilesSet(name, description, ignoresKnownFiles, ignoresUnallocatedSpace, rules); if (rules != null) {
this.filesSets.put(newSet.getName(), newSet); setToAdd = new FilesSet(
newSet.getName(),
newSet.getDescription(),
newSet.ignoresKnownFiles(),
newSet.ingoresUnallocatedSpace(),
rules,
newSet.isStandardSet(),
newSet.getVersionNumber()
);
}
this.filesSets.put(setToAdd.getName(), setToAdd);
// Redo the list model for the files set list component, which will make // Redo the list model for the files set list component, which will make
// everything stays sorted as in the working copy tree set. // everything stays sorted as in the working copy tree set.
@ -552,7 +556,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
// Select the new/edited files set definition in the set definitions // Select the new/edited files set definition in the set definitions
// list. This will cause the selection listeners to repopulate the // list. This will cause the selection listeners to repopulate the
// subordinate components. // subordinate components.
this.setsList.setSelectedValue(newSet, true); this.setsList.setSelectedValue(setToAdd, true);
} }
/** /**
@ -1058,7 +1062,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
Map<String, FilesSet.Rule> rules = new HashMap<>(oldSet.getRules()); Map<String, FilesSet.Rule> rules = new HashMap<>(oldSet.getRules());
FilesSet.Rule selectedRule = this.rulesList.getSelectedValue(); FilesSet.Rule selectedRule = this.rulesList.getSelectedValue();
rules.remove(selectedRule.getUuid()); rules.remove(selectedRule.getUuid());
this.replaceFilesSet(oldSet, oldSet.getName(), oldSet.getDescription(), oldSet.ignoresKnownFiles(), oldSet.ingoresUnallocatedSpace(), rules); this.replaceFilesSet(oldSet, oldSet, rules);
if (!this.rulesListModel.isEmpty()) { if (!this.rulesListModel.isEmpty()) {
this.rulesList.setSelectedIndex(0); this.rulesList.setSelectedIndex(0);
} else { } else {
@ -1111,13 +1115,8 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.doFileSetsDialog(this.setsList.getSelectedValue(), true); this.doFileSetsDialog(this.setsList.getSelectedValue(), true);
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_copySetButtonActionPerformed }//GEN-LAST:event_copySetButtonActionPerformed
@NbBundle.Messages({ @NbBundle.Messages({
"FilesSetDefsPanel.yesOwMsg=Yes, overwrite",
"FilesSetDefsPanel.noSkipMsg=No, skip",
"FilesSetDefsPanel.cancelImportMsg=Cancel import",
"# {0} - FilesSet name",
"FilesSetDefsPanel.interesting.overwriteSetPrompt=Interesting files set <{0}> already exists locally, overwrite?",
"FilesSetDefsPanel.interesting.importOwConflict=Import Interesting files set conflict",
"FilesSetDefsPanel.interesting.failImportMsg=Interesting files set not imported", "FilesSetDefsPanel.interesting.failImportMsg=Interesting files set not imported",
"FilesSetDefsPanel.interesting.fileExtensionFilterLbl=Autopsy Interesting File Set File (xml)", "FilesSetDefsPanel.interesting.fileExtensionFilterLbl=Autopsy Interesting File Set File (xml)",
"FilesSetDefsPanel.interesting.importButtonAction.featureName=Interesting Files Set Import" "FilesSetDefsPanel.interesting.importButtonAction.featureName=Interesting Files Set Import"
@ -1157,28 +1156,14 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
logger.log(Level.WARNING, "No Interesting files set definitions were read from the selected file, exception", ex); logger.log(Level.WARNING, "No Interesting files set definitions were read from the selected file, exception", ex);
return; return;
} }
for (FilesSet set : importedSets) {
int choice = JOptionPane.OK_OPTION; importedSets = importedSets
if (filesSets.containsKey(set.getName())) { .stream()
Object[] options = {NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.yesOwMsg"), .map((filesSet) -> StandardInterestingFilesSetsLoader.getAsStandardFilesSet(filesSet, false))
NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.noSkipMsg"), .collect(Collectors.toList());
NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.cancelImportMsg")};
choice = JOptionPane.showOptionDialog(this, FilesSet newSelected = determineFilesToImport(importedSets);
NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.overwriteSetPrompt", set.getName()),
NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.interesting.importOwConflict"),
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]);
}
if (choice == JOptionPane.OK_OPTION) {
selectedSet = set;
this.filesSets.put(set.getName(), set);
} else if (choice == JOptionPane.CANCEL_OPTION) {
break;
}
}
// Redo the list model for the files set list component // Redo the list model for the files set list component
FilesSetDefsPanel.this.setsListModel.clear(); FilesSetDefsPanel.this.setsListModel.clear();
this.filesSets.values().forEach((set) -> { this.filesSets.values().forEach((set) -> {
@ -1187,12 +1172,234 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
// Select the new/edited files set definition in the set definitions // Select the new/edited files set definition in the set definitions
// list. This will cause the selection listeners to repopulate the // list. This will cause the selection listeners to repopulate the
// subordinate components. // subordinate components.
this.setsList.setSelectedValue(selectedSet, true); this.setsList.setSelectedValue(newSelected == null ? selectedSet : newSelected, true);
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
} }
}//GEN-LAST:event_importSetButtonActionPerformed }//GEN-LAST:event_importSetButtonActionPerformed
/**
* From the files sets that can be imported, this method rectifies any
* conflicts that may occur.
*
* @param importedSets The sets to be imported.
*
* @return The files set to be selected or null if no items imported.
*/
private FilesSet determineFilesToImport(Collection<FilesSet> importedSets) {
FilesSet selectedSet = null;
for (FilesSet set : importedSets) {
Pair<FilesSet, Integer> conflictResult = handleConflict(set, true);
int choice = conflictResult.getRight();
FilesSet resultingFilesSet = conflictResult.getLeft();
if (choice == JOptionPane.OK_OPTION) {
selectedSet = resultingFilesSet;
this.filesSets.put(resultingFilesSet.getName(), resultingFilesSet);
} else if (choice == JOptionPane.CANCEL_OPTION) {
break;
}
}
return selectedSet;
}
/**
* Handles any possible conflicts that may arise from importing a files set.
*
* @param set The set to potentially import.
* @param isImport The set with which to handle the conflict is being
* imported, otherwise this is a set to be added from the
* "New Set" button.
*
* @return A pair of the files set to be imported (or null if none) and the
* integer corresponding to the JOptionPane choice of the
* JOptionPane.YES_NO_CANCEL option.
*/
private Pair<FilesSet, Integer> handleConflict(FilesSet set, boolean isImport) {
FilesSet conflict = this.filesSets.get(set.getName());
// if no conflict, return the files set as is with the option to proceed
if (conflict == null) {
return Pair.of(set, JOptionPane.OK_OPTION);
}
if (isImport) {
if (conflict.isStandardSet()) {
return onImportStandardSetConflict(set);
} else {
return onImportConflict(set);
}
} else {
if (conflict.isStandardSet()) {
return onNewEditSetStandardSetConflict(set);
} else {
return onNewEditSetConflict(set);
}
}
}
/**
* When a user imports a files set and the files set name collides with a
* pre-existing files set (not a standard files set), the user is prompted
* for how they would like that handled (overwrite, skip, or cancel whole
* operation)
*
* @param set The set to be imported.
*
* @return a pair of the files set and the JOptionPane.YES_NO_CANCEL option
*/
@Messages({
"FilesSetDefsPanel.yesOwMsg=Yes, overwrite",
"FilesSetDefsPanel.noSkipMsg=No, skip",
"FilesSetDefsPanel.cancelImportMsg=Cancel import",
"# {0} - FilesSet name",
"FilesSetDefsPanel.interesting.overwriteSetPrompt=Interesting files set \"{0}\" already exists locally, overwrite?",
"FilesSetDefsPanel.interesting.importOwConflict=Import Interesting files set conflict",})
private Pair<FilesSet, Integer> onImportConflict(FilesSet set) {
// if there is a conflict, see if it is okay to overwrite.
Object[] options = {
Bundle.FilesSetDefsPanel_yesOwMsg(),
Bundle.FilesSetDefsPanel_noSkipMsg(),
Bundle.FilesSetDefsPanel_cancelImportMsg()
};
int conflictChoice = JOptionPane.showOptionDialog(this,
Bundle.FilesSetDefsPanel_interesting_overwriteSetPrompt(set.getName()),
Bundle.FilesSetDefsPanel_interesting_importOwConflict(),
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]);
if (conflictChoice == JOptionPane.OK_OPTION) {
// if so, just return the files set to be placed in the map overwriting what is currently present.
return Pair.of(set, conflictChoice);
}
return Pair.of(null, conflictChoice);
}
/**
* When a user imports a files set and the files set name collides with a
* pre-existing standard files set, the user is prompted for how they would
* like that handled (create files set with a " custom" suffix, skip, or
* cancel whole operation)
*
* @param set The set to be imported.
*
* @return a pair of the files set and the JOptionPane.YES_NO_CANCEL option
*/
@Messages({
"FilesSetDefsPanel.yesStandardFileConflictCreate=Yes, create",
"# {0} - FilesSet name",
"# {1} - New FilesSet name",
"FilesSetDefsPanel.interesting.standardFileConflict=A standard interesting file set already exists with the name \"{0}.\" Would you like to rename your set to \"{1}?\"",})
private Pair<FilesSet, Integer> onImportStandardSetConflict(FilesSet set) {
// if there is a conflict and the conflicting files set is a standard files set,
// see if allowing a custom files set is okay.
Object[] options = {
Bundle.FilesSetDefsPanel_yesStandardFileConflictCreate(),
Bundle.FilesSetDefsPanel_noSkipMsg(),
Bundle.FilesSetDefsPanel_cancelImportMsg()
};
String setName = set.getName();
String customSetName = Bundle.StandardInterestingFileSetsLoader_customSuffixed(set.getName());
int conflictChoice = JOptionPane.showOptionDialog(this,
Bundle.FilesSetDefsPanel_interesting_standardFileConflict(setName, customSetName),
Bundle.FilesSetDefsPanel_interesting_importOwConflict(),
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]);
// if it is okay to create with custom prefix, try again to see if there is a conflict.
if (conflictChoice == JOptionPane.OK_OPTION) {
return handleConflict(StandardInterestingFilesSetsLoader.getAsCustomFileSet(set), true);
}
return Pair.of(null, conflictChoice);
}
/**
* When a user creates a files set or edits a files set and the files set
* name collides with a pre-existing files set (not a standard files set),
* the user is prompted for how they would like that handled (overwrite or
* cancel whole operation)
*
* @param set The set to be added.
*
* @return a pair of the files set and the JOptionPane.YES_NO_CANCEL option
*/
@Messages({
"FilesSetDefsPanel.cancelNewSetMsg=Cancel",
"FilesSetDefsPanel.interesting.newOwConflict=Interesting files set conflict",})
private Pair<FilesSet, Integer> onNewEditSetConflict(FilesSet set) {
// if there is a conflict, see if it is okay to overwrite.
Object[] options = {
Bundle.FilesSetDefsPanel_yesOwMsg(),
Bundle.FilesSetDefsPanel_cancelNewSetMsg()
};
int conflictChoice = JOptionPane.showOptionDialog(this,
Bundle.FilesSetDefsPanel_interesting_overwriteSetPrompt(set.getName()),
Bundle.FilesSetDefsPanel_interesting_newOwConflict(),
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]);
if (conflictChoice == JOptionPane.OK_OPTION) {
// if so, just return the files set to be placed in the map overwriting what is currently present.
return Pair.of(set, conflictChoice);
}
return Pair.of(null, conflictChoice);
}
/**
* When a user creates a files set and the files set name collides with a
* pre-existing standard files set, the user is prompted for how they would
* like that handled (create files set with a " custom" suffix or cancel
* whole operation)
*
* @param set The set to be adedd.
*
* @return a pair of the files set and the JOptionPane.YES_NO_CANCEL option
*/
private Pair<FilesSet, Integer> onNewEditSetStandardSetConflict(FilesSet set) {
// if there is a conflict and the conflicting files set is a standard files set,
// see if allowing a custom files set is okay.
Object[] options = {
Bundle.FilesSetDefsPanel_yesStandardFileConflictCreate(),
Bundle.FilesSetDefsPanel_cancelNewSetMsg()
};
String setName = set.getName();
String customSetName = Bundle.StandardInterestingFileSetsLoader_customSuffixed(set.getName());
int conflictChoice = JOptionPane.showOptionDialog(this,
Bundle.FilesSetDefsPanel_interesting_standardFileConflict(setName, customSetName),
Bundle.FilesSetDefsPanel_interesting_newOwConflict(),
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]);
// if it is okay to create with custom prefix, try again to see if there is a conflict.
if (conflictChoice == JOptionPane.OK_OPTION) {
return handleConflict(StandardInterestingFilesSetsLoader.getAsCustomFileSet(set), false);
}
return Pair.of(null, conflictChoice);
}
@NbBundle.Messages({"FilesSetDefsPanel.interesting.exportButtonAction.featureName=Interesting Files Set Export", @NbBundle.Messages({"FilesSetDefsPanel.interesting.exportButtonAction.featureName=Interesting Files Set Export",
"# {0} - file name", "# {0} - file name",
"FilesSetDefsPanel.exportButtonActionPerformed.fileExistPrompt=File {0} exists, overwrite?", "FilesSetDefsPanel.exportButtonActionPerformed.fileExistPrompt=File {0} exists, overwrite?",

View File

@ -26,6 +26,7 @@ import java.io.Serializable;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -35,6 +36,7 @@ import java.util.regex.PatternSyntaxException;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.openide.util.io.NbObjectInputStream; import org.openide.util.io.NbObjectInputStream;
import org.openide.util.io.NbObjectOutputStream; import org.openide.util.io.NbObjectOutputStream;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -46,9 +48,13 @@ import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.MetaTypeCond
import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.MimeTypeCondition; import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.MimeTypeCondition;
import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.ParentPathCondition; import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.ParentPathCondition;
import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.DateCondition; import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.DateCondition;
import org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSetsManagerException;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import java.util.Comparator;
import java.util.function.Function;
import java.util.stream.Collectors;
class InterestingItemsFilesSetSettings implements Serializable { class InterestingItemsFilesSetSettings implements Serializable {
@ -79,6 +85,8 @@ class InterestingItemsFilesSetSettings implements Serializable {
private static final Logger logger = Logger.getLogger(InterestingItemsFilesSetSettings.class.getName()); private static final Logger logger = Logger.getLogger(InterestingItemsFilesSetSettings.class.getName());
private static final String TYPE_FILTER_ATTR = "typeFilter"; //NON-NLS private static final String TYPE_FILTER_ATTR = "typeFilter"; //NON-NLS
private static final String EXTENSION_RULE_TAG = "EXTENSION"; //NON-NLS private static final String EXTENSION_RULE_TAG = "EXTENSION"; //NON-NLS
private static final String STANDARD_SET = "standardSet";
private static final String VERSION_NUMBER = "versionNumber";
private Map<String, FilesSet> filesSets; private Map<String, FilesSet> filesSets;
@ -378,6 +386,25 @@ class InterestingItemsFilesSetSettings implements Serializable {
if (!ignoreUnallocated.isEmpty()) { if (!ignoreUnallocated.isEmpty()) {
ignoreUnallocatedSpace = Boolean.parseBoolean(ignoreUnallocated); ignoreUnallocatedSpace = Boolean.parseBoolean(ignoreUnallocated);
} }
String isStandardSetString = setElem.getAttribute(STANDARD_SET);
boolean isStandardSet = false;
if (StringUtils.isNotBlank(isStandardSetString)) {
isStandardSet = Boolean.parseBoolean(isStandardSetString);
}
String versionNumberString = setElem.getAttribute(VERSION_NUMBER);
int versionNumber = 0;
if (StringUtils.isNotBlank(versionNumberString)) {
try {
versionNumber = Integer.parseInt(versionNumberString);
} catch (NumberFormatException ex) {
logger.log(Level.WARNING,
String.format("Unable to parse version number for files set named: %s with provided input: '%s'", setName, versionNumberString),
ex);
}
}
// Read the set membership rules, if any. // Read the set membership rules, if any.
Map<String, FilesSet.Rule> rules = new HashMap<>(); Map<String, FilesSet.Rule> rules = new HashMap<>();
NodeList allRuleElems = setElem.getChildNodes(); NodeList allRuleElems = setElem.getChildNodes();
@ -401,7 +428,7 @@ class InterestingItemsFilesSetSettings implements Serializable {
// Make the files set. Note that degenerate sets with no rules are // Make the files set. Note that degenerate sets with no rules are
// allowed to facilitate the separation of set definition and rule // allowed to facilitate the separation of set definition and rule
// definitions. A set without rules is simply the empty set. // definitions. A set without rules is simply the empty set.
FilesSet set = new FilesSet(setName, description, ignoreKnownFiles, ignoreUnallocatedSpace, rules); FilesSet set = new FilesSet(setName, description, ignoreKnownFiles, ignoreUnallocatedSpace, rules, isStandardSet, versionNumber);
filesSets.put(set.getName(), set); filesSets.put(set.getName(), set);
} }
// Note: This method takes a file path to support the possibility of // Note: This method takes a file path to support the possibility of
@ -446,31 +473,51 @@ class InterestingItemsFilesSetSettings implements Serializable {
* org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSetsManagerException * org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSetsManagerException
*/ */
static Map<String, FilesSet> readDefinitionsXML(File xmlFile) throws FilesSetsManager.FilesSetsManagerException { static Map<String, FilesSet> readDefinitionsXML(File xmlFile) throws FilesSetsManager.FilesSetsManagerException {
Map<String, FilesSet> filesSets = new HashMap<>();
if (!xmlFile.exists()) { if (!xmlFile.exists()) {
return filesSets; return new HashMap<>();
} }
// Check if the file can be read. // Check if the file can be read.
if (!xmlFile.canRead()) { if (!xmlFile.canRead()) {
logger.log(Level.SEVERE, "FilesSet definition file at {0} exists, but cannot be read", xmlFile.getPath()); // NON-NLS logger.log(Level.SEVERE, "FilesSet definition file at {0} exists, but cannot be read", xmlFile.getPath()); // NON-NLS
return filesSets; return new HashMap<>();
} }
// Parse the XML in the file.
Document doc = XMLUtil.loadDoc(InterestingItemsFilesSetSettings.class, xmlFile.getPath()); Document doc = XMLUtil.loadDoc(InterestingItemsFilesSetSettings.class, xmlFile.getPath());
return readDefinitionsXML(doc, xmlFile.getPath());
}
/**
* Reads an XML file and returns a map of fileSets. Allows for legacy XML
* support as well as importing of file sets to XMLs.
*
*
* @param doc The xml document.
*
* @return fileSets - a Map<String, Filesset> of the definition(s) found in
* the xml file for logging purposes (can provide null).
*
* @throws
* org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSetsManagerException
*/
static Map<String, FilesSet> readDefinitionsXML(Document doc, String resourceName) throws FilesSetsManager.FilesSetsManagerException {
// Parse the XML in the file.
Map<String, FilesSet> filesSets = new HashMap<>();
if (doc == null) { if (doc == null) {
logger.log(Level.SEVERE, "FilesSet definition file at {0}", xmlFile.getPath()); // NON-NLS logger.log(Level.SEVERE, "FilesSet definition file at {0}", resourceName); // NON-NLS
return filesSets; return filesSets;
} }
// Get the root element. // Get the root element.
Element root = doc.getDocumentElement(); Element root = doc.getDocumentElement();
if (root == null) { if (root == null) {
logger.log(Level.SEVERE, "Failed to get root {0} element tag of FilesSet definition file at {1}", new Object[]{FILE_SETS_ROOT_TAG, xmlFile.getPath()}); // NON-NLS logger.log(Level.SEVERE, "Failed to get root {0} element tag of FilesSet definition file at {1}",
new Object[]{FILE_SETS_ROOT_TAG, resourceName}); // NON-NLS
return filesSets; return filesSets;
} }
// Read in the files set definitions. // Read in the files set definitions.
NodeList setElems = root.getElementsByTagName(FILE_SET_TAG); NodeList setElems = root.getElementsByTagName(FILE_SET_TAG);
for (int i = 0; i < setElems.getLength(); ++i) { for (int i = 0; i < setElems.getLength(); ++i) {
readFilesSet((Element) setElems.item(i), filesSets, xmlFile.getPath()); readFilesSet((Element) setElems.item(i), filesSets, resourceName);
} }
return filesSets; return filesSets;
} }
@ -493,6 +540,34 @@ class InterestingItemsFilesSetSettings implements Serializable {
} }
return true; return true;
} }
/**
* Generates an alphabetically sorted list based on the provided collection and Function to retrieve a string field from each object.
* @param itemsToSort The items to be sorted into the newly generated list.
* @param getName The method to retrieve the given field from the object.
* @return The newly generated list sorted alphabetically by the given field.
*/
private static <T> List<T> sortOnField(Collection<T> itemsToSort, final Function<T, String> getName) {
Comparator<T> comparator = (a,b) -> {
String aName = getName.apply(a);
String bName = getName.apply(b);
if (aName == null) {
aName = "";
}
if (bName == null) {
bName = "";
}
return aName.compareToIgnoreCase(bName);
};
return itemsToSort.stream()
.sorted(comparator)
.collect(Collectors.toList());
}
/** /**
* Write the FilesSets to a file as an xml. * Write the FilesSets to a file as an xml.
@ -512,15 +587,27 @@ class InterestingItemsFilesSetSettings implements Serializable {
Element rootElement = doc.createElement(FILE_SETS_ROOT_TAG); Element rootElement = doc.createElement(FILE_SETS_ROOT_TAG);
doc.appendChild(rootElement); doc.appendChild(rootElement);
// Add the interesting files sets to the document. // Add the interesting files sets to the document.
for (FilesSet set : interestingFilesSets) {
List<FilesSet> sortedFilesSets = sortOnField(
interestingFilesSets,
filesSet -> filesSet == null ? null : filesSet.getName());
for (FilesSet set : sortedFilesSets) {
// Add the files set element and its attributes. // Add the files set element and its attributes.
Element setElement = doc.createElement(FILE_SET_TAG); Element setElement = doc.createElement(FILE_SET_TAG);
setElement.setAttribute(NAME_ATTR, set.getName()); setElement.setAttribute(NAME_ATTR, set.getName());
setElement.setAttribute(DESC_ATTR, set.getDescription()); setElement.setAttribute(DESC_ATTR, set.getDescription());
setElement.setAttribute(IGNORE_KNOWN_FILES_ATTR, Boolean.toString(set.ignoresKnownFiles())); setElement.setAttribute(IGNORE_KNOWN_FILES_ATTR, Boolean.toString(set.ignoresKnownFiles()));
setElement.setAttribute(STANDARD_SET, Boolean.toString(set.isStandardSet()));
setElement.setAttribute(VERSION_NUMBER, Integer.toString(set.getVersionNumber()));
// Add the child elements for the set membership rules. // Add the child elements for the set membership rules.
// All conditions of a rule will be written as a single element in the xml // All conditions of a rule will be written as a single element in the xml
for (FilesSet.Rule rule : set.getRules().values()) {
List<FilesSet.Rule> sortedRules = sortOnField(
set.getRules().values(),
rule -> rule == null ? null : rule.getName());
for (FilesSet.Rule rule : sortedRules) {
// Add a rule element with the appropriate name Condition // Add a rule element with the appropriate name Condition
// type tag. // type tag.
Element ruleElement; Element ruleElement;
@ -577,13 +664,13 @@ class InterestingItemsFilesSetSettings implements Serializable {
ruleElement.setAttribute(FS_SIZE_ATTR, Integer.toString(sizeCondition.getSizeValue())); ruleElement.setAttribute(FS_SIZE_ATTR, Integer.toString(sizeCondition.getSizeValue()));
ruleElement.setAttribute(FS_UNITS_ATTR, sizeCondition.getUnit().getName()); ruleElement.setAttribute(FS_UNITS_ATTR, sizeCondition.getUnit().getName());
} }
//Add the optional date condition //Add the optional date condition
DateCondition dateCondition = rule.getDateCondition(); DateCondition dateCondition = rule.getDateCondition();
if (dateCondition != null) { if (dateCondition != null) {
ruleElement.setAttribute(DAYS_INCLUDED_ATTR, Integer.toString(dateCondition.getDaysIncluded())); ruleElement.setAttribute(DAYS_INCLUDED_ATTR, Integer.toString(dateCondition.getDaysIncluded()));
} }
setElement.appendChild(ruleElement); setElement.appendChild(ruleElement);
} }
rootElement.appendChild(setElement); rootElement.appendChild(setElement);

View File

@ -0,0 +1,232 @@
/*
* 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.modules.interestingitems;
import java.io.File;
import java.io.FilenameFilter;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.openide.modules.InstalledFileLocator;
import org.openide.modules.OnStart;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* When the interesting items module loads, this runnable loads standard
* interesting file set rules.
*/
@OnStart
public class StandardInterestingFilesSetsLoader implements Runnable {
private static final Logger LOGGER = Logger.getLogger(StandardInterestingFilesSetsLoader.class.getName());
private static final String CONFIG_DIR = "InterestingFileSetRules";
private static final FilenameFilter DEFAULT_XML_FILTER = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".xml");
}
};
@Override
public void run() {
Map<String, FilesSet> standardInterestingFileSets = readStandardFileXML();
// Call FilesSetManager.getInterestingFilesSets() to get a Map<String, FilesSet> of the existing rule sets.
Map<String, FilesSet> userConfiguredSettings = null;
try {
userConfiguredSettings = FilesSetsManager.getInstance().getInterestingFilesSets();
} catch (FilesSetsManager.FilesSetsManagerException ex) {
LOGGER.log(Level.SEVERE, "Unable to properly read user-configured interesting files sets.", ex);
}
if (userConfiguredSettings == null) {
userConfiguredSettings = new HashMap<>();
}
// Add each FilesSet read from the standard rules set XML files that is missing from the Map to the Map.
copyOnNewer(standardInterestingFileSets, userConfiguredSettings, true);
try {
// Call FilesSetManager.setInterestingFilesSets with the updated Map.
FilesSetsManager.getInstance().setInterestingFilesSets(userConfiguredSettings);
} catch (FilesSetsManager.FilesSetsManagerException ex) {
LOGGER.log(Level.SEVERE, "Unable to write updated configuration for interesting files sets to config directory.", ex);
}
}
/**
* Reads xml definitions for each file found in the standard interesting
* file set config directory and marks the files set as a standard
* interesting file if it isn't already.
*
* @return The mapping of files set keys to the file sets.
*/
private static Map<String, FilesSet> readStandardFileXML() {
Map<String, FilesSet> standardInterestingFileSets = new HashMap<>();
File[] standardFileSets = InstalledFileLocator.getDefault()
.locate(CONFIG_DIR, StandardInterestingFilesSetsLoader.class.getPackage().getName(), false)
.listFiles(DEFAULT_XML_FILTER);
for (File standardFileSetsFile : standardFileSets) { //NON-NLS
try {
Map<String, FilesSet> thisFilesSet = InterestingItemsFilesSetSettings.readDefinitionsXML(standardFileSetsFile);
// ensure that read resources are standard sets
thisFilesSet = thisFilesSet.values()
.stream()
.map((filesSet) -> getAsStandardFilesSet(filesSet, true))
.collect(Collectors.toMap(FilesSet::getName, Function.identity()));
copyOnNewer(thisFilesSet, standardInterestingFileSets);
} catch (FilesSetsManager.FilesSetsManagerException ex) {
LOGGER.log(Level.WARNING, String.format("There was a problem importing the standard interesting file set at: %s.",
standardFileSetsFile.getAbsoluteFile()), ex);
}
}
return standardInterestingFileSets;
}
/**
* gets a copy of the Files Set forcing the standard file set flag to what
* is provided as a parameter.
*
* @param origFilesSet The fileset to get a copy of.
* @param standardFilesSet Whether or not the copy should be a standard
* files set.
*
* @return The copy.
*/
static FilesSet getAsStandardFilesSet(FilesSet origFilesSet, boolean standardFilesSet) {
return new FilesSet(
origFilesSet.getName(),
origFilesSet.getDescription(),
origFilesSet.ignoresKnownFiles(),
origFilesSet.ingoresUnallocatedSpace(),
origFilesSet.getRules(),
standardFilesSet,
origFilesSet.getVersionNumber()
);
}
/**
* Copies the entries in the src map to the destination map if the src item
* has a newer version than what is in dest or no equivalent entry exists
* within the dest map.
*
* @param src The source map.
* @param dest The destination map.
*/
private static void copyOnNewer(Map<String, FilesSet> src, Map<String, FilesSet> dest) {
copyOnNewer(src, dest, false);
}
/**
* Copies the entries in the src map to the destination map if the src item
* has a newer version than what is in dest or no equivalent entry exists
* within the dest map.
*
* @param src The source map.
* @param dest The destination map.
* @param appendCustom On conflict, if one of the items is readonly and one
* is not, this flag can be set so the item that is not
* readonly will have " (custom)" appended.
*/
private static void copyOnNewer(Map<String, FilesSet> src, Map<String, FilesSet> dest, boolean appendCustom) {
for (Map.Entry<String, FilesSet> srcEntry : src.entrySet()) {
String key = srcEntry.getKey();
FilesSet srcFileSet = srcEntry.getValue();
FilesSet destFileSet = dest.get(key);
if (destFileSet != null) {
// If and only if there is a naming conflict with a user-defined rule set, append (Custom)
// to the user-defined rule set and add it back to the Map.
if (appendCustom && srcFileSet.isStandardSet() != destFileSet.isStandardSet()) {
if (srcFileSet.isStandardSet()) {
addCustomFile(dest, destFileSet);
dest.put(key, srcFileSet);
} else {
addCustomFile(dest, srcFileSet);
}
continue;
}
// Replace each FilesSet read from the standard rules set XML files that has a newer version
// number than the corresponding FilesSet in the Map with the updated FilesSet.
if (destFileSet.getVersionNumber() >= srcEntry.getValue().getVersionNumber()) {
continue;
}
}
dest.put(srcEntry.getKey(), srcEntry.getValue());
}
}
/**
* Adds an entry to the destination map where the name will be the same as
* the key with " (custom)" appended.
*
* @param dest The destination map.
* @param srcFilesSet The FilesSet to append as custom. A non-readonly
* filesset must be provided.
*/
private static void addCustomFile(Map<String, FilesSet> dest, FilesSet srcFilesSet) {
if (srcFilesSet.isStandardSet()) {
LOGGER.log(Level.SEVERE, "An attempt to create a custom file that was a standard set.");
return;
}
FilesSet srcToAdd = srcFilesSet;
do {
srcToAdd = getAsCustomFileSet(srcToAdd);
} while (dest.containsKey(srcToAdd.getName()));
dest.put(srcToAdd.getName(), srcToAdd);
}
/**
* Gets a copy of the FilesSet as a non-standard files set with " (custom)"
* appended.
*
* @param srcFilesSet The files set.
*
* @return The altered copy.
*/
@Messages({
"# {0} - filesSetName",
"StandardInterestingFileSetsLoader.customSuffixed={0} (Custom)"
})
static FilesSet getAsCustomFileSet(FilesSet srcFilesSet) {
String customKey = Bundle.StandardInterestingFileSetsLoader_customSuffixed(srcFilesSet.getName());
return new FilesSet(
customKey,
srcFilesSet.getDescription(),
srcFilesSet.ignoresKnownFiles(),
srcFilesSet.ingoresUnallocatedSpace(),
srcFilesSet.getRules(),
false,
srcFilesSet.getVersionNumber()
);
}
}

View File

@ -81,6 +81,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
static final boolean DEFAULT_CONFIG_KEEP_CORRUPTED_FILES = false; static final boolean DEFAULT_CONFIG_KEEP_CORRUPTED_FILES = false;
private static final String PHOTOREC_DIRECTORY = "photorec_exec"; //NON-NLS private static final String PHOTOREC_DIRECTORY = "photorec_exec"; //NON-NLS
private static final String PHOTOREC_SUBDIRECTORY = "bin"; //NON-NLS
private static final String PHOTOREC_EXECUTABLE = "photorec_win.exe"; //NON-NLS private static final String PHOTOREC_EXECUTABLE = "photorec_win.exe"; //NON-NLS
private static final String PHOTOREC_LINUX_EXECUTABLE = "photorec"; private static final String PHOTOREC_LINUX_EXECUTABLE = "photorec";
private static final String PHOTOREC_RESULTS_BASE = "results"; //NON-NLS private static final String PHOTOREC_RESULTS_BASE = "results"; //NON-NLS
@ -454,6 +455,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
return path; return path;
} }
/** /**
* Finds and returns the path to the executable, if able. * Finds and returns the path to the executable, if able.
* *
@ -468,7 +470,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
Path execName; Path execName;
String photorec_linux_directory = "/usr/bin"; String photorec_linux_directory = "/usr/bin";
if (PlatformUtil.isWindowsOS()) { if (PlatformUtil.isWindowsOS()) {
execName = Paths.get(PHOTOREC_DIRECTORY, PHOTOREC_EXECUTABLE); execName = Paths.get(PHOTOREC_DIRECTORY, PHOTOREC_SUBDIRECTORY, PHOTOREC_EXECUTABLE);
exeFile = InstalledFileLocator.getDefault().locate(execName.toString(), PhotoRecCarverFileIngestModule.class.getPackage().getName(), false); exeFile = InstalledFileLocator.getDefault().locate(execName.toString(), PhotoRecCarverFileIngestModule.class.getPackage().getName(), false);
} else { } else {
File usrBin = new File("/usr/bin/photorec"); File usrBin = new File("/usr/bin/photorec");

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