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"/>
</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
have it stored in a ZIP file. We'll extract it in place and remove
the ZIP file afterward. -->

View File

@ -2,7 +2,7 @@ Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.core/10
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties
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
AutoUpdate-Show-In-Client: true
AutoUpdate-Essential-Module: true

View File

@ -139,5 +139,5 @@ nbm.homepage=http://www.sleuthkit.org/
nbm.module.author=Brian Carrier
nbm.needs.restart=true
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
*
* Copyright 2011-2019 Basis Technology Corp.
* Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* 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()) {
for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) {
TagName tagName = entry.getValue();
TagSet tagSet = tagName.getTagSet();
TagSet tagSet = tagsManager.getTagSet(tagName);
// Show custom tags before predefined tags in the menu
if (tagSet != null) {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* 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.KeyEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.List;
import java.util.Map;
@ -36,7 +35,6 @@ import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.KeyStroke;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
@ -150,7 +148,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
}
);
try {
try {
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
List<String> standardTagNames = TagsManager.getStandardTagNames();
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) -> {
TagSet tagSet = null;
try {
tagSet = tagName.getTagSet();
tagSet = tagsManager.getTagSet(tagName);
} catch (TskCoreException ex) {
Logger.getLogger(GetTagNameAndCommentDialog.class
.getName()).log(Level.SEVERE, "Failed to get tag set", ex); //NON-NLS

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Copyright 2018-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* 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()) {
for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) {
TagName tagName = entry.getValue();
TagSet tagSet = tagName.getTagSet();
TagSet tagSet = tagsManager.getTagSet(tagName);
// Show custom tags before predefined tags in the menu
if (tagSet != null) {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2020 Basis Technology Corp.
* Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -56,7 +56,7 @@ public class TagsManager implements Closeable {
private final SleuthkitCase caseDb;
private static String DEFAULT_TAG_SET_NAME = "Project VIC";
private static final Object lock = new Object();
static {
@ -235,16 +235,16 @@ public class TagsManager implements Closeable {
public static String getNotableTagDisplayName() {
return TagNameDefinition.getNotableTagDisplayName();
}
/**
* Creates a new TagSetDefinition file.
*
*
* @param tagSetDef The tag set definition.
*
* @throws IOException
*
* @throws IOException
*/
public static void addTagSetDefinition(TagSetDefinition tagSetDef) throws IOException {
synchronized(lock) {
synchronized (lock) {
TagSetDefinition.writeTagSetDefinition(tagSetDef);
}
}
@ -267,20 +267,20 @@ public class TagsManager implements Closeable {
caseDb.addOrUpdateTagName(def.getDisplayName(), def.getDescription(), def.getColor(), def.getKnownStatus());
}
//Assume new case and add tag sets
for(TagSetDefinition setDef: TagSetDefinition.readTagSetDefinitions()) {
for (TagSetDefinition setDef : TagSetDefinition.readTagSetDefinitions()) {
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()));
}
if(!tagNameList.isEmpty()) {
if (!tagNameList.isEmpty()) {
taggingMgr.addTagSet(setDef.getName(), tagNameList);
}
}
}
} catch (TskCoreException 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);
}
@ -288,28 +288,41 @@ public class TagsManager implements Closeable {
tagName.saveToCase(caseDb);
}
}
/**
* Get a list of all tag sets currently in the case database.
*
*
* @return A list, possibly empty, of TagSet objects.
*
*
* @throws TskCoreException
*/
public List<TagSet> getAllTagSets() throws TskCoreException {
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
* 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.
*
*
* @return A new TagSet object.
*
* @throws TskCoreException
*
* @throws TskCoreException
*/
public TagSet addTagSet(String name, List<TagName> tagNameList) throws TskCoreException {
return caseDb.getTaggingManager().addTagSet(name, tagNameList);
@ -501,7 +514,7 @@ public class TagsManager implements Closeable {
* name to the case database.
*/
public TagName addTagName(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown knownStatus) throws TagNameAlreadyExistsException, TskCoreException {
synchronized(lock) {
synchronized (lock) {
try {
TagName tagName = caseDb.addOrUpdateTagName(displayName, description, color, knownStatus);
Set<TagNameDefinition> customTypes = TagNameDefinition.getTagNameDefinitions();

View File

@ -1,9 +1,9 @@
OpenIDE-Module-Name=Central Repository
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=\
Correlation Engine 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\
Central Repository ingest module and central database. \n\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.
CentralRepoCommentDialog.commentLabel.text=Comment:
CentralRepoCommentDialog.okButton.text=&OK

View File

@ -4,10 +4,10 @@ AddEditCentralRepoCommentAction.menuItemText.addEditCentralRepoCommentNoMD5=Add/
CentralRepoCommentDialog.title.addEditCentralRepoComment=Add/Edit Central Repository Comment
OpenIDE-Module-Name=Central Repository
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=\
Correlation Engine 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\
Central Repository ingest module and central database. \n\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.
CentralRepoCommentDialog.commentLabel.text=Comment:
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 {

View File

@ -234,7 +234,7 @@ public class Persona {
private static Persona createPersona(String name, String comment, PersonaStatus status) throws CentralRepoException {
// generate a UUID for the persona
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();
Long timeStampMillis = instant.toEpochMilli();
@ -248,22 +248,22 @@ public class Persona {
+ examiner.getId()
+ ")";
CentralRepository.getInstance().executeInsertSQL(insertClause);
getCRInstance().executeInsertSQL(insertClause);
return getPersonaByUUID(uuidStr);
}
/**
* Sets the comment of this persona.
*
* @param name The new comment.
* @param comment The new comment.
*
* @throws CentralRepoException If there is an error.
*/
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();
if (cr != null) {
cr.executeUpdateSQL(updateClause);
getCRInstance().executeUpdateSQL(updateClause);
}
}
@ -275,7 +275,7 @@ public class Persona {
* @throws CentralRepoException If there is an error.
*/
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();
if (cr != null) {
cr.executeUpdateSQL(updateClause);
@ -313,7 +313,9 @@ public class Persona {
/**
* 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
* Personas table.
@ -329,7 +331,7 @@ public class Persona {
String deleteSQL = "UPDATE personas SET status_id = " + PersonaStatus.DELETED.status_id + " WHERE id = " + this.id;
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
cr.executeDeleteSQL(deleteSQL);
cr.executeUpdateSQL(deleteSQL);
}
}
@ -395,7 +397,7 @@ public class Persona {
+ "WHERE p.uuid = '" + uuid + "'";
PersonaQueryCallback queryCallback = new PersonaQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeSelectSQL(queryClause, queryCallback);
Collection<Persona> personas = queryCallback.getPersonas();
@ -420,7 +422,7 @@ public class Persona {
" AND LOWER(p.name) LIKE " + "LOWER('%" + partialName + "%')" ;
PersonaQueryCallback queryCallback = new PersonaQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonas();
}
@ -485,7 +487,9 @@ public class Persona {
/**
* 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
* Personas table.
@ -535,7 +539,9 @@ public class Persona {
/**
* 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
* Personas table.
@ -580,7 +586,7 @@ public class Persona {
while (resultSet.next()) {
// 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);
}
}
@ -605,14 +611,14 @@ public class Persona {
Collection<CentralRepoAccount> accounts = PersonaAccount.getAccountsForPersona(this.getId());
for (CentralRepoAccount account : accounts) {
int corrTypeId = account.getAccountType().getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
CorrelationAttributeInstance.Type correlationType = getCRInstance().getCorrelationTypeById(corrTypeId);
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType);
String querySql = "SELECT DISTINCT case_id FROM " + tableName
+ " WHERE account_id = " + account.getId();
CaseForAccountInstanceQueryCallback queryCallback = new CaseForAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback);
getCRInstance().executeSelectSQL(querySql, queryCallback);
// Add any cases that aren't already on the list.
for (CorrelationCase corrCase : queryCallback.getCases()) {
@ -639,8 +645,8 @@ public class Persona {
while (resultSet.next()) {
// get Case for case_id
CorrelationCase correlationCase = CentralRepository.getInstance().getCaseById(resultSet.getInt("case_id"));
CorrelationDataSource correlationDatasource = CentralRepository.getInstance().getDataSourceById(correlationCase, resultSet.getInt("data_source_id"));
CorrelationCase correlationCase = getCRInstance().getCaseById(resultSet.getInt("case_id"));
CorrelationDataSource correlationDatasource = getCRInstance().getDataSourceById(correlationCase, resultSet.getInt("data_source_id"));
// Add data source to list if not already on it.
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());
for (CentralRepoAccount account : accounts) {
int corrTypeId = account.getAccountType().getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
CorrelationAttributeInstance.Type correlationType = getCRInstance().getCorrelationTypeById(corrTypeId);
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType);
String querySql = "SELECT case_id, data_source_id FROM " + tableName
+ " WHERE account_id = " + account.getId();
DatasourceForAccountInstanceQueryCallback queryCallback = new DatasourceForAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback);
getCRInstance().executeSelectSQL(querySql, queryCallback);
// Add any data sources that aren't already on the list.
for (CorrelationDataSource correlationDatasource : queryCallback.getDataSources()) {
@ -738,7 +744,7 @@ public class Persona {
private static String getPersonaFromInstanceTableQueryTemplate(CentralRepoAccount.CentralRepoAccountType crAccountType) throws CentralRepoException {
int corrTypeId = crAccountType.getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
CorrelationAttributeInstance.Type correlationType = getCRInstance().getCorrelationTypeById(corrTypeId);
String instanceTableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(correlationType);
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 {
Collection<Persona> personaList = new ArrayList<>();
Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = CentralRepository.getInstance().getAllAccountTypes();
Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = getCRInstance().getAllAccountTypes();
for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) {
String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType)
@ -770,7 +776,7 @@ public class Persona {
+ "AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback);
getCRInstance().executeSelectSQL(querySql, queryCallback);
// Add persona that aren't already on the list.
for (Persona persona : queryCallback.getPersonasList()) {
@ -794,7 +800,7 @@ public class Persona {
public static Collection<Persona> getPersonasForDataSource(CorrelationDataSource dataSource) throws CentralRepoException {
Collection<Persona> personaList = new ArrayList<>();
Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = CentralRepository.getInstance().getAllAccountTypes();
Collection<CentralRepoAccount.CentralRepoAccountType> accountTypes = getCRInstance().getAllAccountTypes();
for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) {
String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType)
@ -802,7 +808,7 @@ public class Persona {
+ "AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback();
CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback);
getCRInstance().executeSelectSQL(querySql, queryCallback);
// Add persona that aren't already on the list.
for (Persona persona : queryCallback.getPersonasList()) {
@ -814,4 +820,23 @@ public class Persona {
}
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.
*
* @param persona Persona for which the account is being added.
* @param account Account.
* @param persona Persona for which the account is being added.
* @param account Account.
* @param justification Reason for assigning the alias, may be null.
* @param confidence Confidence level.
* @param confidence Confidence level.
*
* @return PersonaAccount
*
* @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 {
CentralRepository cr = CentralRepository.getInstance();
if(cr == null) {
throw new CentralRepoException("Failed to add Persona, Central Repository is not enable");
}
CentralRepoExaminer currentExaminer = cr.getOrInsertExaminer(System.getProperty("user.name"));
CentralRepoExaminer currentExaminer = getCRInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli();
@ -151,14 +146,14 @@ public class PersonaAccount {
+ currentExaminer.getId()
+ ")";
CentralRepository.getInstance().executeInsertSQL(insertClause);
getCRInstance().executeInsertSQL(insertClause);
String queryClause = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ "WHERE persona_id = " + persona.getId()
+ " AND account_type_id = " + account.getAccountType().getAccountTypeId()
+ " AND account_unique_identifier = \"" + account.getIdentifier() + "\"";
+ " AND account_unique_identifier = '" + account.getIdentifier() + "'";
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeSelectSQL(queryClause, queryCallback);
Collection<PersonaAccount> accounts = queryCallback.getPersonaAccountsList();
if (accounts.size() != 1) {
@ -203,7 +198,7 @@ public class PersonaAccount {
);
// 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(
rs.getInt("account_id"),
crAccountType,
@ -249,19 +244,13 @@ public class PersonaAccount {
* persona_account.
*/
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) {
String queryClause = PERSONA_ACCOUNTS_QUERY_CLAUSE
+ " WHERE persona_accounts.persona_id = " + personaId;
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
getCRInstance().executeSelectSQL(queryClause, queryCallback);
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
}
return new ArrayList<>();
return queryCallback.getPersonaAccountsList();
}
/**
@ -279,16 +268,9 @@ public class PersonaAccount {
+ " WHERE persona_accounts.account_id = " + accountId
+ " AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
}
return new ArrayList<>();
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
getCRInstance().executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
}
/**
@ -308,15 +290,10 @@ public class PersonaAccount {
+ " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')"
+ " AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback);
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
getCRInstance().executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
return queryCallback.getPersonaAccountsList();
}
return new ArrayList<>();
}
/**
@ -335,14 +312,9 @@ public class PersonaAccount {
+ " AND type_name = '" + account.getAccountType().getTypeName() + "' "
+ " AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId();
CentralRepository cr = CentralRepository.getInstance();
if (cr != null) {
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
}
return new ArrayList<>();
PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
getCRInstance().executeSelectSQL(queryClause, queryCallback);
return queryCallback.getPersonaAccountsList();
}
/**
@ -351,36 +323,24 @@ public class PersonaAccount {
* @param id row id for the account to be removed
*
* @throws CentralRepoException If there is an error in removing the
* account.
* account.
*/
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;
cr.executeDeleteSQL(deleteClause);
getCRInstance().executeDeleteSQL(deleteClause);
}
/**
* Modifies the PersonaAccount row by the given id
*
* @param id row id for the account to be removed
*
* @throws CentralRepoException If there is an error in removing the
* account.
* account.
*/
static void modifyPersonaAccount(long id, Persona.Confidence confidence, String justification) throws CentralRepoException {
CentralRepository cr = CentralRepository.getInstance();
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);
String updateClause = "UPDATE persona_accounts SET confidence_id = " + confidence.getLevelId() + ", justification = '" + justification + "' WHERE id = " + id;
getCRInstance().executeUpdateSQL(updateClause);
}
/**
@ -397,7 +357,7 @@ public class PersonaAccount {
while (rs.next()) {
// 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(
rs.getInt("account_id"),
crAccountType,
@ -418,28 +378,41 @@ public class PersonaAccount {
* @param personaId Id of the persona to look for.
*
* @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
* accounts.
* accounts.
*/
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) {
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;
AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback();
getCRInstance().executeSelectSQL(queryClause, queryCallback);
AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback();
cr.executeSelectSQL(queryClause, queryCallback);
return queryCallback.getAccountsList();
}
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 {
CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name"));
CentralRepoExaminer examiner = getCRInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli();
@ -113,16 +113,16 @@ public class PersonaAlias {
+ examiner.getId()
+ ")";
CentralRepository.getInstance().executeInsertSQL(insertClause);
getCRInstance().executeInsertSQL(insertClause);
String queryClause = SELECT_QUERY_BASE
+ "WHERE pa.persona_id = " + persona.getId()
+ " AND pa.alias = \"" + alias + "\""
+ " AND pa.alias = '" + alias + "'"
+ " AND pa.date_added = " + timeStampMillis
+ " AND pa.examiner_id = " + examiner.getId();
PersonaAliasesQueryCallback queryCallback = new PersonaAliasesQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeSelectSQL(queryClause, queryCallback);
Collection<PersonaAlias> aliases = queryCallback.getAliases();
if (aliases.size() != 1) {
@ -141,7 +141,7 @@ public class PersonaAlias {
*/
static void removePersonaAlias(PersonaAlias alias) throws CentralRepoException {
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");
}
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);
}
@ -208,9 +208,26 @@ public class PersonaAlias {
String queryClause = SELECT_QUERY_BASE + "WHERE pa.persona_id = " + personaId;
PersonaAliasesQueryCallback queryCallback = new PersonaAliasesQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeSelectSQL(queryClause, queryCallback);
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 {
CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name"));
CentralRepoExaminer examiner = getCRInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli();
@ -123,17 +123,17 @@ public class PersonaMetadata {
+ examiner.getId()
+ ")";
CentralRepository.getInstance().executeInsertSQL(insertClause);
getCRInstance().executeInsertSQL(insertClause);
String queryClause = SELECT_QUERY_BASE
+ "WHERE pmd.persona_id = " + personaId
+ " AND pmd.name = \"" + name + "\""
+ " AND pmd.value = \"" + value + "\""
+ " AND pmd.name = '" + name + "'"
+ " AND pmd.value = '" + value + "'"
+ " AND pmd.date_added = " + timeStampMillis
+ " AND pmd.examiner_id = " + examiner.getId();
PersonaMetadataQueryCallback queryCallback = new PersonaMetadataQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeSelectSQL(queryClause, queryCallback);
Collection<PersonaMetadata> metadata = queryCallback.getMetadataList();
if (metadata.size() != 1) {
@ -152,7 +152,7 @@ public class PersonaMetadata {
*/
static void removePersonaMetadata(PersonaMetadata metadata) throws CentralRepoException {
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");
}
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);
}
@ -219,10 +219,27 @@ public class PersonaMetadata {
String queryClause = SELECT_QUERY_BASE + "WHERE pmd.persona_id = " + personaId;
PersonaMetadataQueryCallback queryCallback = new PersonaMetadataQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
getCRInstance().executeSelectSQL(queryClause, queryCallback);
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();
}
TimingMetric timingMetric = HealthMonitor.getTimingMetric("Correlation Engine: Bulk insert");
TimingMetric timingMetric = HealthMonitor.getTimingMetric("Central Repository: Bulk insert");
HealthMonitor.submitTimingMetric(timingMetric);
// Reset state

View File

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

View File

@ -73,7 +73,7 @@ import org.sleuthkit.datamodel.CommunicationsUtils;
* Listen for ingest events and update entries in the Central Repository
* database accordingly
*/
@NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Correlation Engine"})
@NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Central Repository"})
public class IngestEventsListener {
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
* Correlation Engine.
* Central Repository.
*/
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
* Correlation Engine.
* Central Repository.
*/
public synchronized static void decrementCorrelationEngineModuleCount() {
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.
*/
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.
*
* @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() {
return correlationModuleInstanceCount;
@ -282,7 +282,7 @@ public class IngestEventsListener {
@Override
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
//in these cases we still want to create correlation attributesForNewArtifact for those artifacts when appropriate
if (!IngestManager.getInstance().isIngestRunning() || getCeModuleInstanceCount() > 0) {
@ -349,7 +349,7 @@ public class IngestEventsListener {
if (getCeModuleInstanceCount() == 0) {
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

View File

@ -1,6 +1,6 @@
CentralRepoIngestModel_name_header=Name:<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.prevCaseComment.text=Previous Case:
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
CentralRepoIngestModule_postToBB_knownBadMsg=Notable: {0}
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.flagTaggedNotableItemsCheckbox.text=Flag items previously tagged as notable
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;
/**
* Instantiate the Correlation Engine ingest module.
* Instantiate the Central Repository ingest module.
*
* @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) {
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);
HealthMonitor.submitTimingMetric(timingMetric);
if (!caseDisplayNamesList.isEmpty()) {
@ -220,7 +220,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
// see ArtifactManagerTimeTester for details
@Messages({
"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
public void startUp(IngestJobContext context) throws IngestModuleException {
@ -235,7 +235,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
* posited.
*
* 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
* notable items, and the proceeding module (with flagging disabled)
* 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
if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE)
&& (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
}
jobId = context.getJobId();

View File

@ -33,7 +33,7 @@ import org.sleuthkit.autopsy.ingest.NoIngestModuleIngestJobSettings;
* Factory for Central Repository ingest modules
*/
@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"})
public class CentralRepoIngestModuleFactory extends IngestModuleFactoryAdapter {

View File

@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.centralrepository.ingestmodule;
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 {

View File

@ -22,7 +22,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
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
final class IngestSettingsPanel extends IngestModuleIngestJobSettingsPanel {

View File

@ -1,15 +1,6 @@
CTL_OpenPersonaManager=Persona Manager
CTL_PersonaManagerTopComponentAction=Persona Manager
CTL_OpenPersonas=Personas
CTL_PersonasTopComponentAction=Personas
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.okBtn.text=OK
PersonaDetailsPanel.casesLbl.text=Cases found in:
@ -27,7 +18,6 @@ PersonaDetailsPanel.nameLbl.text=Name:
AddAliasDialog.accountsLbl.text=Account:
AddAliasDialog.okBtn.text=OK
AddAliasDialog.cancelBtn.text=Cancel
PersonaManagerTopComponent.deleteBtn.text=Delete Persona
PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add
@ -73,3 +63,20 @@ PersonaMetadataDialog.cancelBtn.text=Cancel
PersonaDetailsPanel.editAccountBtn.text=Edit
PersonaDetailsPanel.editMetadataBtn.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_empty_name_msg=A metadata entry cannot have an empty name or value.
AddMetadataDialog_empty_name_Title=Missing field(s)
CTL_OpenPersonaManager=Persona Manager
CTL_PersonaManagerTopComponentAction=Persona Manager
CreatePersonaAccountDialog.title.text=Create Account
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
OpenPersonasAction.displayName=Persona Manager
OpenPersonasAction.displayName=Personas
PersonaAccountDialog.title.text=Add Account
PersonaAccountDialog_dup_msg=This account is already added to the persona.
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_empty_msg=An alias cannot be empty.
PersonaAliasDialog_empty_Title=Empty alias
PersonaDetailsDialog.cancelBtn.text=Cancel
PersonaDetailsDialog.okBtn.text=OK
PersonaDetailsDialogCreateTitle=Create Persona
PersonaDetailsDialogEditTitle=Edit 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.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add
@ -64,7 +48,6 @@ PersonaDetailsPanel.nameLbl.text=Name:
AddAliasDialog.accountsLbl.text=Account:
AddAliasDialog.okBtn.text=OK
AddAliasDialog.cancelBtn.text=Cancel
PersonaManagerTopComponent.deleteBtn.text=Delete Persona
PersonaDetailsPanel.casesLbl.text=Cases found in:
PersonaDetailsPanel.deleteAliasBtn.text=Delete
PersonaDetailsPanel.addAliasBtn.text=Add
@ -98,6 +81,18 @@ PersonaAliasDialog.justificationLbl.text=Justification:
PersonaAliasDialog.aliasTextField.text=
PersonaAliasDialog.aliasLbl.text=Alias:
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.justificationTextField.text=
PersonaMetadataDialog.justificationLbl.text=Justification:
@ -110,10 +105,27 @@ PersonaMetadataDialog.cancelBtn.text=Cancel
PersonaDetailsPanel.editAccountBtn.text=Edit
PersonaDetailsPanel.editMetadataBtn.text=Edit
PersonaDetailsPanel.editAliasBtn.text=Edit
PMTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona?
PMTopComponent_delete_confirmation_Title=Are you sure?
PMTopComponent_delete_exception_msg=Failed to delete persona.
PMTopComponent_delete_exception_Title=Delete failure
PMTopComponent_Name=Persona Manager
PMTopComponent_search_exception_msg=Failed to search personas.
PMTopComponent_search_exception_Title=Search failure
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.
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
PersonaMetadataDialog.okBtn.text=OK
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.
*/
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.centralrepository.persona.OpenPersonaManagerAction")
@ActionRegistration(displayName = "#CTL_OpenPersonaManager", lazy = false)
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.centralrepository.persona.OpenPersonasAction")
@ActionRegistration(displayName = "#CTL_OpenPersonas", lazy = false)
@ActionReferences(value = {
@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 final JMenuItem menuItem;
public OpenPersonaManagerAction() {
public OpenPersonasAction() {
menuItem = super.getMenuPresenter();
this.setEnabled(CentralRepository.isEnabled());
}
@ -54,7 +54,7 @@ public final class OpenPersonaManagerAction extends CallableSystemAction {
@Override
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void performAction() {
final TopComponent topComponent = WindowManager.getDefault().findTopComponent("PersonaManagerTopComponent");
final TopComponent topComponent = WindowManager.getDefault().findTopComponent("PersonasTopComponent");
if (topComponent != null) {
if (topComponent.isOpened() == false) {
topComponent.open();
@ -65,7 +65,7 @@ public final class OpenPersonaManagerAction extends CallableSystemAction {
}
@Override
@NbBundle.Messages("OpenPersonasAction.displayName=Persona Manager")
@NbBundle.Messages("OpenPersonasAction.displayName=Personas")
public String getName() {
return Bundle.OpenPersonasAction_displayName();
}

View File

@ -1,35 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="searchButtonGroup">
</Component>
</NonVisualComponents>
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[400, 400]"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
@ -46,17 +23,52 @@
<Layout>
<DimensionLayout dim="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>
</DimensionLayout>
<DimensionLayout dim="1">
<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>
</DimensionLayout>
</Layout>
<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"/>
<SubComponents>
@ -70,16 +82,10 @@
<Layout>
<DimensionLayout dim="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"/>
<Group type="103" groupAlignment="0" 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="32767" attributes="0"/>
<Component id="deleteBtn" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="createButtonSeparator" max="32767" attributes="0"/>
<Component id="resultsPane" pref="0" max="32767" attributes="0"/>
<Component id="searchField" max="32767" attributes="0"/>
<Group type="102" attributes="0">
@ -89,6 +95,19 @@
<EmptySpace max="32767" attributes="0"/>
<Component id="searchBtn" min="-2" max="-2" attributes="0"/>
</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>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -106,7 +125,7 @@
<Component id="searchBtn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<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"/>
<Group type="103" groupAlignment="3" 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"/>
</Group>
<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>
</DimensionLayout>
@ -122,7 +145,7 @@
<Component class="javax.swing.JTextField" name="searchField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/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>
</Properties>
</Component>
@ -133,7 +156,7 @@
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/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>
</Properties>
</Component>
@ -143,7 +166,14 @@
<ComponentRef name="searchButtonGroup"/>
</Property>
<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>
</Properties>
</Component>
@ -157,20 +187,20 @@
<Component class="javax.swing.JTable" name="resultsTable">
<Properties>
<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 name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0">
<Column maxWidth="25" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/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>
<Editor/>
<Renderer/>
</Column>
<Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true">
<Title editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/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>
<Editor/>
<Renderer/>
@ -184,48 +214,59 @@
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="searchBtn">
<Component class="javax.swing.JButton" name="createAccountBtn">
<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.searchBtn.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;)"/>
<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>
<AuxValues>
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
</AuxValues>
</Component>
<Component class="javax.swing.JButton" name="deleteBtn">
<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.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 name="enabled" type="boolean" value="false"/>
</Properties>
</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>
</Container>
<Component class="org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel" name="detailsPanel">
<Container class="javax.swing.JScrollPane" name="detailsScrollPane">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
</Constraint>
</Constraints>
</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>
</Container>
</SubComponents>

View File

@ -44,27 +44,29 @@ import org.sleuthkit.autopsy.coreutils.Logger;
* Top component for the Personas tool
*
*/
@TopComponent.Description(preferredID = "PersonaManagerTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER)
@TopComponent.Registration(mode = "personamanager", openAtStartup = false)
@RetainLocation("personamanager")
@TopComponent.Description(preferredID = "PersonasTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER)
@TopComponent.Registration(mode = "personas", openAtStartup = false)
@RetainLocation("personas")
@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 Persona selectedPersona = null;
@Messages({
"PMTopComponent_Name=Persona Manager",
"PMTopComponent_delete_exception_Title=Delete failure",
"PMTopComponent_delete_exception_msg=Failed to delete persona.",
"PMTopComponent_delete_confirmation_Title=Are you sure?",
"PMTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona?",
"PersonasTopComponent_Name=Personas",
"PersonasTopComponent_delete_exception_Title=Delete failure",
"PersonasTopComponent_delete_exception_msg=Failed to delete persona.",
"PersonasTopComponent_delete_confirmation_Title=Are you sure?",
"PersonasTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona?",
})
public PersonaManagerTopComponent() {
public PersonasTopComponent() {
initComponents();
setName(Bundle.PMTopComponent_Name());
setName(Bundle.PersonasTopComponent_Name());
executeSearch();
searchBtn.addActionListener(new ActionListener() {
@ -77,7 +79,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
editBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new PersonaDetailsDialog(PersonaManagerTopComponent.this,
new PersonaDetailsDialog(PersonasTopComponent.this,
PersonaDetailsMode.EDIT, selectedPersona, new CreateEditCallbackImpl());
}
});
@ -85,7 +87,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
createBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new PersonaDetailsDialog(PersonaManagerTopComponent.this,
new PersonaDetailsDialog(PersonasTopComponent.this,
PersonaDetailsMode.CREATE, selectedPersona, new CreateEditCallbackImpl());
}
});
@ -94,8 +96,8 @@ public final class PersonaManagerTopComponent extends TopComponent {
@Override
public void actionPerformed(ActionEvent e) {
NotifyDescriptor confirm = new NotifyDescriptor.Confirmation(
Bundle.PMTopComponent_delete_confirmation_msg(),
Bundle.PMTopComponent_delete_confirmation_Title(),
Bundle.PersonasTopComponent_delete_confirmation_msg(),
Bundle.PersonasTopComponent_delete_confirmation_Title(),
NotifyDescriptor.YES_NO_OPTION);
DialogDisplayer.getDefault().notify(confirm);
if (confirm.getValue().equals(NotifyDescriptor.YES_OPTION)) {
@ -105,9 +107,9 @@ public final class PersonaManagerTopComponent extends TopComponent {
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Failed to delete persona: " + selectedPersona.getName(), ex);
JOptionPane.showMessageDialog(PersonaManagerTopComponent.this,
Bundle.PMTopComponent_delete_exception_msg(),
Bundle.PMTopComponent_delete_exception_Title(),
JOptionPane.showMessageDialog(PersonasTopComponent.this,
Bundle.PersonasTopComponent_delete_exception_msg(),
Bundle.PersonasTopComponent_delete_exception_Title(),
JOptionPane.ERROR_MESSAGE);
return;
}
@ -132,6 +134,13 @@ public final class PersonaManagerTopComponent extends TopComponent {
searchAccountRadio.addActionListener((ActionEvent e) -> {
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({
"PMTopComponent_search_exception_Title=Search failure",
"PMTopComponent_search_exception_msg=Failed to search personas.",})
"PersonasTopComponent_search_exception_Title=Search failure",
"PersonasTopComponent_search_exception_msg=Failed to search personas.",})
private void executeSearch() {
Collection<Persona> results;
try {
@ -227,8 +236,8 @@ public final class PersonaManagerTopComponent extends TopComponent {
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Failed to search personas", ex);
JOptionPane.showMessageDialog(this,
Bundle.PMTopComponent_search_exception_Title(),
Bundle.PMTopComponent_search_exception_msg(),
Bundle.PersonasTopComponent_search_exception_Title(),
Bundle.PersonasTopComponent_search_exception_msg(),
JOptionPane.ERROR_MESSAGE);
return;
}
@ -254,49 +263,69 @@ public final class PersonaManagerTopComponent extends TopComponent {
private void initComponents() {
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();
searchField = new javax.swing.JTextField();
searchNameRadio = new javax.swing.JRadioButton();
searchAccountRadio = new javax.swing.JRadioButton();
searchBtn = new javax.swing.JButton();
resultsPane = new javax.swing.JScrollPane();
resultsTable = new javax.swing.JTable();
searchBtn = new javax.swing.JButton();
createAccountBtn = new javax.swing.JButton();
editBtn = new javax.swing.JButton();
createBtn = 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();
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);
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);
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);
resultsPane.setViewportView(resultsTable);
if (resultsTable.getColumnModel().getColumnCount() > 0) {
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(1).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.resultsTable.columnModel.title1")); // 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(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);
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(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.deleteBtn.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(deleteBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.deleteBtn.text")); // NOI18N
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);
searchPanel.setLayout(searchPanelLayout);
searchPanelLayout.setHorizontalGroup(
@ -304,12 +333,7 @@ public final class PersonaManagerTopComponent extends TopComponent {
.addGroup(searchPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(searchPanelLayout.createSequentialGroup()
.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(createButtonSeparator)
.addComponent(resultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(searchField)
.addGroup(searchPanelLayout.createSequentialGroup()
@ -317,7 +341,17 @@ public final class PersonaManagerTopComponent extends TopComponent {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(searchAccountRadio)
.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())
);
searchPanelLayout.setVerticalGroup(
@ -331,36 +365,52 @@ public final class PersonaManagerTopComponent extends TopComponent {
.addComponent(searchAccountRadio)
.addComponent(searchBtn))
.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)
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(editBtn)
.addComponent(createBtn)
.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())
);
jSplitPane1.setLeftComponent(searchPanel);
jSplitPane1.setRightComponent(detailsPanel);
mainSplitPane.setLeftComponent(searchPanel);
detailsScrollPane.setViewportView(detailsPanel);
mainSplitPane.setRightComponent(detailsScrollPane);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
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.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
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton createAccountBtn;
private javax.swing.JButton createBtn;
private javax.swing.JSeparator createButtonSeparator;
private javax.swing.JButton deleteBtn;
private org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel detailsPanel;
private javax.swing.JScrollPane detailsScrollPane;
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.JTable resultsTable;
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,
* 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
* any data sources are unprocessed. If the settings reflect that a
* 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
* current central repository instead of another. - Does not check that the
* 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.
*/
@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
for (CorrelationDataSource correlatedDataSource : correlatedDataSources) {
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);
break;
}
}
}
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()) {
if (correlationEngineModuleName.equals(ingestModuleInfo.getDisplayName())) {
dataSourceCorrelationMap.put(dataSource, CorrelatedStatus.CORRELATED);

View File

@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.communications.relationships;
import java.beans.PropertyChangeEvent;
import org.openide.explorer.ExplorerManager;
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.datamodel.BlackboardArtifact;

View File

@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.communications.relationships;
import java.beans.PropertyChangeEvent;
import org.openide.explorer.ExplorerManager;
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.datamodel.BlackboardArtifact;

View File

@ -25,7 +25,7 @@ import org.openide.explorer.ExplorerManager;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.casemodule.Case;
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.coreutils.Logger;
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.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.
ApplicationContentViewer.title=Application
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.progress.bufferingCancelled=media buffering was canceled
FXVideoPanel.progress.bufferingInterrupted=media buffering was interrupted
@ -107,11 +85,6 @@ MediaViewVideoPanel.progressLabel.text=00:00
MediaViewVideoPanel.infoLabel.text=info
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.truncated=(results truncated)
Metadata.nodeText.unknown=Unknown
@ -1054,46 +1027,4 @@ manager.properties.lafError =\
manager.properties.brokenProperty = Broken default property {0} value: {1}
manager.properties.missingProperty = Missing default property {0} value: {1}
DefaultArtifactContentViewer.copyMenuItem.text=Copy
DefaultArtifactContentViewer.selectAllMenuItem.text=Select All
ContactArtifactViewer.contactNameLabel.text=Joanna Doe
ContactArtifactViewer.phonesLabel.text=Phone
ContactArtifactViewer.emailsLabel.text=Email
ContactArtifactViewer.othersLabel.text=Other
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}
# {0} - tableName
SQLiteViewer.selectTable.errorText=\u6b21\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u884c\u30ab\u30a6\u30f3\u30c8\u3092\u53d6\u5f97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f: {0}
DefaultArtifactContentViewer.selectAllMenuItem.text=\u3059\u3079\u3066\u3092\u9078\u629e
DefaultArtifactContentViewer.copyMenuItem.text=\u30b3\u30d4\u30fc
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.
*/
@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 long serialVersionUID = 1L;
@ -54,7 +54,7 @@ final class HtmlPanel extends javax.swing.JPanel {
/**
* Creates new form HtmlViewerPanel
*/
HtmlPanel() {
public HtmlPanel() {
initComponents();
Platform.runLater(() -> {
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.
*/
void setHtmlText(String htmlText) {
public void setHtmlText(String htmlText) {
this.htmlText = htmlText;
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.
*/
void reset() {
public void reset() {
Platform.runLater(() -> {
webView.getEngine().loadContent("", TEXT_TYPE);
});

View File

@ -131,6 +131,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
private double zoomRatio;
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 = {
0.0625, 0.125, 0.25, 0.375, 0.5, 0.75,
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.setPopupSize(300, 150);
//Disable image tagging for non-windows users or upon failure to load OpenCV.
if (!PlatformUtil.isWindowsOS() || !OpenCvLoader.openCvIsLoaded()) {
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
* content viewer. For example, when the tags group is empty (no tags on image),
* disable delete menu item, hide menu item, and export menu item.
* content viewer. For example, when the tags group is empty (no tags on
* image), disable delete menu item, hide menu item, and export menu item.
*/
private void subscribeTagMenuItemsToStateChanges() {
pcs.addPropertyChangeListener((event) -> {
@ -394,6 +396,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}
try {
autoResize = true;
Image fxImage = readImageTask.get();
masterGroup.getChildren().clear();
tagsGroup.getChildren().clear();
@ -464,7 +467,9 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
* current file.
*
* @param contentTags
*
* @return
*
* @throws TskCoreException
* @throws NoCurrentCaseException
*/
@ -488,7 +493,9 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
* appropriate type.
*
* @param contentTags
*
* @return
*
* @throws TskCoreException
* @throws NoCurrentCaseException
*/
@ -685,16 +692,21 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}// </editor-fold>//GEN-END:initComponents
private void rotateLeftButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateLeftButtonActionPerformed
autoResize = false;
rotation = (rotation + 270) % 360;
updateView();
}//GEN-LAST:event_rotateLeftButtonActionPerformed
private void rotateRightButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateRightButtonActionPerformed
autoResize = false;
rotation = (rotation + 90) % 360;
updateView();
}//GEN-LAST:event_rotateRightButtonActionPerformed
private void zoomInButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed
autoResize = false;
// Find the next zoom step.
for (int i = 0; i < ZOOM_STEPS.length; i++) {
if (zoomRatio < ZOOM_STEPS[i]) {
@ -706,6 +718,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}//GEN-LAST:event_zoomInButtonActionPerformed
private void zoomOutButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed
autoResize = false;
// Find the next zoom step.
for (int i = ZOOM_STEPS.length - 1; i >= 0; i--) {
if (zoomRatio > ZOOM_STEPS[i]) {
@ -717,11 +730,16 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}//GEN-LAST:event_zoomOutButtonActionPerformed
private void zoomResetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomResetButtonActionPerformed
autoResize = true;
resetView();
}//GEN-LAST:event_zoomResetButtonActionPerformed
private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
updateView();
if (autoResize) {
resetView();
} else {
updateView();
}
}//GEN-LAST:event_formComponentResized
/**
@ -801,6 +819,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
* Creates an ImageTag instance from the ContentViewerTag.
*
* @param contentViewerTag
*
* @return
*/
private ImageTag buildImageTag(ContentViewerTag<ImageTagRegion> contentViewerTag) {
@ -883,17 +902,17 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
List<ContentTag> tags = Case.getCurrentCase().getServices()
.getTagsManager().getContentTagsByContent(file);
List<ContentViewerTag<ImageTagRegion>> contentViewerTags = getContentViewerTags(tags);
//Pull out image tag regions
Collection<ImageTagRegion> regions = contentViewerTags.stream()
.map(cvTag -> cvTag.getDetails()).collect(Collectors.toList());
//Apply tags to image and write to file
BufferedImage taggedImage = ImageTagsUtil.getImageWithTags(file, regions);
Path output = Paths.get(exportChooser.getSelectedFile().getPath(),
FilenameUtils.getBaseName(file.getName()) + "-with_tags.png"); //NON-NLS
ImageIO.write(taggedImage, "png", output.toFile());
JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport());
} 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.

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.
*/
class TranslatablePanel extends JPanel {
public class TranslatablePanel extends JPanel {
/**
* 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
* content to the provided content string.
*/
class TranslatablePanelException extends Exception {
public class TranslatablePanelException extends Exception {
public static final long serialVersionUID = 1L;
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
* 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.
* @return the root component to insert into the translatable panel
@ -191,7 +191,7 @@ class TranslatablePanel extends JPanel {
@Messages({"TranslatablePanel.comboBoxOption.originalText=Original Text",
"TranslatablePanel.comboBoxOption.translatedText=Translated Text"})
TranslatablePanel(ContentComponent contentComponent) {
public TranslatablePanel(ContentComponent contentComponent) {
this(
contentComponent,
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
* based on whether there is a provider.
*/
final void reset() {
public final void reset() {
setContent(null, null);
}
@ -272,7 +272,7 @@ class TranslatablePanel extends JPanel {
* @param content the content for the panel
* @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();
setTranslationEnabled();
this.translateComboBox.setSelectedIndex(0);

View File

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

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Component;
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
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Component;
import java.awt.GridBagConstraints;
@ -27,7 +27,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.JScrollPane;
@ -35,6 +34,7 @@ import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
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 GridBagConstraints m_constraints = new GridBagConstraints();
private final List<PersonaSearchAndDisplayTask> personaSearchtasks = new ArrayList<>();
private PersonaAccountFetcher currentAccountFetcher = null;
/**
* Creates new form CallLogArtifactViewer.
@ -92,6 +92,10 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
public void setArtifact(BlackboardArtifact artifact) {
resetComponent();
if (artifact == null) {
return;
}
CallLogViewData callLogViewData = null;
try {
callLogViewData = getCallLogViewData(artifact);
@ -101,11 +105,16 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
// update the view with the call log data
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
this.revalidate();
}
/**
@ -281,6 +290,8 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
* Update the viewer with the call log data.
*
* @param callLogViewData Call log data to update the view with.
*
* @return List of AccountPersonaSearcherData objects.
*/
@NbBundle.Messages({
"CallLogArtifactViewer_heading_parties=Parties",
@ -288,10 +299,11 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
"CallLogArtifactViewer_label_from=From",
"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());
List<AccountPersonaSearcherData> dataList = new ArrayList<>();
// Display From address
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);
// show persona
Optional<PersonaSearchAndDisplayTask> task = CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount());
if (task.isPresent()) {
personaSearchtasks.add(task.get());
}
dataList.addAll( CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount()));
} else {
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);
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
Optional<PersonaSearchAndDisplayTask> task = CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount());
if (task.isPresent()) {
personaSearchtasks.add(task.get());
}
dataList.addAll( CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount()));
} else {
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.addValue(this, m_gridBagLayout, this.m_constraints, otherParty);
Optional<PersonaSearchAndDisplayTask> task = CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, otherParty);
if (task.isPresent()) {
personaSearchtasks.add(task.get());
}
dataList.addAll( CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, otherParty));
}
updateMetadataView(callLogViewData);
updateOtherAttributesView(callLogViewData);
updateSourceView(callLogViewData);
if (CentralRepository.isEnabled() == false) {
showCRDisabledMessage();
}
CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints);
this.setLayout(m_gridBagLayout);
this.revalidate();
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());
}
/**
* 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
* 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
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() {
// cancel any outstanding persona searching threads.
personaSearchtasks.forEach(task -> task.cancel(Boolean.TRUE));
personaSearchtasks.clear();
if(currentAccountFetcher != null && !currentAccountFetcher.isDone()) {
currentAccountFetcher.cancel(true);
currentAccountFetcher = null;
}
// clear the panel
this.removeAll();
@ -441,9 +489,9 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac
m_constraints.anchor = GridBagConstraints.FIRST_LINE_START;
m_constraints.gridy = 0;
m_constraints.gridx = 0;
m_constraints.weighty = 0.05;
m_constraints.weightx = 0.05;
m_constraints.insets = new java.awt.Insets(0, 0, 0, 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;
}

View File

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

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Dimension;
import java.awt.Font;
@ -27,12 +27,14 @@ import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import java.util.Optional;
import org.openide.util.NbBundle;
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
* gridbaglayout.
*/
public final class CommunicationArtifactViewerHelper {
final class CommunicationArtifactViewerHelper {
// Number of columns in the gridbag layout.
private final static int MAX_COLS = 4;
private final static int LEFT_INDENT = 12;
final static int LEFT_INSET = 12;
/**
* Empty private constructor
*/
private CommunicationArtifactViewerHelper() {
}
/**
* Adds a new heading to the panel.
*
@ -62,8 +64,15 @@ public final class CommunicationArtifactViewerHelper {
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @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
// the first section
@ -75,9 +84,9 @@ public final class CommunicationArtifactViewerHelper {
// let the header span all of the row
constraints.gridwidth = MAX_COLS;
constraints.insets = new Insets(0, 0, 0, 0); // No inset for header
// create label for heading
javax.swing.JLabel headingLabel = new javax.swing.JLabel();
// set text
headingLabel.setText(headerString);
// make it large and bold
@ -92,6 +101,28 @@ public final class CommunicationArtifactViewerHelper {
// add line end glue
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 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.
constraints.gridx = MAX_COLS;
@ -155,7 +186,7 @@ public final class CommunicationArtifactViewerHelper {
* @param gridbagLayout Layout 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.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 gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @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.gridx = 0;
constraints.gridx = gridx < MAX_COLS - 1 ? gridx : MAX_COLS - 2;
Insets savedInsets = constraints.insets;
// 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();
// set text
keyLabel.setText(keyString + ": ");
// add to panel
gridbagLayout.setConstraints(keyLabel, constraints);
panel.add(keyLabel);
// restore inset
constraints.insets = savedInsets;
return keyLabel;
}
/**
* Adds a value string to the panel.
* Adds a value string to the panel at col 1.
*
* @param panel Panel to update.
* @param gridbagLayout Layout to use.
* @param constraints Constrains to use.
* @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;
// let the value span 2 cols
constraints.gridwidth = 2;
// create label,
javax.swing.JLabel valueField = new javax.swing.JLabel();
// set text
valueField.setText(valueString);
// attach a right click menu with Copy option
@ -234,6 +296,63 @@ public final class CommunicationArtifactViewerHelper {
// end the line
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 accountIdentifier Account identifier to search the persona.
*
* @return Optional PersonaSearchAndDisplayTask started to search for
* persona.
* @return List of AccountPersonaSearcherData objects.
*/
@NbBundle.Messages({
"CommunicationArtifactViewerHelper_persona_label=Persona: ",
@ -260,17 +378,17 @@ public final class CommunicationArtifactViewerHelper {
"CommunicationArtifactViewerHelper_persona_button_view=View",
"CommunicationArtifactViewerHelper_persona_button_create=Create"
})
static Optional<PersonaSearchAndDisplayTask> addPersonaRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String accountIdentifier) {
PersonaSearchAndDisplayTask personaTask = null;
static List<AccountPersonaSearcherData> addPersonaRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String accountIdentifier) {
List<AccountPersonaSearcherData> dataList = new ArrayList<>();
constraints.gridy++;
constraints.gridx = 1;
Insets savedInsets = constraints.insets;
// Indent in
constraints.insets = new java.awt.Insets(0, LEFT_INDENT, 0, 0);
// extra Indent in
constraints.insets = new java.awt.Insets(0, 2 * LEFT_INSET, 0, 0);
// create label
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.
javax.swing.JButton personaButton = new javax.swing.JButton();
personaButton.setText(Bundle.CommunicationArtifactViewerHelper_persona_button_view());
personaButton.setMargin(new Insets(0, 5, 0, 5));
personaButton.setEnabled(false);
gridbagLayout.setConstraints(personaButton, constraints);
panel.add(personaButton);
if (CentralRepository.isEnabled()) {
// kick off a task to find the persona for this account
personaTask = new PersonaSearchAndDisplayTask(panel, new AccountPersonaSearcherData(accountIdentifier, personaLabel, personaButton));
personaTask.execute();
dataList.add(new AccountPersonaSearcherData(accountIdentifier, personaLabel, personaButton));
} else {
personaLabel.setEnabled(false);
}
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">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="DefaultArtifactContentViewer.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<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>
</Properties>
</MenuItem>
<MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="DefaultArtifactContentViewer.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<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>
</Properties>
</MenuItem>

View File

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

View File

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

View File

@ -120,7 +120,7 @@
<Component class="javax.swing.JLabel" name="fromLabel">
<Properties>
<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>
</Properties>
</Component>
@ -128,28 +128,28 @@
<Properties>
<Property name="horizontalAlignment" type="int" value="4"/>
<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>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="fromText">
<Properties>
<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>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="toLabel">
<Properties>
<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>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="toText">
<Properties>
<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 name="autoscrolls" type="boolean" value="true"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
@ -160,14 +160,14 @@
<Component class="javax.swing.JLabel" name="ccLabel">
<Properties>
<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>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="ccText">
<Properties>
<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 name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 14]"/>
@ -177,14 +177,14 @@
<Component class="javax.swing.JLabel" name="subjectLabel">
<Properties>
<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>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="subjectText">
<Properties>
<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 name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[26, 14]"/>
@ -195,7 +195,7 @@
<Properties>
<Property name="horizontalAlignment" type="int" value="4"/>
<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>
</Properties>
</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">
<JTabbedPaneConstraints tabName="Headers">
<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>
</JTabbedPaneConstraints>
</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">
<JTabbedPaneConstraints tabName="HTML">
<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>
</JTabbedPaneConstraints>
</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">
<JTabbedPaneConstraints tabName="RTF">
<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>
</JTabbedPaneConstraints>
</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">
<JTabbedPaneConstraints tabName="Attachments">
<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>
</JTabbedPaneConstraints>
</Constraint>
@ -311,7 +311,7 @@
<Component class="javax.swing.JButton" name="viewInNewWindowButton">
<Properties>
<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>
</Properties>
<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">
<JTabbedPaneConstraints tabName="Accounts">
<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>
</JTabbedPaneConstraints>
</Constraint>

View File

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

View File

@ -1,4 +1,4 @@
/**
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
@ -16,122 +16,150 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
package org.sleuthkit.autopsy.contentviewers.artifactviewers;
import java.awt.Component;
import java.awt.event.ActionListener;
import java.util.ArrayList;
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.ExecutionException;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.JButton;
import javax.swing.SwingWorker;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
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.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.
*
* When the search is complete, it updates the UI components
* for the persona appropriately.
*
*/
* SwingWorker for fetching and updating Persona controls.
*/
class PersonaAccountFetcher extends SwingWorker<Map<String, Collection<Persona>>, Void> {
@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",
"PersonaDisplayTask_persona_count_suffix=(1 of {0})"
})
class PersonaSearchAndDisplayTask extends SwingWorker<Collection<Persona>, Void> {
private final static Logger logger = Logger.getLogger(PersonaSearchAndDisplayTask.class.getName());
private final Component parentComponent;
private final AccountPersonaSearcherData personaSearcherData;
/**
* Update the Persona gui controls.
*
* @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) {
this.parentComponent = parentComponent;
this.personaSearcherData = personaSearcherData;
}
if (personas == null || personas.isEmpty()) {
// No persona found
personaLabelText += Bundle.CommunicationArtifactViewerHelper_persona_unknown();
@Override
protected Collection<Persona> doInBackground() throws Exception {
Collection<Persona> personas = new ArrayList<>();
if (CentralRepository.isEnabled()) {
Collection<CentralRepoAccount> accountCandidates
= CentralRepoAccount.getAccountsWithIdentifier(personaSearcherData.getAccountIdentifer());
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());
}
// 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()));
}
return personas;
// Show a 'View' button
personaButtonText = Bundle.CommunicationArtifactViewerHelper_persona_button_view();
buttonActionListener = new ViewPersonaButtonListener(parentComponent, persona);
}
@Override
protected void done() {
Collection<Persona> personas;
try {
personas = super.get();
personaSearcherData.getPersonaNameLabel().setText(personaLabelText);
personaSearcherData.getPersonaActionButton().setText(personaButtonText);
personaSearcherData.getPersonaActionButton().setEnabled(true);
if (this.isCancelled()) {
return;
}
// set button action
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.
*/
@ -177,7 +205,7 @@ class PersonaSearchAndDisplayTask extends SwingWorker<Collection<Persona>, Void>
* Callback method for the create mode of the PersonaDetailsDialog
*/
class PersonaCreateCallbackImpl implements PersonaDetailsDialogCallback {
private final Component parentComponent;
private final AccountPersonaSearcherData personaSearcherData;
@ -217,4 +245,5 @@ class PersonaSearchAndDisplayTask extends SwingWorker<Collection<Persona>, Void>
// nothing to do
}
}
}

View File

@ -33,6 +33,8 @@ import java.util.logging.Handler;
import java.util.logging.Level;
import javafx.application.Platform;
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.lang3.StringUtils;
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
* exist.
* Make a folder in the config directory for object detection classifiers if
* one does not exist.
*/
private static void ensureClassifierFolderExists() {
File objectDetectionClassifierDir = new File(PlatformUtil.getObjectDetectionClassifierPath());
@ -381,6 +383,7 @@ public class Installer extends ModuleInstall {
ensureClassifierFolderExists();
ensureOcrLanguagePacksFolderExists();
initJavaFx();
initializeSevenZip();
for (ModuleInstall mi : packageInstallers) {
try {
mi.restored();
@ -393,8 +396,21 @@ public class Installer extends ModuleInstall {
logger.log(Level.INFO, "Autopsy Core restore completed"); //NON-NLS
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.
*/
@ -403,13 +419,12 @@ public class Installer extends ModuleInstall {
try {
JythonModuleLoader.getIngestModuleFactories();
JythonModuleLoader.getGeneralReportModules();
}
catch (Exception ex) {
} catch (Exception ex) {
// This is a firewall exception to ensure that any possible exception caused
// 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);
}
};
new Thread(loader).start();
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-2015 Basis Technology Corp.
* Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -42,65 +42,29 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* This class periodically checks availability of collaboration resources -
* remote database, remote keyword search server, messaging service - and
* reports status updates to the user in case of a gap in service.
* Monitors the status of services and publishes events and user notifications
* when the status of a service changes. The database server, keyword search
* 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 {
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
* status update.
*/
private final ConcurrentHashMap<String, String> statusByService;
/**
* 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.
* An enumeration of the core services in a collaborative, multi-user case
* environment. The display names provided here can be used to identify the
* service status events published for these services and to directly query
* the ServicesMonitor for the current status of these services.
*/
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")),
/**
* 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")),
/**
* 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"));
private final String displayName;
private Service(String name) {
this.displayName = name;
private Service(String displayName) {
this.displayName = displayName;
}
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 {
/**
* Service is currently up.
*/
UP,
/**
* Service is currently 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() {
if (instance == null) {
instance = new ServicesMonitor();
if (servicesMonitor == null) {
servicesMonitor = new ServicesMonitor();
}
return instance;
}
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);
return servicesMonitor;
}
/**
* Updates service status and publishes the service status update if it is
* different from previous status. Event is published locally. Logs status
* Constructs a services monitor that monitors the status of services and
* publishes events and user notifications when the status of a service
* 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 status Updated status for the service.
* @param details Details of the event.
* @param status Current status of the service.
* @param details Additional status 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))) {
return;
}
// new service or status has changed - identify service's display name
statusByService.put(service, status);
String serviceDisplayName;
try {
serviceDisplayName = ServicesMonitor.Service.valueOf(service).getDisplayName();
} catch (IllegalArgumentException ignore) {
// custom service that is not listed in ServicesMonitor.Service enum. Use service name as display name.
serviceDisplayName = service;
}
if (status.equals(ServiceStatus.UP.toString())) {
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));
} else if (status.equals(ServiceStatus.DOWN.toString())) {
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));
} else {
logger.log(Level.INFO, "Status for {0} is {1}", new Object[]{serviceDisplayName, status}); //NON-NLS
MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.title"),
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.msg", new Object[]{serviceDisplayName, status}));
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"),
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));
}
/**
* Get last status update for a service.
* Get last recorded status for a service.
*
* @param service Name of the service.
*
* @return ServiceStatus Status for the service.
*
* @throws ServicesMonitorException If service name is null or service
* doesn't exist.
* @throws ServicesMonitorException If the service name is unknown to the
* services monitor.
*/
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"));
}
// if request is for one of our "core" services - perform an on demand check
// to make sure we have the latest status.
if (servicesList.contains(service)) {
/*
* If the request is for a core service, perform an "on demand" check to
* get the current status.
*/
if (coreServices.contains(service)) {
checkServiceStatus(service);
}
String status = statusByService.get(service);
if (status == null) {
// no such service
throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.unknownServiceName.excepton.txt", service));
}
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) {
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() {
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() {
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="discovery.wsmode" url="discoveryWsmode.xml"/>
<file name="geolocation.wsmode" url="geolocationWsmode.xml"/>
<file name="personas.wsmode" url="personasWsmode.xml"/>
</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 java.util.Collections;
import java.util.HashSet;
import org.sleuthkit.autopsy.contentviewers.ArtifactContentViewer;
import org.sleuthkit.autopsy.contentviewers.DefaultArtifactContentViewer;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.ArtifactContentViewer;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.DefaultArtifactContentViewer;
/**
* 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 {
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");
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
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
* 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<>();
// maps the artifact type to its child node
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")
TypeFactory() {
super();
@ -173,27 +179,11 @@ public class ExtractedContent implements AutopsyVisitableItem {
private final PropertyChangeListener pcl = (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()))) {
refresh(true);
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
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;
}
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
@ -204,32 +194,26 @@ public class ExtractedContent implements AutopsyVisitableItem {
*/
try {
Case.getCurrentCaseThrows();
refresh(true);
refresh(false);
} catch (NoCurrentCaseException notUsed) {
/**
* 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
protected void addNotify() {
refreshThrottler.registerForIngestModuleEvents();
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);
}
@Override
protected void removeNotify() {
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
typeNodeList.clear();
}
@ -273,6 +257,40 @@ public class ExtractedContent implements AutopsyVisitableItem {
typeNodeList.put(key, 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
*/
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) {
super(type.getTypeName());
this.type = type;
}
private final PropertyChangeListener pcl = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
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(false);
} catch (NoCurrentCaseException notUsed) {
/**
* 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.
* Case is closed, do nothing.
*/
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
protected void onAdd() {
refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
}
@Override
protected void onRemove() {
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
}
@Override
@ -451,5 +449,43 @@ public class ExtractedContent implements AutopsyVisitableItem {
}
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
* 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 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() {
super();
this.CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE);
this.pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.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
* 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().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl);
refreshThrottler.registerForIngestModuleEvents();
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
private void removeListeners() {
deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
refreshThrottler.unregisterEventListener();
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
@ -154,7 +141,52 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
setChanged();
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");
/**

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);
/**
* 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
* files by mime type. Filters out certain kinds of files and directories,
* and known/slack files based on user preferences.
*
* @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() {
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() {
deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
refreshThrottler.unregisterEventListener();
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
@ -155,32 +162,20 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
this.typesRoot = typesRoot;
this.pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())
|| eventType.equals(Case.Events.DATA_SOURCE_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();
typesRoot.updateShowCounts();
populateHashMap();
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
refreshMimeTypes();
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
if (evt.getNewValue() == null) {
removeListeners();
}
}
};
refreshThrottler = new RefreshThrottler(new FileTypesByMimeTypeRefresher());
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
refreshThrottler.registerForIngestModuleEvents();
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
populateHashMap();
}
@ -201,7 +196,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
* @param node the Node which you wish to check.
*
* @return True if originNode is an instance of ByMimeTypeNode and is empty,
* false otherwise.
* false otherwise.
*/
public static boolean isEmptyMimeTypeNode(Node node) {
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
* 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.weighty = LABEL_WEIGHT;
constraints.gridwidth = LABEL_WIDTH;
addToGridBagLayout(filterPanel.getCheckbox(), null, column);
addToGridBagLayout(filterPanel.getCheckbox(), filterPanel.getAdditionalLabel(), column);
if (filterPanel.hasPanel()) {
constraints.gridx += constraints.gridwidth;
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
VideoFilterPanel.videoFiltersSplitPane.border.title=Step 2: Filter which videos to show
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.searchCancelled.text=Search has been cancelled.
# {0} - search
DiscoveryTopComponent.searchComplete.text=Results for {0}
DiscoveryTopComponent.searchComplete.text=Results with {0}
# {0} - searchType
DiscoveryTopComponent.searchInProgress.text=Performing search for results of type {0}. Please wait.
DiscoveryUiUtility.bytes.text=bytes
@ -57,18 +57,24 @@ FileSearch.InterestingItemGroupKey.noSets=None
FileSearch.KeywordListGroupKey.noKeywords=None
FileSearch.NoGroupingGroupKey.allFiles=All Files
FileSearch.ObjectDetectedGroupKey.noSets=None
FileSearchData.FileSize.LARGE_IMAGE.displayName=Large: 1-50MB
FileSearchData.FileSize.LARGE_VIDEO.displayName=Large: 1-5GB
FileSearchData.FileSize.MEDIUM_IMAGE.displayName=Medium: 100KB-1MB
FileSearchData.FileSize.MEDIUM_VIDEO.displayName=Medium: 100MB-1GB
FileSearchData.FileSize.SMALL_IMAGE.displayName=Small: 16-100KB
FileSearchData.FileSize.SMALL_VIDEO.displayName=Small: 500KB-100MB
FileSearchData.FileSize.XLARGE_IMAGE.displayName=XLarge: 50-200MB
FileSearchData.FileSize.XLARGE_VIDEO.displayName=XLarge: 5-10GB
FileSearchData.FileSize.XSMALL_IMAGE.displayName=XSmall: 0-16KB
FileSearchData.FileSize.XSMALL_VIDEO.displayName=XSmall: 0-500KB
FileSearchData.FileSize.XXLARGE_IMAGE.displayName=XXLarge: 200MB+
FileSearchData.FileSize.XXLARGE_VIDEO.displayName=XXLarge: 10GB+
FileSearchData.FileSize.100kbto1mb=: 100KB-1MB
FileSearchData.FileSize.100mbto1gb=: 100MB-1GB
FileSearchData.FileSize.10PlusGb=: 10GB+
FileSearchData.FileSize.16kbto100kb=: 16-100KB
FileSearchData.FileSize.1gbto5gb=: 1-5GB
FileSearchData.FileSize.1mbto50mb=: 1-50MB
FileSearchData.FileSize.200PlusMb=: 200MB+
FileSearchData.FileSize.500kbto100mb=: 500KB-100MB
FileSearchData.FileSize.50mbto200mb=: 50-200MB
FileSearchData.FileSize.5gbto10gb=: 5-10GB
FileSearchData.FileSize.LARGE.displayName=Large
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.Documents.displayName=Documents
FileSearchData.FileType.Executables.displayName=Executables
@ -106,45 +112,44 @@ FileSearchFiltering.concatenateSetNamesForDisplay.comma=,
# {1} - Data source ID
FileSearchFiltering.DataSourceFilter.datasource={0}({1})
# {0} - filters
FileSearchFiltering.DataSourceFilter.desc=Files in data source(s): {0}
FileSearchFiltering.DataSourceFilter.or=\ or
FileSearchFiltering.DataSourceFilter.desc=Data source(s): {0}
FileSearchFiltering.DataSourceFilter.or=,
# {0} - filters
FileSearchFiltering.FileTypeFilter.desc=Files with type: {0}
FileSearchFiltering.FileTypeFilter.or=\ or
FileSearchFiltering.FileTypeFilter.desc=Type: {0}
FileSearchFiltering.FileTypeFilter.or=,
# {0} - filters
FileSearchFiltering.FrequencyFilter.desc=Files with frequency: {0}
FileSearchFiltering.FrequencyFilter.or=\ or
FileSearchFiltering.FrequencyFilter.desc=Past occurrences: {0}
FileSearchFiltering.FrequencyFilter.or=,
# {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
FileSearchFiltering.InterestingItemSetFilter.desc=Files with interesting item hits in set(s): {0}
FileSearchFiltering.InterestingItemSetFilter.desc=Interesting item hits in set(s): {0}
# {0} - filters
FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0}
FileSearchFiltering.KnownFilter.desc=Files which are not known
FileSearchFiltering.KeywordListFilter.desc=Keywords in list(s): {0}
FileSearchFiltering.KnownFilter.desc=which are not known
# {0} - filters
FileSearchFiltering.ObjectDetectionFilter.desc=Files with objects detected in set(s): {0}
FileSearchFiltering.ObjectDetectionFilter.desc=Objects detected in set(s): {0}
# {0} - filters
FileSearchFiltering.ParentFilter.desc=Files with paths matching: {0}
FileSearchFiltering.ParentFilter.desc=Paths matching: {0}
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.ParentSearchTerm.excludeString=\ (exclude)
FileSearchFiltering.ParentSearchTerm.fullString=\ (exact)
FileSearchFiltering.ParentSearchTerm.includeString=\ (include)
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
FileSearchFiltering.ScoreFilter.desc=Files with score(s) of : {0}
FileSearchFiltering.ScoreFilter.desc=Score(s) of : {0}
# {0} - filters
FileSearchFiltering.SizeFilter.desc=Files with size in range(s): {0}
FileSearchFiltering.SizeFilter.or=\ or
# {0} - Minimum bytes
# {1} - Maximum bytes
FileSearchFiltering.SizeFilter.range=({0} to {1})
FileSearchFiltering.SizeFilter.desc=Size(s): {0}
FileSearchFiltering.SizeFilter.or=,
# {0} - tag names
FileSearchFiltering.TagsFilter.desc=Files that have been tagged {0}
FileSearchFiltering.TagsFilter.or=\ or
FileSearchFiltering.UserCreatedFilter.desc=Files that contain EXIF data
FileSearchFiltering.TagsFilter.desc=Tagged {0}
FileSearchFiltering.TagsFilter.or=,
FileSearchFiltering.UserCreatedFilter.desc=that contain EXIF data
FileSearchPanel.sortingPanel.border.title=Grouping
FileSearchPanel.addButton.text=Add
FileSearchPanel.substringRadioButton.text=Substring
@ -186,7 +191,7 @@ GroupsListPanel.noResults.title.text=No results found
ImageThumbnailPanel.isDeleted.text=All instances of file are deleted.
# {0} - otherInstanceCount
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.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.
@ -259,6 +264,9 @@ DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which
ImageFilterPanel.imageFiltersSplitPane.border.title=Step 2: Filter which images to show
VideoFilterPanel.videoFiltersSplitPane.border.title=Step 2: Filter which videos to show
DiscoveryDialog.step1Label.text=Step 1: Choose result type
ResultsSplitPaneDivider.hideButton.text=
ResultsSplitPaneDivider.showButton.text=
ResultsSplitPaneDivider.detailsLabel.text=Details Area
VideoThumbnailPanel.bytes.text=bytes
VideoThumbnailPanel.deleted.text=All instances of file are deleted.
VideoThumbnailPanel.gigaBytes.text=GB

View File

@ -43,23 +43,25 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace pref="196" max="32767" attributes="0"/>
<Group type="103" groupAlignment="1" 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" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Component id="imagesButton" min="-2" pref="110" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="videosButton" min="-2" pref="110" max="-2" attributes="0"/>
<EmptySpace 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>
<EmptySpace pref="196" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>

View File

@ -275,19 +275,21 @@ final class DiscoveryDialog extends javax.swing.JDialog {
toolBarPanelLayout.setHorizontalGroup(
toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(toolBarPanelLayout.createSequentialGroup()
.addContainerGap(196, Short.MAX_VALUE)
.addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.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))
.addContainerGap()
.addGroup(toolBarPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(toolBarPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addComponent(imagesButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(videosButton, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(documentsButton)))
.addContainerGap(196, Short.MAX_VALUE))
.addComponent(documentsButton)
.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.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)

View File

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

View File

@ -19,11 +19,14 @@
package org.sleuthkit.autopsy.discovery;
import com.google.common.eventbus.Subscribe;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.util.List;
import java.util.stream.Collectors;
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.Messages;
import org.openide.windows.Mode;
@ -67,6 +70,34 @@ public final class DiscoveryTopComponent extends TopComponent {
mainSplitPane.setLeftComponent(groupListPanel);
rightSplitPane.setTopComponent(resultsPanel);
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.setPreferredSize(new java.awt.Dimension(1100, 700));
rightSplitPane.setDividerSize(15);
rightSplitPane.setDividerSize(35);
rightSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
rightSplitPane.setResizeWeight(1.0);
rightSplitPane.setPreferredSize(new java.awt.Dimension(800, 700));
@ -249,11 +280,11 @@ public final class DiscoveryTopComponent extends TopComponent {
@Subscribe
@Messages({"DiscoveryTopComponent.newSearch.text=New Search",
"# {0} - search",
"DiscoveryTopComponent.searchComplete.text=Results for {0}"})
"DiscoveryTopComponent.searchComplete.text=Results with {0}"})
void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCompleteEvent) {
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
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);
}
@ -269,6 +300,7 @@ public final class DiscoveryTopComponent extends TopComponent {
newSearchButton.setText(Bundle.DiscoveryTopComponent_newSearch_text());
progressMessageTextArea.setForeground(Color.red);
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchCancelled_text());
}
/**

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.discovery;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
@ -29,6 +30,8 @@ import java.util.logging.Level;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
@ -224,7 +227,16 @@ final class DiscoveryUiUtils {
message += dsmodulesWrapper.getMessage();
}
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) {
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 HashSetFilterPanel(), false, null, 1);
addFilter(new InterestingItemsFilterPanel(), false, null, 1);
addFilter(new ObjectDetectedFilterPanel(), false, null, 1);
addFilter(new ParentFolderFilterPanel(), false, null, 1);
addPanelsToScrollPane(documentsFiltersSplitPane);
}

View File

@ -126,39 +126,46 @@ final class FileSearchData {
* Enum representing the file size
*/
@NbBundle.Messages({
"FileSearchData.FileSize.XXLARGE_IMAGE.displayName=XXLarge: 200MB+",
"FileSearchData.FileSize.XLARGE_IMAGE.displayName=XLarge: 50-200MB",
"FileSearchData.FileSize.LARGE_IMAGE.displayName=Large: 1-50MB",
"FileSearchData.FileSize.MEDIUM_IMAGE.displayName=Medium: 100KB-1MB",
"FileSearchData.FileSize.SMALL_IMAGE.displayName=Small: 16-100KB",
"FileSearchData.FileSize.XSMALL_IMAGE.displayName=XSmall: 0-16KB",
"FileSearchData.FileSize.XXLARGE_VIDEO.displayName=XXLarge: 10GB+",
"FileSearchData.FileSize.XLARGE_VIDEO.displayName=XLarge: 5-10GB",
"FileSearchData.FileSize.LARGE_VIDEO.displayName=Large: 1-5GB",
"FileSearchData.FileSize.MEDIUM_VIDEO.displayName=Medium: 100MB-1GB",
"FileSearchData.FileSize.SMALL_VIDEO.displayName=Small: 500KB-100MB",
"FileSearchData.FileSize.XSMALL_VIDEO.displayName=XSmall: 0-500KB",})
"FileSearchData.FileSize.XXLARGE.displayName=XXLarge",
"FileSearchData.FileSize.XLARGE.displayName=XLarge",
"FileSearchData.FileSize.LARGE.displayName=Large",
"FileSearchData.FileSize.MEDIUM.displayName=Medium",
"FileSearchData.FileSize.SMALL.displayName=Small",
"FileSearchData.FileSize.XSMALL.displayName=XSmall",
"FileSearchData.FileSize.10PlusGb=: 10GB+",
"FileSearchData.FileSize.5gbto10gb=: 5-10GB",
"FileSearchData.FileSize.1gbto5gb=: 1-5GB",
"FileSearchData.FileSize.100mbto1gb=: 100MB-1GB",
"FileSearchData.FileSize.200PlusMb=: 200MB+",
"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 {
XXLARGE_VIDEO(0, 10000 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_VIDEO_displayName()),
XLARGE_VIDEO(1, 5000 * BYTES_PER_MB, 10000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_VIDEO_displayName()),
LARGE_VIDEO(2, 1000 * BYTES_PER_MB, 5000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_VIDEO_displayName()),
MEDIUM_VIDEO(3, 100 * BYTES_PER_MB, 1000 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_VIDEO_displayName()),
SMALL_VIDEO(4, 500000, 100 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_SMALL_VIDEO_displayName()),
XSMALL_VIDEO(5, 0, 500000, Bundle.FileSearchData_FileSize_XSMALL_VIDEO_displayName()),
XXLARGE_IMAGE(6, 200 * BYTES_PER_MB, -1, Bundle.FileSearchData_FileSize_XXLARGE_IMAGE_displayName()),
XLARGE_IMAGE(7, 50 * BYTES_PER_MB, 200 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_XLARGE_IMAGE_displayName()),
LARGE_IMAGE(8, 1 * BYTES_PER_MB, 50 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_LARGE_IMAGE_displayName()),
MEDIUM_IMAGE(9, 100000, 1 * BYTES_PER_MB, Bundle.FileSearchData_FileSize_MEDIUM_IMAGE_displayName()),
SMALL_IMAGE(10, 16000, 100000, Bundle.FileSearchData_FileSize_SMALL_IMAGE_displayName()),
XSMALL_IMAGE(11, 0, 16000, Bundle.FileSearchData_FileSize_XSMALL_IMAGE_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_displayName(), Bundle.FileSearchData_FileSize_5gbto10gb()),
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_displayName(), Bundle.FileSearchData_FileSize_100mbto1gb()),
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_displayName(), Bundle.FileSearchData_FileSize_upTo500kb()),
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_displayName(), Bundle.FileSearchData_FileSize_50mbto200mb()),
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_displayName(), Bundle.FileSearchData_FileSize_100kbto1mb()),
SMALL_IMAGE(10, 16000, 100000, Bundle.FileSearchData_FileSize_SMALL_displayName(), Bundle.FileSearchData_FileSize_16kbto100kb()),
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 long minBytes; // Note that the size must be strictly greater than this to match
private final long maxBytes;
private final String displayName;
private final String sizeGroup;
private final String displaySize;
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.minBytes = minB;
if (maxB >= 0) {
@ -166,7 +173,8 @@ final class FileSearchData {
} else {
this.maxBytes = NO_MAXIMUM;
}
this.displayName = displayName;
this.sizeGroup = displayName;
this.displaySize = displaySize;
}
/**
@ -246,7 +254,11 @@ final class FileSearchData {
@Override
public String toString() {
return displayName;
return sizeGroup + displaySize;
}
String getSizeGroup(){
return sizeGroup;
}
/**

View File

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

View File

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

View File

@ -37,6 +37,18 @@
</Property>
</Properties>
</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>
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
@ -143,6 +155,9 @@
</Container>
<Component class="javax.swing.JRadioButton" name="fullRadioButton">
<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="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;)"/>
@ -152,6 +167,9 @@
</Component>
<Component class="javax.swing.JRadioButton" name="includeRadioButton">
<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="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;)"/>
@ -161,6 +179,9 @@
</Component>
<Component class="javax.swing.JRadioButton" name="substringRadioButton">
<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">
<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>
@ -169,6 +190,9 @@
</Component>
<Component class="javax.swing.JRadioButton" name="excludeRadioButton">
<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">
<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>

View File

@ -66,6 +66,8 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
parentCheckbox = new javax.swing.JCheckBox();
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();
parentList = new javax.swing.JList<>();
fullRadioButton = new javax.swing.JRadioButton();
@ -106,17 +108,21 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
});
parentScrollPane.setViewportView(parentList);
pathTypeButtonGroup.add(fullRadioButton);
fullRadioButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(fullRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.fullRadioButton.text_1")); // NOI18N
fullRadioButton.setEnabled(false);
includeButtonGroup.add(includeRadioButton);
includeRadioButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(includeRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.includeRadioButton.text_1")); // NOI18N
includeRadioButton.setEnabled(false);
pathTypeButtonGroup.add(substringRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(substringRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.substringRadioButton.text_1")); // NOI18N
substringRadioButton.setEnabled(false);
includeButtonGroup.add(excludeRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(excludeRadioButton, org.openide.util.NbBundle.getMessage(ParentFolderFilterPanel.class, "ParentFolderFilterPanel.excludeRadioButton.text_1")); // NOI18N
excludeRadioButton.setEnabled(false);
@ -188,7 +194,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
}// </editor-fold>//GEN-END:initComponents
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
private void parentListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_parentListValueChanged
@ -235,6 +241,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
parentCheckbox.setSelected(selected);
if (parentCheckbox.isEnabled() && parentCheckbox.isSelected()) {
parentScrollPane.setEnabled(true);
parentLabel.setEnabled(true);
includeRadioButton.setEnabled(true);
excludeRadioButton.setEnabled(true);
fullRadioButton.setEnabled(true);
@ -248,6 +255,7 @@ final class ParentFolderFilterPanel extends AbstractDiscoveryFilterPanel {
}
} else {
parentScrollPane.setEnabled(false);
parentLabel.setEnabled(false);
parentList.setEnabled(false);
includeRadioButton.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_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,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>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<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>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
@ -32,7 +37,7 @@
<Container class="javax.swing.JPanel" name="videoFiltersPanel">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[223, 66]"/>
<Dimension value="[310, 100]"/>
</Property>
</Properties>
<AuxValues>
@ -45,7 +50,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" 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"/>
</Group>
</Group>
@ -54,7 +59,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" 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"/>
</Group>
</Group>

View File

@ -66,7 +66,9 @@ final class VideoFilterPanel extends AbstractFiltersPanel {
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.setResizeWeight(0.5);
@ -77,14 +79,14 @@ final class VideoFilterPanel extends AbstractFiltersPanel {
videoFiltersPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(videoFiltersPanelLayout.createSequentialGroup()
.addGap(8, 8, 8)
.addComponent(videoFiltersSplitPane)
.addComponent(videoFiltersSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 294, Short.MAX_VALUE)
.addGap(8, 8, 8))
);
videoFiltersPanelLayout.setVerticalGroup(
videoFiltersPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(videoFiltersPanelLayout.createSequentialGroup()
.addGap(8, 8, 8)
.addComponent(videoFiltersSplitPane)
.addComponent(videoFiltersSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 84, Short.MAX_VALUE)
.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_TRACK.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;

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",
"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();

View File

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

View File

@ -135,7 +135,7 @@ public class IngestOptionsPanel extends IngestModuleGlobalSettingsPanel implemen
tabbedPane.setEnabled(!ingestIsRunning);
settingsPanel.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;
/**
* 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
final class DataSourceIntegrityIngestSettingsPanel extends IngestModuleIngestJobSettingsPanel {

View File

@ -139,9 +139,10 @@ class SevenZipExtractor {
}
SevenZipExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) throws SevenZipNativeInitializationException {
if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) {
SevenZip.initSevenZipFromPlatformJAR();
if (!SevenZip.isInitializedSuccessfully()) {
throw new SevenZipNativeInitializationException("SevenZip has not been previously initialized.");
}
this.context = context;
this.fileTypeDetector = fileTypeDetector;
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))
FilesSetDefsPanel.bytes=Bytes
FilesSetDefsPanel.cancelImportMsg=Cancel import
FilesSetDefsPanel.cancelNewSetMsg=Cancel
# {0} - file name
FilesSetDefsPanel.exportButtonActionPerformed.fileExistPrompt=File {0} exists, overwrite?
FilesSetDefsPanel.gigaBytes=Gigabytes
@ -23,8 +24,12 @@ FilesSetDefsPanel.interesting.fileExtensionFilterLbl=Autopsy Interesting File Se
FilesSetDefsPanel.interesting.importButtonAction.featureName=Interesting Files Set Import
FilesSetDefsPanel.interesting.importOwConflict=Import Interesting files set conflict
FilesSetDefsPanel.interesting.importSetButton.text=Import Set
FilesSetDefsPanel.interesting.newOwConflict=Interesting files set conflict
# {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.kiloBytes=Kilobytes
FilesSetDefsPanel.loadError=Error loading interesting files sets from file.
@ -32,6 +37,7 @@ FilesSetDefsPanel.megaBytes=Megabytes
FilesSetDefsPanel.noSkipMsg=No, skip
FilesSetDefsPanel.saveError=Error saving interesting files sets to file.
FilesSetDefsPanel.yesOwMsg=Yes, overwrite
FilesSetDefsPanel.yesStandardFileConflictCreate=Yes, create
FilesSetPanel.filter.title=File Filter
FilesSetPanel.ingest.createNewFilter=Create/edit file ingest filters...
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.mimeTypeLabel.text=MIME Type:
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 boolean ignoreKnownFiles;
private final boolean ignoreUnallocatedSpace;
private final boolean standardSet;
private final int versionNumber;
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.
*/
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())) {
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.description = (description != null ? description : "");
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.
*
@ -624,7 +674,6 @@ public final class FilesSet implements Serializable {
* To ensure compatibility with existing serialized configuration
* settings, this class cannot have a 'serialVersionUID'.
*/
private final TextMatcher textMatcher;
/**
@ -648,10 +697,10 @@ public final class FilesSet implements Serializable {
AbstractTextCondition(Pattern regex) {
this.textMatcher = new FilesSet.Rule.RegexMatcher(regex);
}
/**
* Construct a case-insensitive multi-value text condition.
*
*
* @param values The list of values in which to look for a match.
*/
AbstractTextCondition(List<String> values) {
@ -783,7 +832,6 @@ public final class FilesSet implements Serializable {
* To ensure compatibility with existing serialized configuration
* settings, this class cannot have a 'serialVersionUID'.
*/
private final static long SECS_PER_DAY = 60 * 60 * 24;
private int daysIncluded;
@ -834,8 +882,8 @@ public final class FilesSet implements Serializable {
// AbstractFile.getFileNameExtension() returns just the
// extension chars and not the dot.
super(normalize(extension), false);
}
}
/**
* Construct a case-insensitive file name extension condition.
*
@ -862,29 +910,29 @@ public final class FilesSet implements Serializable {
public boolean passes(AbstractFile file) {
return this.textMatches(file.getNameExtension());
}
/**
* Strip "." from the start of extensions in the provided list.
*
*
* @param extensions The list of extensions to be processed.
*
*
* @return A post-processed list of extensions.
*/
private static List<String> normalize(List<String> 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)));
}
return values;
}
/**
* Strip "." from the start of the provided extension.
*
*
* @param extension The extension to be processed.
*
*
* @return A post-processed extension.
*/
private static String normalize(String extension) {

View File

@ -30,6 +30,7 @@ import java.util.Map;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFileChooser;
@ -37,12 +38,15 @@ import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.commons.lang3.tuple.Pair;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
import org.sleuthkit.autopsy.ingest.IngestProfiles;
import org.sleuthkit.autopsy.ingest.IngestProfiles.IngestProfile;
@ -100,6 +104,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.panelType = panelType;
this.initComponents();
this.customInit();
this.setsList.setModel(setsListModel);
this.setsList.addListSelectionListener(new FilesSetDefsPanel.SetsListSelectionListener());
this.rulesList.setModel(rulesListModel);
@ -133,6 +138,13 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.ruleDialogTitle = "FilesSetPanel.interesting.title";
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",
@ -172,20 +184,23 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
}
}
public void enableButtons(boolean isEnabled) {
boolean setSelected = (FilesSetDefsPanel.this.setsList.getSelectedValue() != null);
public void enableButtons() {
FilesSet selectedFilesSet = this.setsList.getSelectedValue();
boolean setSelected = (selectedFilesSet != null);
boolean isStandardSet = (selectedFilesSet != null && selectedFilesSet.isStandardSet());
boolean ruleSelected = (FilesSetDefsPanel.this.rulesList.getSelectedValue() != null);
canBeEnabled = isEnabled;
newRuleButton.setEnabled(isEnabled);
copySetButton.setEnabled(isEnabled && setSelected);
newSetButton.setEnabled(isEnabled);
editRuleButton.setEnabled(isEnabled && ruleSelected);
editSetButton.setEnabled(isEnabled && setSelected);
newRuleButton.setEnabled(canBeEnabled && !isStandardSet);
copySetButton.setEnabled(canBeEnabled && setSelected);
newSetButton.setEnabled(canBeEnabled);
editRuleButton.setEnabled(canBeEnabled && ruleSelected && !isStandardSet);
editSetButton.setEnabled(canBeEnabled && setSelected && !isStandardSet);
exportSetButton.setEnabled(setSelected);
importSetButton.setEnabled(isEnabled);
deleteRuleButton.setEnabled(isEnabled && ruleSelected);
deleteSetButton.setEnabled(isEnabled && setSelected);
ingestWarningLabel.setVisible(!isEnabled);
importSetButton.setEnabled(canBeEnabled);
deleteRuleButton.setEnabled(canBeEnabled && ruleSelected && !isStandardSet);
deleteSetButton.setEnabled(canBeEnabled && setSelected && !isStandardSet);
ingestWarningLabel.setVisible(!canBeEnabled);
}
@Override
@ -231,17 +246,11 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
* Clears the list models and resets all of the components.
*/
private void resetComponents() {
this.resetRuleComponents();
this.setsListModel.clear();
this.setDescriptionTextArea.setText("");
this.ignoreKnownFilesCheckbox.setSelected(true);
this.ingoreUnallocCheckbox.setSelected(true);
this.newSetButton.setEnabled(true && canBeEnabled);
this.editSetButton.setEnabled(false);
this.copySetButton.setEnabled(false);
this.exportSetButton.setEnabled(false);
this.importSetButton.setEnabled(true && canBeEnabled);
this.deleteSetButton.setEnabled(false);
this.resetRuleComponents();
}
/**
@ -260,9 +269,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.equalitySignComboBox.setSelectedIndex(2);
this.fileSizeUnitComboBox.setSelectedIndex(1);
this.fileSizeSpinner.setValue(0);
this.newRuleButton.setEnabled(!this.setsListModel.isEmpty() && canBeEnabled);
this.editRuleButton.setEnabled(false);
this.deleteRuleButton.setEnabled(false);
enableButtons();
}
/**
@ -275,12 +282,10 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
if (e.getValueIsAdjusting()) {
return;
}
FilesSetDefsPanel.this.rulesListModel.clear();
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
// components.
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.ignoreKnownFilesCheckbox.setSelected(selectedSet.ignoresKnownFiles());
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.
List<FilesSet.Rule> rules = new ArrayList<>(selectedSet.getRules().values());
Collections.sort(rules, new Comparator<FilesSet.Rule>() {
@ -311,15 +311,8 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
if (!FilesSetDefsPanel.this.rulesListModel.isEmpty()) {
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.fileSizeSpinner.setValue(0);
}
if (dateCondition != null){
FilesSetDefsPanel.this.daysIncludedTextField.setText(Integer.toString(dateCondition.getDaysIncluded()));
if (dateCondition != null) {
FilesSetDefsPanel.this.daysIncludedTextField.setText(Integer.toString(dateCondition.getDaysIncluded()));
} else {
FilesSetDefsPanel.this.daysIncludedTextField.setText("");
}
else {
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);
enableButtons();
} 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);
} 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) {
Map<String, FilesSet.Rule> rules = new HashMap<>();
if (selectedSet != null) {
@ -453,10 +433,25 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
// Preserve the existing rules from the set being edited.
rules.putAll(selectedSet.getRules());
}
if (shouldCreateNew) {
this.replaceFilesSet(null, panel.getFilesSetName(), panel.getFilesSetDescription(), panel.getFileSetIgnoresKnownFiles(), panel.getFileSetIgnoresUnallocatedSpace(), rules);
} else {
this.replaceFilesSet(selectedSet, panel.getFilesSetName(), panel.getFilesSetDescription(), panel.getFileSetIgnoresKnownFiles(), panel.getFileSetIgnoresUnallocatedSpace(), rules);
FilesSet filesSet = new FilesSet(
panel.getFilesSetName(),
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
// 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
// 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
* be replaced, so this is an add/edit operation.
*
* @param oldSet A set to replace, null if the new set is
* not a replacement.
* @param name The name of the files set.
* @param description The description of the files set.
* @param ignoresKnownFiles Whether or not the files set ignores
* known files.
* @param rules The set membership rules for the set.
* @param processesUnallocatedSpace Whether or not this set of rules
* processes unallocated space
* @param oldSet A set to replace, null if the new set is not a
* replacement.
* @param newSet The new set of rules.
* @param rules The set membership rules for the set. If null,
* the rules in the new set will be used.
*/
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) {
// Remove the set to be replaced from the working copy if the files
// set definitions.
this.filesSets.remove(oldSet.getName());
}
FilesSet setToAdd = newSet;
// Make the new/edited set definition and add it to the working copy of
// the files set definitions.
FilesSet newSet = new FilesSet(name, description, ignoresKnownFiles, ignoresUnallocatedSpace, rules);
this.filesSets.put(newSet.getName(), newSet);
if (rules != null) {
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
// 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
// list. This will cause the selection listeners to repopulate the
// 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());
FilesSet.Rule selectedRule = this.rulesList.getSelectedValue();
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()) {
this.rulesList.setSelectedIndex(0);
} else {
@ -1111,13 +1115,8 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.doFileSetsDialog(this.setsList.getSelectedValue(), true);
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_copySetButtonActionPerformed
@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.fileExtensionFilterLbl=Autopsy Interesting File Set File (xml)",
"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);
return;
}
for (FilesSet set : importedSets) {
int choice = JOptionPane.OK_OPTION;
if (filesSets.containsKey(set.getName())) {
Object[] options = {NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.yesOwMsg"),
NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.noSkipMsg"),
NbBundle.getMessage(this.getClass(), "FilesSetDefsPanel.cancelImportMsg")};
choice = JOptionPane.showOptionDialog(this,
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;
}
}
importedSets = importedSets
.stream()
.map((filesSet) -> StandardInterestingFilesSetsLoader.getAsStandardFilesSet(filesSet, false))
.collect(Collectors.toList());
FilesSet newSelected = determineFilesToImport(importedSets);
// Redo the list model for the files set list component
FilesSetDefsPanel.this.setsListModel.clear();
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
// list. This will cause the selection listeners to repopulate the
// subordinate components.
this.setsList.setSelectedValue(selectedSet, true);
this.setsList.setSelectedValue(newSelected == null ? selectedSet : newSelected, true);
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//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",
"# {0} - file name",
"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.Paths;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -35,6 +36,7 @@ import java.util.regex.PatternSyntaxException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.openide.util.io.NbObjectInputStream;
import org.openide.util.io.NbObjectOutputStream;
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.ParentPathCondition;
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.Element;
import org.w3c.dom.NodeList;
import java.util.Comparator;
import java.util.function.Function;
import java.util.stream.Collectors;
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 String TYPE_FILTER_ATTR = "typeFilter"; //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;
@ -378,6 +386,25 @@ class InterestingItemsFilesSetSettings implements Serializable {
if (!ignoreUnallocated.isEmpty()) {
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.
Map<String, FilesSet.Rule> rules = new HashMap<>();
NodeList allRuleElems = setElem.getChildNodes();
@ -401,7 +428,7 @@ class InterestingItemsFilesSetSettings implements Serializable {
// Make the files set. Note that degenerate sets with no rules are
// allowed to facilitate the separation of set definition and rule
// 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);
}
// 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
*/
static Map<String, FilesSet> readDefinitionsXML(File xmlFile) throws FilesSetsManager.FilesSetsManagerException {
Map<String, FilesSet> filesSets = new HashMap<>();
if (!xmlFile.exists()) {
return filesSets;
return new HashMap<>();
}
// Check if the file can be read.
if (!xmlFile.canRead()) {
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());
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) {
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;
}
// Get the root element.
Element root = doc.getDocumentElement();
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;
}
// Read in the files set definitions.
NodeList setElems = root.getElementsByTagName(FILE_SET_TAG);
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;
}
@ -493,6 +540,34 @@ class InterestingItemsFilesSetSettings implements Serializable {
}
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.
@ -512,15 +587,27 @@ class InterestingItemsFilesSetSettings implements Serializable {
Element rootElement = doc.createElement(FILE_SETS_ROOT_TAG);
doc.appendChild(rootElement);
// 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.
Element setElement = doc.createElement(FILE_SET_TAG);
setElement.setAttribute(NAME_ATTR, set.getName());
setElement.setAttribute(DESC_ATTR, set.getDescription());
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.
// 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
// type tag.
Element ruleElement;
@ -577,13 +664,13 @@ class InterestingItemsFilesSetSettings implements Serializable {
ruleElement.setAttribute(FS_SIZE_ATTR, Integer.toString(sizeCondition.getSizeValue()));
ruleElement.setAttribute(FS_UNITS_ATTR, sizeCondition.getUnit().getName());
}
//Add the optional date condition
//Add the optional date condition
DateCondition dateCondition = rule.getDateCondition();
if (dateCondition != null) {
ruleElement.setAttribute(DAYS_INCLUDED_ATTR, Integer.toString(dateCondition.getDaysIncluded()));
}
setElement.appendChild(ruleElement);
}
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;
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_LINUX_EXECUTABLE = "photorec";
private static final String PHOTOREC_RESULTS_BASE = "results"; //NON-NLS
@ -454,6 +455,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
return path;
}
/**
* Finds and returns the path to the executable, if able.
*
@ -468,7 +470,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
Path execName;
String photorec_linux_directory = "/usr/bin";
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);
} else {
File usrBin = new File("/usr/bin/photorec");

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