INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED);
private final IngestJobEventPropertyChangeListener ingestJobEventListener;
-
-
-
+
/**
* Creates new form EamOptionsPanel
*/
public GlobalSettingsPanel() {
ingestJobEventListener = new IngestJobEventPropertyChangeListener();
-
+
// listen for change events in currently saved choice
CentralRepoDbManager.addPropertyChangeListener((PropertyChangeEvent evt) -> ingestStateUpdated(Case.isCaseOpen()));
initComponents();
@@ -74,8 +72,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
ingestStateUpdated(evt.getNewValue() != null);
});
}
-
-
+
private void customizeComponents() {
setName(NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.pnCorrelationProperties.border.title"));
}
@@ -85,39 +82,36 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
ingestStateUpdated(Case.isCaseOpen());
}
- private void updateDatabase() {
- updateDatabase(this);
- }
-
/**
- * This method invokes central repository database choice selection as well as input for necessary configuration.
- * @param parent The parent component for displaying dialogs.
- * @param initialSelection If non-null, the menu item will be set to this choice; if null,
- * the currently selected db choice will be selected.
- * @return True if there was a change.
+ * This method invokes central repository database choice selection as well
+ * as input for necessary configuration.
+ *
+ * @param parent The parent component for displaying dialogs.
+ * @param initialSelection If non-null, the menu item will be set to this
+ * choice; if null, the currently selected db choice
+ * will be selected.
+ *
+ * @return True if there was a change.
*/
private static boolean invokeCrChoice(Component parent, CentralRepoDbChoice initialSelection) {
- EamDbSettingsDialog dialog = (initialSelection != null) ?
- new EamDbSettingsDialog(initialSelection) :
- new EamDbSettingsDialog();
-
- if (dialog.wasConfigurationChanged()) {
- updateDatabase(parent);
- return true;
- }
-
- return false;
+ EamDbSettingsDialog dialog = (initialSelection != null)
+ ? new EamDbSettingsDialog(initialSelection)
+ : new EamDbSettingsDialog();
+ return dialog.wasConfigurationChanged();
}
-
-
+
/**
- * When multi user settings are updated, this function triggers pertinent updates for central repository.
- * NOTE: If multi user settings were previously enabled and multi user settings are currently selected, this function assumes
- * there is a change in the postgres connectivity.
- *
- * @param parent The swing component that serves as a parent for dialogs that may arise.
- * @param muPreviouslySelected If multi user settings were previously enabled.
- * @param muCurrentlySelected If multi user settings are currently enabled as of most recent change.
+ * When multi user settings are updated, this function triggers pertinent
+ * updates for central repository. NOTE: If multi user settings were
+ * previously enabled and multi user settings are currently selected, this
+ * function assumes there is a change in the postgres connectivity.
+ *
+ * @param parent The swing component that serves as a parent
+ * for dialogs that may arise.
+ * @param muPreviouslySelected If multi user settings were previously
+ * enabled.
+ * @param muCurrentlySelected If multi user settings are currently enabled
+ * as of most recent change.
*/
@NbBundle.Messages({
"GlobalSettingsPanel.onMultiUserChange.enable.title=Use with Central Repository?",
@@ -128,33 +122,31 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
boolean crEnabled = CentralRepoDbUtil.allowUseOfCentralRepository();
boolean crMultiUser = CentralRepoDbManager.getSavedDbChoice() == CentralRepoDbChoice.POSTGRESQL_MULTIUSER;
boolean crDisabledDueToFailure = CentralRepoDbManager.isDisabledDueToFailure();
-
+
if (!muPreviouslySelected && muCurrentlySelected) {
SwingUtilities.invokeLater(() -> {
- if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(parent,
- "" +
- "" +
- "
" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.enable.description") + "
" +
- "
" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.enable.description2") + "
" +
- "
" +
- "",
- NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.enable.title"),
- JOptionPane.YES_NO_OPTION)) {
-
- // setup database for CR
- CentralRepoDbUtil.setUseCentralRepo(true);
- CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.POSTGRESQL_MULTIUSER);
- handleDbChange(parent);
- }
+ if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(parent,
+ ""
+ + ""
+ + "
" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.enable.description") + "
"
+ + "
" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.enable.description2") + "
"
+ + "
"
+ + "",
+ NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.enable.title"),
+ JOptionPane.YES_NO_OPTION)) {
+
+ // setup database for CR
+ CentralRepoDbUtil.setUseCentralRepo(true);
+ CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.POSTGRESQL_MULTIUSER);
+ handleDbChange(parent);
+ }
});
- }
- // moving from selected to not selected && 'PostgreSQL using multi-user settings' is selected
+ } // moving from selected to not selected && 'PostgreSQL using multi-user settings' is selected
else if (muPreviouslySelected && !muCurrentlySelected && crEnabled && crMultiUser) {
SwingUtilities.invokeLater(() -> {
askForCentralRepoDbChoice(parent);
});
- }
- // changing multi-user settings connection && 'PostgreSQL using multi-user settings' is selected &&
+ } // changing multi-user settings connection && 'PostgreSQL using multi-user settings' is selected &&
// central repo either enabled or was disabled due to error
else if (muPreviouslySelected && muCurrentlySelected && crMultiUser && (crEnabled || crDisabledDueToFailure)) {
// test databse for CR change
@@ -163,10 +155,12 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
}
}
-
/**
- * This method is called when a user must select a new database other than using database from multi user settings.
- * @param parent The parent component to use for displaying dialogs in reference.
+ * This method is called when a user must select a new database other than
+ * using database from multi user settings.
+ *
+ * @param parent The parent component to use for displaying dialogs in
+ * reference.
*/
@NbBundle.Messages({
"GlobalSettingsPanel.onMultiUserChange.disabledMu.title=Central Repository Change Necessary",
@@ -177,21 +171,21 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
// disable central repository until user makes choice
CentralRepoDbUtil.setUseCentralRepo(false);
CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.DISABLED, false);
-
+
Object[] options = {
"Use SQLite",
"Configure PostgreSQL",
"Disable Central Repository"
};
-
+
int result = JOptionPane.showOptionDialog(
parent,
- "" +
- "" +
- "
" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.disabledMu.description") + "
" +
- "
" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.disabledMu.description2") + "
" +
- "
" +
- "",
+ ""
+ + ""
+ + "
" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.disabledMu.description") + "
"
+ + "
" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.disabledMu.description2") + "
"
+ + "
"
+ + "",
NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.disabledMu.title"),
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.PLAIN_MESSAGE,
@@ -199,51 +193,21 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
options,
options[0]
);
-
+
if (JOptionPane.YES_OPTION == result) {
invokeCrChoice(parent, CentralRepoDbChoice.SQLITE);
- }
- else if (JOptionPane.NO_OPTION == result) {
+ } else if (JOptionPane.NO_OPTION == result) {
invokeCrChoice(parent, CentralRepoDbChoice.POSTGRESQL_CUSTOM);
}
}
-
-
+
private static void handleDbChange(Component parent) {
SwingUtilities.invokeLater(() -> {
- boolean successful = EamDbSettingsDialog.testStatusAndCreate(parent, new CentralRepoDbManager());
- if (successful) {
- updateDatabase(parent);
- }
- else {
- // disable central repository due to error
+ if (!EamDbSettingsDialog.testStatusAndCreate(parent, new CentralRepoDbManager())) {
CentralRepoDbManager.disableDueToFailure();
}
});
}
-
-
- @Messages({"GlobalSettingsPanel.updateFailed.title=Central repository disabled"})
- private static void updateDatabase(Component parent) {
- if (CentralRepoDbChoice.DISABLED.equals(CentralRepoDbManager.getSavedDbChoice())) {
- return;
- }
- parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
-
- try {
- CentralRepoDbManager.upgradeDatabase();
- parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
- } catch (CentralRepoException ex) {
- parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
- JOptionPane.showMessageDialog(parent,
- ex.getUserMessage(),
- NbBundle.getMessage(GlobalSettingsPanel.class,
- "GlobalSettingsPanel.updateFailed.title"),
- JOptionPane.WARNING_MESSAGE);
- } finally {
- parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
- }
- }
/**
* This method is called from within the constructor to initialize the form.
@@ -589,17 +553,15 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
private void cbUseCentralRepoActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbUseCentralRepoActionPerformed
//if saved setting is disabled checkbox should be disabled already
store();
-
+
// if moving to using CR, multi-user mode is disabled and selection is multiuser settings, set to disabled
- if (cbUseCentralRepo.isSelected() &&
- !CentralRepoDbManager.isPostgresMultiuserAllowed() &&
- CentralRepoDbManager.getSavedDbChoice() == CentralRepoDbChoice.POSTGRESQL_MULTIUSER) {
-
+ if (cbUseCentralRepo.isSelected()
+ && !CentralRepoDbManager.isPostgresMultiuserAllowed()
+ && CentralRepoDbManager.getSavedDbChoice() == CentralRepoDbChoice.POSTGRESQL_MULTIUSER) {
+
CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.DISABLED);
}
-
- updateDatabase();
load();
this.ingestStateUpdated(Case.isCaseOpen());
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
@@ -620,20 +582,17 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
lbDbNameValue.setText("");
lbDbLocationValue.setText("");
tbOops.setText(Bundle.GlobalSettingsPanel_validationerrMsg_mustConfigure());
- }
- else {
+ } else {
enableButtonSubComponents(cbUseCentralRepo.isSelected());
if (selectedDb == CentralRepoPlatforms.POSTGRESQL) {
try {
- PostgresCentralRepoSettings dbSettingsPg = new PostgresCentralRepoSettings();
+ PostgresCentralRepoSettings dbSettingsPg = new PostgresCentralRepoSettings();
lbDbNameValue.setText(dbSettingsPg.getDbName());
lbDbLocationValue.setText(dbSettingsPg.getHost());
- }
- catch (CentralRepoException e) {
+ } catch (CentralRepoException e) {
logger.log(Level.WARNING, "Unable to load settings into global panel for postgres settings", e);
}
- }
- else if (selectedDb == CentralRepoPlatforms.SQLITE) {
+ } else if (selectedDb == CentralRepoPlatforms.SQLITE) {
SqliteCentralRepoSettings dbSettingsSqlite = new SqliteCentralRepoSettings();
lbDbNameValue.setText(dbSettingsSqlite.getDbName());
lbDbLocationValue.setText(dbSettingsSqlite.getDbDirectory());
@@ -647,7 +606,8 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
}
/**
- * This method validates that the dialog/panel is filled out correctly for our usage.
+ * This method validates that the dialog/panel is filled out correctly for
+ * our usage.
*
* @return True if it is okay, false otherwise.
*/
@@ -731,9 +691,9 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
enableButtonSubComponents(cbUseCentralRepo.isSelected());
} else {
load();
- enableDatabaseConfigureButton(cbUseCentralRepo.isSelected() && !caseIsOpen);
}
+ enableDatabaseConfigureButton(cbUseCentralRepo.isSelected() && !caseIsOpen);
}
/**
diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCaseSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCaseSearchResults.java
index e096638434..6b1fe1ab72 100644
--- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCaseSearchResults.java
+++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCaseSearchResults.java
@@ -123,7 +123,7 @@ final public class CommonAttributeCaseSearchResults {
if (currentCaseDataSourceMap == null) { //there are no results
return filteredCaseNameToDataSourcesTree;
}
- CorrelationAttributeInstance.Type attributeType = CentralRepository.getInstance().getCorrelationTypes()
+ CorrelationAttributeInstance.Type attributeType = CentralRepository.getInstance().getDefinedCorrelationTypes()
.stream()
.filter(filterType -> filterType.getId() == resultTypeId)
.findFirst().get();
diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java
index 85923e53b6..80a65b2835 100644
--- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java
+++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributeCountSearchResults.java
@@ -129,7 +129,7 @@ final public class CommonAttributeCountSearchResults {
}
CentralRepository eamDb = CentralRepository.getInstance();
- CorrelationAttributeInstance.Type attributeType = eamDb.getCorrelationTypes()
+ CorrelationAttributeInstance.Type attributeType = eamDb.getDefinedCorrelationTypes()
.stream()
.filter(filterType -> filterType.getId() == this.resultTypeId)
.findFirst().get();
diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java
index 43d834e39d..5e7aa3adb4 100644
--- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java
+++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/CommonAttributePanel.java
@@ -255,7 +255,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
filterByDocuments = interCasePanel.documentsCheckboxIsSelected();
}
if (corType == null) {
- corType = CentralRepository.getInstance().getCorrelationTypes().get(0);
+ corType = CentralRepository.getInstance().getDefinedCorrelationTypes().get(0);
}
if (caseId == InterCasePanel.NO_CASE_SELECTED) {
builder = new AllInterCaseCommonAttributeSearcher(filterByMedia, filterByDocuments, corType, percentageThreshold);
@@ -366,7 +366,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
filterByDocuments = interCasePanel.documentsCheckboxIsSelected();
}
if (corType == null) {
- corType = CentralRepository.getInstance().getCorrelationTypes().get(0);
+ corType = CentralRepository.getInstance().getDefinedCorrelationTypes().get(0);
}
if (caseId == InterCasePanel.NO_CASE_SELECTED) {
builder = new AllInterCaseCommonAttributeSearcher(filterByMedia, filterByDocuments, corType, percentageThreshold);
diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java
index a2c1c01529..f36f8ab520 100644
--- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java
+++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java
@@ -118,7 +118,7 @@ public final class InterCasePanel extends javax.swing.JPanel {
void setupCorrelationTypeFilter() {
this.correlationTypeFilters = new HashMap<>();
try {
- List types = CentralRepository.getInstance().getCorrelationTypes();
+ List types = CentralRepository.getInstance().getDefinedCorrelationTypes();
for (CorrelationAttributeInstance.Type type : types) {
correlationTypeFilters.put(type.getDisplayName(), type);
this.correlationTypeComboBox.addItem(type.getDisplayName());
diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/AccountSummary.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/AccountSummary.java
index 0888a105a3..5401f1a839 100755
--- a/Core/src/org/sleuthkit/autopsy/communications/relationships/AccountSummary.java
+++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/AccountSummary.java
@@ -33,6 +33,7 @@ import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.FileAttachment;
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
+import org.sleuthkit.datamodel.CommunicationsUtils;
/**
*
@@ -97,20 +98,27 @@ class AccountSummary {
boolean isReference = false;
- for (BlackboardAttribute attribute: attributes) {
+ for (BlackboardAttribute attribute : attributes) {
+
String attributeTypeName = attribute.getAttributeType().getTypeName();
String attributeValue = attribute.getValueString();
-
- if (attributeTypeName.contains("PHONE")) {
- attributeValue = RelationshipsNodeUtilities.normalizePhoneNum(attributeValue);
- } else if (attributeTypeName.contains("EMAIL")) {
- attributeValue = RelationshipsNodeUtilities.normalizeEmailAddress(attributeValue);
- }
-
- if ( typeSpecificID.equals(attributeValue) ) {
- isReference = true;
- break;
+ try {
+ if (attributeTypeName.contains("PHONE")) {
+ attributeValue = CommunicationsUtils.normalizePhoneNum(attributeValue);
+ } else if (attributeTypeName.contains("EMAIL")) {
+ attributeValue = CommunicationsUtils.normalizeEmailAddress(attributeValue);
+ }
+
+ if (typeSpecificID.equals(attributeValue)) {
+ isReference = true;
+ break;
+ }
+ } catch (TskCoreException ex) {
+ logger.log(Level.WARNING, String.format("Exception thrown "
+ + "in trying to normalize attribute value: %s",
+ attributeValue), ex); //NON-NLS
}
+
}
if (isReference) {
referenceCnt++;
diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java
index 288b3efa3f..3151d75780 100755
--- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java
+++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CallLogNode.java
@@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.communications.relationships;
import java.util.logging.Level;
+import org.apache.commons.lang.StringUtils;
import org.openide.nodes.Sheet;
import org.sleuthkit.autopsy.communications.Utils;
import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNodeUtilities.getAttributeDisplayString;
@@ -67,14 +68,6 @@ final class CallLogNode extends BlackboardArtifactNode {
return sheet;
}
- String phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM);
- if(phoneNumber == null || phoneNumber.isEmpty()) {
- phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO);
- }
- if(phoneNumber == null || phoneNumber.isEmpty()) {
- phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER);
- }
-
long duration = -1;
try{
duration = getCallDuration(artifact);
@@ -84,7 +77,7 @@ final class CallLogNode extends BlackboardArtifactNode {
sheetSet.put(createNode(TSK_DATETIME_START, artifact));
sheetSet.put(createNode(TSK_DIRECTION, artifact));
- sheetSet.put(new NodeProperty<>(TSK_PHONE_NUMBER.getLabel(), TSK_PHONE_NUMBER.getDisplayName(), "", phoneNumber));
+ sheetSet.put(new NodeProperty<>(TSK_PHONE_NUMBER.getLabel(), TSK_PHONE_NUMBER.getDisplayName(), "", getPhoneNumber(artifact)));
if(duration != -1) {
sheetSet.put(new NodeProperty<>("duration", "Duration", "", Long.toString(duration)));
}
@@ -107,6 +100,59 @@ final class CallLogNode extends BlackboardArtifactNode {
return endAttribute.getValueLong() - startAttribute.getValueLong();
}
+ /**
+ * Returns the phone number to display in the To/From column. The number is
+ * picked from one of the 3 possible phone number attributes, based on the
+ * direction of the call.
+ *
+ * @param artifact Call log artifact.
+ *
+ * @return Phone number to display.
+ */
+ private String getPhoneNumber(BlackboardArtifact artifact) {
+ String direction = getAttributeDisplayString(artifact, TSK_DIRECTION);
+
+ String phoneNumberToReturn;
+ String fromPhoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM);
+ String toPhoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO);
+ String phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER);
+ switch (direction.toLowerCase()) {
+ case "incoming": // NON-NLS
+ phoneNumberToReturn = getFirstNonBlank(fromPhoneNumber, phoneNumber, toPhoneNumber);
+ break;
+ case "outgoing": // NON-NLS
+ phoneNumberToReturn = getFirstNonBlank(toPhoneNumber, phoneNumber, fromPhoneNumber);
+ break;
+ default:
+ phoneNumberToReturn = getFirstNonBlank(toPhoneNumber, fromPhoneNumber, phoneNumber );
+ break;
+ }
+
+ return phoneNumberToReturn;
+ }
+
+ /**
+ * Checks the given string arguments in order and returns the first non blank string.
+ * Returns a blank string if all the input strings are blank.
+ *
+ * @param string1 First string to check
+ * @param string2 Second string to check
+ * @param string3 Third string to check
+ *
+ * @retunr first non blank string if there is one, blank string otherwise.
+ *
+ */
+ private String getFirstNonBlank(String string1, String string2, String string3 ) {
+
+ if (!StringUtils.isBlank(string1)) {
+ return string1;
+ } else if (!StringUtils.isBlank(string2)) {
+ return string2;
+ } else if (!StringUtils.isBlank(string3)) {
+ return string3;
+ }
+ return "";
+ }
/**
* Circumvent DataResultFilterNode's slightly odd delegation to
* BlackboardArtifactNode.getSourceName().
diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/CorrelationCaseChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/CorrelationCaseChildNodeFactory.java
index 49617c9dfb..67e0366a9b 100755
--- a/Core/src/org/sleuthkit/autopsy/communications/relationships/CorrelationCaseChildNodeFactory.java
+++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/CorrelationCaseChildNodeFactory.java
@@ -111,7 +111,7 @@ final class CorrelationCaseChildNodeFactory extends ChildFactory();
- List correcationTypeList = CentralRepository.getInstance().getCorrelationTypes();
+ List correcationTypeList = CentralRepository.getInstance().getDefinedCorrelationTypes();
correcationTypeList.forEach((type) -> {
correlationTypeMap.put(type.getId(), type);
});
diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java
index 252b4ee1ff..be150dd590 100755
--- a/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java
+++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/RelationshipsNodeUtilities.java
@@ -69,42 +69,4 @@ final class RelationshipsNodeUtilities {
}
}
- /**
- * Normalize the phone number by removing all non numeric characters, except
- * for leading +.
- *
- * This function copied from CommunicationManager.
- *
- * @param phoneNum The phone number to normalize
- *
- * @return The normalized phone number.
- */
- static String normalizePhoneNum(String phoneNum) {
- String normailzedPhoneNum = phoneNum.replaceAll("\\D", "");
-
- if (phoneNum.startsWith("+")) {
- normailzedPhoneNum = "+" + normailzedPhoneNum;
- }
-
- if (normailzedPhoneNum.isEmpty()) {
- normailzedPhoneNum = phoneNum;
- }
-
- return normailzedPhoneNum;
- }
-
- /**
- * Normalize the given email address by converting it to lowercase.
- *
- * This function copied from CommunicationManager.
- *
- * @param emailAddress The email address tot normalize
- *
- * @return The normalized email address.
- */
- static String normalizeEmailAddress(String emailAddress) {
- String normailzedEmailAddr = emailAddress.toLowerCase();
-
- return normailzedEmailAddr;
- }
}
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED
index 737b6900d4..c0a6f73aaf 100755
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED
@@ -41,6 +41,7 @@ MediaFileViewer.AccessibleContext.accessibleDescription=
MediaFileViewer.title=Media
MediaFileViewer.toolTip=Displays supported multimedia files (images, videos, audio)
MediaPlayerPanel.noSupport=File not supported.
+MediaPlayerPanel.playbackDisabled=A problem was encountered with the video and audio playback service. Video and audio playback will be disabled for the remainder of the session.
MediaPlayerPanel.timeFormat=%02d:%02d:%02d
MediaPlayerPanel.unknownTime=Unknown
MediaViewImagePanel.createTagOption=Create
@@ -168,7 +169,7 @@ MediaPlayerPanel.playBackSpeedLabel.text=Speed:
SQLiteViewer.readTable.errorText=Error getting rows for table: {0}
# {0} - tableName
SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}
-TextTranslatableComponent.setPanelContent.onSetContentError=Unable to display text at this time.
-TextTranslatableComponent.setTranslated.onTranslateError=Unable to translate text at this time.
TranslatablePanel.comboBoxOption.originalText=Original Text
TranslatablePanel.comboBoxOption.translatedText=Translated Text
+# {0} - exception message
+TranslatablePanel.onSetContentError.text=There was an error displaying the text: {0}
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java
index 56dd8e9ebe..9decc79175 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java
@@ -194,6 +194,6 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
@Override
public boolean isSupported(AbstractFile file){
- return true;
+ return mediaPlayerPanel.isSupported(file) || imagePanel.isSupported(file);
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.form
index 0b00c43d8a..793e31830b 100755
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.form
@@ -63,7 +63,7 @@
-
+
@@ -129,12 +129,6 @@
-
-
-
-
-
-
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java
index c518abdd45..40122acea7 100755
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java
@@ -74,7 +74,9 @@ import org.freedesktop.gstreamer.Format;
import org.freedesktop.gstreamer.GstException;
import org.freedesktop.gstreamer.event.SeekFlags;
import org.freedesktop.gstreamer.event.SeekType;
-import org.sleuthkit.autopsy.coreutils.PlatformUtil;
+import org.sleuthkit.autopsy.contentviewers.utils.GstLoader;
+import org.sleuthkit.autopsy.contentviewers.utils.GstLoader.GstStatus;
+import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
/**
* This is a video player that is part of the Media View layered pane. It uses
@@ -213,6 +215,8 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
//and the TrackListener on the slider itself.
private final Semaphore sliderLock;
+ private static volatile boolean IS_GST_ENABLED = true;
+
/**
* Creates a new MediaPlayerPanel
*/
@@ -222,18 +226,10 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
//True for fairness. In other words,
//acquire() calls are processed in order of invocation.
sliderLock = new Semaphore(1, true);
-
- /**
- * See JIRA-5888 for details. Initializing gstreamer here is more stable
- * on Windows.
- */
- if (PlatformUtil.isWindowsOS()) {
- Gst.init();
- }
}
private void customizeComponents() {
- progressSlider.setEnabled(false); // disable slider; enable after user plays vid
+ enableComponents(false);
progressSlider.setMinimum(0);
progressSlider.setMaximum(PROGRESS_SLIDER_SIZE);
progressSlider.setValue(0);
@@ -241,7 +237,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
progressSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
- if (progressSlider.getValueIsAdjusting()) {
+ if (progressSlider.getValueIsAdjusting() && gstPlayBin != null) {
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
double relativePosition = progressSlider.getValue() * 1.0 / PROGRESS_SLIDER_SIZE;
long newStartTime = (long) (relativePosition * duration);
@@ -264,20 +260,23 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
//Manage the video while the user is performing actions on the track.
progressSlider.addMouseListener(new MouseListener() {
private State previousState = State.NULL;
-
+
@Override
public void mousePressed(MouseEvent e) {
- previousState = gstPlayBin.getState();
- gstPlayBin.pause();
+ if (gstPlayBin != null) {
+ previousState = gstPlayBin.getState();
+ gstPlayBin.pause();
+ }
}
@Override
public void mouseReleased(MouseEvent e) {
- if(previousState.equals(State.PLAYING)) {
+ if (previousState.equals(State.PLAYING) && gstPlayBin != null) {
gstPlayBin.play();
}
previousState = State.NULL;
}
+
@Override
public void mouseClicked(MouseEvent e) {
}
@@ -289,11 +288,11 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
@Override
public void mouseExited(MouseEvent e) {
}
-
+
});
//Manage the audio level when the user is adjusting the volume slider
audioSlider.addChangeListener((ChangeEvent event) -> {
- if (audioSlider.getValueIsAdjusting()) {
+ if (audioSlider.getValueIsAdjusting() && gstPlayBin != null) {
double audioPercent = (audioSlider.getValue() * 2.0) / 100.0;
gstPlayBin.setVolume(audioPercent);
}
@@ -327,11 +326,13 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
endOfStreamListener = new Bus.EOS() {
@Override
public void endOfStream(GstObject go) {
- gstPlayBin.seek(ClockTime.ZERO);
- /**
- * Keep the video from automatically playing
- */
- Gst.getExecutor().submit(() -> gstPlayBin.pause());
+ if (gstPlayBin != null) {
+ gstPlayBin.seek(ClockTime.ZERO);
+ /**
+ * Keep the video from automatically playing
+ */
+ Gst.getExecutor().submit(() -> gstPlayBin.pause());
+ }
}
};
}
@@ -385,13 +386,16 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
}
timer.stop();
if (gstPlayBin != null) {
- gstPlayBin.stop();
- gstPlayBin.getBus().disconnect(endOfStreamListener);
- gstPlayBin.getBus().disconnect(stateChangeListener);
- gstPlayBin.getBus().disconnect(errorListener);
- gstPlayBin.dispose();
- fxAppSink.clear();
- gstPlayBin = null;
+ Gst.getExecutor().submit(() -> {
+ gstPlayBin.stop();
+ gstPlayBin.getBus().disconnect(endOfStreamListener);
+ gstPlayBin.getBus().disconnect(stateChangeListener);
+ gstPlayBin.getBus().disconnect(errorListener);
+ gstPlayBin.getBus().dispose();
+ gstPlayBin.dispose();
+ fxAppSink.clear();
+ gstPlayBin = null;
+ });
}
videoPanel.removeAll();
resetComponents();
@@ -420,6 +424,10 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
@Override
public boolean isSupported(AbstractFile file) {
+ if (!IS_GST_ENABLED) {
+ return false;
+ }
+
String extension = file.getNameExtension();
/**
* Although it seems too restrictive, requiring both a supported
@@ -537,6 +545,11 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
/*
* Initialize the playback components if the extraction was successful.
*/
+ @NbBundle.Messages({
+ "MediaPlayerPanel.playbackDisabled=A problem was encountered with"
+ + " the video and audio playback service. Video and audio "
+ + "playback will be disabled for the remainder of the session."
+ })
@Override
protected void done() {
try {
@@ -546,41 +559,49 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
return;
}
- // Initialize Gstreamer. It is safe to call this for every file.
- // It was moved here from the constructor because having it happen
- // earlier resulted in conflicts on Linux. See JIRA-5888.
- if (!PlatformUtil.isWindowsOS()) {
- Gst.init();
- }
+ GstStatus loadStatus = GstLoader.tryLoad();
+ if (loadStatus == GstStatus.FAILURE) {
+ MessageNotifyUtil.Message.error(Bundle.MediaPlayerPanel_playbackDisabled());
- //Video is ready for playback. Create new components
- gstPlayBin = new PlayBin("VideoPlayer", tempFile.toURI());
- //Configure event handling
- Bus playBinBus = gstPlayBin.getBus();
- playBinBus.connect(endOfStreamListener);
- playBinBus.connect(stateChangeListener);
- playBinBus.connect(errorListener);
-
- if (this.isCancelled()) {
+ // This will disable the panel for future use.
+ IS_GST_ENABLED = false;
return;
}
- JFXPanel fxPanel = new JFXPanel();
- videoPanel.removeAll();
- videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
- videoPanel.add(fxPanel);
- fxAppSink = new JavaFxAppSink("JavaFxAppSink", fxPanel);
- gstPlayBin.setVideoSink(fxAppSink);
+ Gst.getExecutor().submit(() -> {
+ //Video is ready for playback. Create new components
+ gstPlayBin = new PlayBin("VideoPlayer", tempFile.toURI());
+ //Configure event handling
+ Bus playBinBus = gstPlayBin.getBus();
+ playBinBus.connect(endOfStreamListener);
+ playBinBus.connect(stateChangeListener);
+ playBinBus.connect(errorListener);
- if (this.isCancelled()) {
- return;
- }
+ if (this.isCancelled()) {
+ return;
+ }
- gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0);
- gstPlayBin.pause();
+ JFXPanel fxPanel = new JFXPanel();
+ videoPanel.removeAll();
+ videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
+ videoPanel.add(fxPanel);
+ fxAppSink = new JavaFxAppSink("JavaFxAppSink", fxPanel);
+ if (gstPlayBin != null) {
+ gstPlayBin.setVideoSink(fxAppSink);
+ }
+ if (this.isCancelled()) {
+ return;
+ }
+ if (gstPlayBin != null) {
+ gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0);
+ gstPlayBin.pause();
+ }
- timer.start();
- enableComponents(true);
+ timer.start();
+ SwingUtilities.invokeLater(() -> {
+ enableComponents(true);
+ });
+ });
} catch (CancellationException ex) {
logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS
} catch (InterruptedException ex) {
@@ -598,24 +619,30 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
@Override
public void actionPerformed(ActionEvent e) {
- if (!progressSlider.getValueIsAdjusting()) {
- sliderLock.acquireUninterruptibly();
- long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
- long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
- /**
- * Duration may not be known until there is video data in the
- * pipeline. We start this updater when data-flow has just been
- * initiated so buffering may still be in progress.
- */
- if (duration >= 0 && position >= 0) {
- double relativePosition = (double) position / duration;
- progressSlider.setValue((int) (relativePosition * PROGRESS_SLIDER_SIZE));
- }
+ if (!progressSlider.getValueIsAdjusting() && gstPlayBin != null) {
+ Gst.getExecutor().submit(() -> {
+ try {
+ sliderLock.acquireUninterruptibly();
+ long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
+ long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
+ /**
+ * Duration may not be known until there is video data
+ * in the pipeline. We start this updater when data-flow
+ * has just been initiated so buffering may still be in
+ * progress.
+ */
+ if (duration >= 0 && position >= 0) {
+ double relativePosition = (double) position / duration;
+ progressSlider.setValue((int) (relativePosition * PROGRESS_SLIDER_SIZE));
+ }
- SwingUtilities.invokeLater(() -> {
- updateTimeLabel(position, duration);
+ SwingUtilities.invokeLater(() -> {
+ updateTimeLabel(position, duration);
+ });
+ } finally {
+ sliderLock.release();
+ }
});
- sliderLock.release();
}
}
}
@@ -641,7 +668,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
public CircularJSliderUI(JSlider slider, Dimension thumbDimension) {
super(slider);
this.thumbDimension = thumbDimension;
-
+
//Configure track and thumb colors.
Color lightBlue = new Color(0, 130, 255);
thumbColor = lightBlue;
@@ -655,8 +682,8 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
}
/**
- * Modifies the View to be an oval rather than the underlying
- * rectangle Controller.
+ * Modifies the View to be an oval rather than the underlying rectangle
+ * Controller.
*/
@Override
public void paintThumb(Graphics graphic) {
@@ -705,12 +732,13 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
@Override
protected TrackListener createTrackListener(JSlider slider) {
/**
- * This track listener will force the thumb to be snapped to the mouse
- * location. This makes grabbing and dragging the JSlider much easier.
- * Using the default track listener, the user would have to click
- * exactly on the slider thumb to drag it. Now the thumb positions
- * itself under the mouse so that it can always be dragged.
- */
+ * This track listener will force the thumb to be snapped to the
+ * mouse location. This makes grabbing and dragging the JSlider much
+ * easier. Using the default track listener, the user would have to
+ * click exactly on the slider thumb to drag it. Now the thumb
+ * positions itself under the mouse so that it can always be
+ * dragged.
+ */
return new TrackListener() {
@Override
public void mousePressed(MouseEvent e) {
@@ -982,88 +1010,104 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
}// //GEN-END:initComponents
private void rewindButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rewindButtonActionPerformed
- long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
- //Skip 30 seconds.
- long rewindDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
- //Ensure new video position is within bounds
- long newTime = Math.max(currentTime - rewindDelta, 0);
- double playBackRate = getPlayBackRate();
- gstPlayBin.seek(playBackRate,
- Format.TIME,
- //FLUSH - flushes the pipeline
- //ACCURATE - video will seek exactly to the position requested
- EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
- //Set the start position to newTime
- SeekType.SET, newTime,
- //Do nothing for the end position
- SeekType.NONE, -1);
+ Gst.getExecutor().submit(() -> {
+ if (gstPlayBin != null) {
+ long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
+ //Skip 30 seconds.
+ long rewindDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
+ //Ensure new video position is within bounds
+ long newTime = Math.max(currentTime - rewindDelta, 0);
+ double playBackRate = getPlayBackRate();
+ gstPlayBin.seek(playBackRate,
+ Format.TIME,
+ //FLUSH - flushes the pipeline
+ //ACCURATE - video will seek exactly to the position requested
+ EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
+ //Set the start position to newTime
+ SeekType.SET, newTime,
+ //Do nothing for the end position
+ SeekType.NONE, -1);
+ }
+ });
}//GEN-LAST:event_rewindButtonActionPerformed
- private void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fastForwardButtonActionPerformed
- long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
- long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
- //Skip 30 seconds.
- long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
- //Don't allow skipping within 2 seconds of video ending. Skipping right to
- //the end causes undefined behavior for some gstreamer plugins.
- long twoSecondsInNano = TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);
- if((duration - currentTime) <= twoSecondsInNano) {
- return;
- }
-
- long newTime;
- if (currentTime + fastForwardDelta >= duration) {
- //If there are less than 30 seconds left, only fast forward to the midpoint.
- newTime = currentTime + (duration - currentTime)/2;
- } else {
- newTime = currentTime + fastForwardDelta;
- }
+ private void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt) {
+ Gst.getExecutor().submit(() -> {
+ if (gstPlayBin != null) {
+ long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
+ long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
+ //Skip 30 seconds.
+ long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
+ //Don't allow skipping within 2 seconds of video ending. Skipping right to
+ //the end causes undefined behavior for some gstreamer plugins.
+ long twoSecondsInNano = TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);
+ if ((duration - currentTime) <= twoSecondsInNano) {
+ return;
+ }
- double playBackRate = getPlayBackRate();
- gstPlayBin.seek(playBackRate,
- Format.TIME,
- //FLUSH - flushes the pipeline
- //ACCURATE - video will seek exactly to the position requested
- EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
- //Set the start position to newTime
- SeekType.SET, newTime,
- //Do nothing for the end position
- SeekType.NONE, -1);
- }//GEN-LAST:event_fastForwardButtonActionPerformed
+ long newTime;
+ if (currentTime + fastForwardDelta >= duration) {
+ //If there are less than 30 seconds left, only fast forward to the midpoint.
+ newTime = currentTime + (duration - currentTime) / 2;
+ } else {
+ newTime = currentTime + fastForwardDelta;
+ }
- private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playButtonActionPerformed
- if (gstPlayBin.isPlaying()) {
- gstPlayBin.pause();
- } else {
- double playBackRate = getPlayBackRate();
- long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
- //Set playback rate before play.
- gstPlayBin.seek(playBackRate,
- Format.TIME,
- //FLUSH - flushes the pipeline
- //ACCURATE - video will seek exactly to the position requested
- EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
- //Set the start position to newTime
- SeekType.SET, currentTime,
- //Do nothing for the end position
- SeekType.NONE, -1);
- gstPlayBin.play();
- }
- }//GEN-LAST:event_playButtonActionPerformed
+ double playBackRate = getPlayBackRate();
+ gstPlayBin.seek(playBackRate,
+ Format.TIME,
+ //FLUSH - flushes the pipeline
+ //ACCURATE - video will seek exactly to the position requested
+ EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
+ //Set the start position to newTime
+ SeekType.SET, newTime,
+ //Do nothing for the end position
+ SeekType.NONE, -1);
+ }
+ });
+ }
- private void playBackSpeedComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playBackSpeedComboBoxActionPerformed
- double playBackRate = getPlayBackRate();
- long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
- gstPlayBin.seek(playBackRate,
- Format.TIME,
- //FLUSH - flushes the pipeline
- //ACCURATE - video will seek exactly to the position requested
- EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
- //Set the position to the currentTime, we are only adjusting the
- //playback rate.
- SeekType.SET, currentTime,
- SeekType.NONE, 0);
- }//GEN-LAST:event_playBackSpeedComboBoxActionPerformed
+ private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {
+ Gst.getExecutor().submit(() -> {
+ if (gstPlayBin != null) {
+ if (gstPlayBin.isPlaying()) {
+ gstPlayBin.pause();
+ } else {
+ double playBackRate = getPlayBackRate();
+ long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
+ //Set playback rate before play.
+ gstPlayBin.seek(playBackRate,
+ Format.TIME,
+ //FLUSH - flushes the pipeline
+ //ACCURATE - video will seek exactly to the position requested
+ EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
+ //Set the start position to newTime
+ SeekType.SET, currentTime,
+ //Do nothing for the end position
+ SeekType.NONE, -1);
+ gstPlayBin.play();
+ }
+ }
+ });
+ }
+
+ private void playBackSpeedComboBoxActionPerformed(java.awt.event.ActionEvent evt) {
+ Gst.getExecutor().submit(() -> {
+ if (gstPlayBin != null) {
+ double playBackRate = getPlayBackRate();
+ long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
+ gstPlayBin.seek(playBackRate,
+ Format.TIME,
+ //FLUSH - flushes the pipeline
+ //ACCURATE - video will seek exactly to the position requested
+ EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
+ //Set the position to the currentTime, we are only adjusting the
+ //playback rate.
+ SeekType.SET, currentTime,
+ SeekType.NONE, 0);
+ }
+ });
+ }
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel VolumeIcon;
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/utils/GstLoader.java b/Core/src/org/sleuthkit/autopsy/contentviewers/utils/GstLoader.java
new file mode 100755
index 0000000000..b6804d488a
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/utils/GstLoader.java
@@ -0,0 +1,74 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2020 Basis Technology Corp.
+ * Contact: carrier sleuthkit 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.utils;
+
+import java.util.logging.Level;
+import org.freedesktop.gstreamer.Gst;
+import org.sleuthkit.autopsy.coreutils.Logger;
+
+/**
+ * A utility class that loads the gstreamer bindings.
+ */
+public final class GstLoader {
+
+ private static final Logger logger = Logger.getLogger(GstLoader.class.getName());
+ private static GstStatus status;
+
+ /**
+ * Attempts to load the gstreamer bindings. Only one attempt will be
+ * performed per Autopsy process. Clients should not attempt to interact
+ * with the gstreamer bindings unless the load was successful.
+ *
+ * @return Status - SUCCESS or FAILURE
+ */
+ public synchronized static GstStatus tryLoad() {
+ // Null is our 'unknown' status. Prior to the first call, the status
+ // is unknown.
+ if (status != null) {
+ return status;
+ }
+
+ try {
+ // Setting the following property causes the GST
+ // Java bindings to call dispose() on the GST
+ // service thread instead of running it in the GST
+ // Native Object Reaper thread.
+ System.setProperty("glib.reapOnEDT", "true");
+ Gst.init();
+ status = GstStatus.SUCCESS;
+ } catch (Throwable ex) {
+ status = GstStatus.FAILURE;
+ logger.log(Level.WARNING, "Failed to load gsteamer bindings", ex);
+ }
+
+ return status;
+ }
+
+ /**
+ * The various init statuses that tryLoad can return.
+ */
+ public enum GstStatus {
+ SUCCESS, FAILURE
+ }
+
+ private GstLoader() {
+
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/PlatformUtil.java b/Core/src/org/sleuthkit/autopsy/coreutils/PlatformUtil.java
index 12415fac87..e729248b47 100644
--- a/Core/src/org/sleuthkit/autopsy/coreutils/PlatformUtil.java
+++ b/Core/src/org/sleuthkit/autopsy/coreutils/PlatformUtil.java
@@ -69,7 +69,7 @@ public class PlatformUtil {
* @return absolute path string to the install root dir
*/
public static String getInstallPath() {
- File coreFolder = InstalledFileLocator.getDefault().locate("core", "org.sleuthkit.autopsy.core", false); //NON-NLS
+ File coreFolder = InstalledFileLocator.getDefault().locate("core", PlatformUtil.class.getPackage().getName(), false); //NON-NLS
File rootPath = coreFolder.getParentFile().getParentFile();
return rootPath.getAbsolutePath();
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java
index 1d53e46ca6..74ac603e0b 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java
@@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
+import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
@@ -28,7 +29,6 @@ import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
-import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet;
@@ -66,6 +66,7 @@ import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
+import org.sleuthkit.autopsy.texttranslation.utils.FileNameTranslationUtil;
/**
* An abstract node that encapsulates AbstractFile data
@@ -94,15 +95,15 @@ public abstract class AbstractAbstractFileNode extends A
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
}
}
-
+
try {
//See JIRA-5971
//Attempt to cache file path during construction of this UI component.
this.content.getUniquePath();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed attempt to cache the "
- + "unique path of the abstract file instance. Name: %s (objID=%d)",
- this.content.getName(), this.content.getId()), ex);
+ + "unique path of the abstract file instance. Name: %s (objID=%d)",
+ this.content.getName(), this.content.getId()), ex);
}
if (TextTranslationService.getInstance().hasProvider() && UserPreferences.displayTranslatedFileNames()) {
@@ -490,39 +491,18 @@ public abstract class AbstractAbstractFileNode extends A
}
/**
- * Translates this nodes content name. Doesn't attempt translation if the
- * name is in english or if there is now translation service available.
+ * Translates the name of the file this node represents. An empty string
+ * will be returned if the translation fails for any reason.
+ *
+ * @return The translated file name or the empty string.
*/
String getTranslatedFileName() {
- //If already in complete English, don't translate.
- if (content.getName().matches("^\\p{ASCII}+$")) {
+ try {
+ return FileNameTranslationUtil.translate(content.getName());
+ } catch (NoServiceProviderException | TranslationException ex) {
+ logger.log(Level.WARNING, MessageFormat.format("Error translating file name (objID={0}))", content.getId()), ex);
return "";
}
- TextTranslationService tts = TextTranslationService.getInstance();
- if (tts.hasProvider()) {
- //Seperate out the base and ext from the contents file name.
- String base = FilenameUtils.getBaseName(content.getName());
- try {
- String translation = tts.translate(base);
- String ext = FilenameUtils.getExtension(content.getName());
-
- //If we have no extension, then we shouldn't add the .
- String extensionDelimiter = (ext.isEmpty()) ? "" : ".";
-
- //Talk directly to this nodes pcl, fire an update when the translation
- //is complete.
- if (!translation.isEmpty()) {
- return translation + extensionDelimiter + ext;
- }
- } catch (NoServiceProviderException noServiceEx) {
- logger.log(Level.WARNING, "Translate unsuccessful because no TextTranslator "
- + "implementation was provided.", noServiceEx.getMessage());
- } catch (TranslationException noTranslationEx) {
- logger.log(Level.WARNING, "Could not successfully translate file name "
- + content.getName(), noTranslationEx.getMessage());
- }
- }
- return "";
}
/**
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
index 17b8248f06..8358dd7d34 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
@@ -60,7 +60,6 @@ import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score;
import org.sleuthkit.autopsy.coreutils.Logger;
-import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
@@ -77,47 +76,68 @@ import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
+import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
+import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
+import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask;
/**
- * Node wrapping a blackboard artifact object. This is generated from several
- * places in the tree.
+ * A BlackboardArtifactNode is an AbstractNode implementation that can be used
+ * to represent an artifact of any type.
*/
public class BlackboardArtifactNode extends AbstractContentNode {
private static final Logger logger = Logger.getLogger(BlackboardArtifactNode.class.getName());
- private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
+
+ /*
+ * Cache of Content objects used to avoid repeated trips to the case
+ * database to retrieve Content objects that are the source of multiple
+ * artifacts.
+ */
+ private static final Cache contentCache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
+
+ /*
+ * Case events that indicate an update to the node's property sheet may be
+ * required.
+ */
+ private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(
+ Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED,
Case.Events.CONTENT_TAG_ADDED,
Case.Events.CONTENT_TAG_DELETED,
- Case.Events.CURRENT_CASE,
- Case.Events.CR_COMMENT_CHANGED);
-
- private static Cache contentCache = CacheBuilder.newBuilder()
- .expireAfterWrite(1, TimeUnit.MINUTES).
- build();
-
- private final BlackboardArtifact artifact;
- private Content associated = null;
-
- private List> customProperties;
+ Case.Events.CR_COMMENT_CHANGED,
+ Case.Events.CURRENT_CASE);
/*
- * Artifact types which should have the full unique path of the associated
- * content as a property.
+ * Artifact types for which the unique path of the artifact's source content
+ * should be displayed in the node's property sheet.
*/
private static final Integer[] SHOW_UNIQUE_PATH = new Integer[]{
BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
- BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID(),};
+ BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
+ };
- // TODO (RC): This is an unattractive alternative to subclassing BlackboardArtifactNode,
- // cut from the same cloth as the equally unattractive SHOW_UNIQUE_PATH array
- // above. It should be removed when and if the subclassing is implemented.
+ /*
+ * Artifact types for which the file metadata of the artifact's source file
+ * should be displayed in the node's property sheet.
+ */
private static final Integer[] SHOW_FILE_METADATA = new Integer[]{
- BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),};
+ BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
+ };
- private final PropertyChangeListener pcl = new PropertyChangeListener() {
+ private final BlackboardArtifact artifact;
+ private Content srcContent;
+ private volatile String translatedSourceName;
+
+ /*
+ * A method has been provided to allow the injection of properties into this
+ * node for display in the node's property sheet, independent of the
+ * artifact the node represents.
+ */
+ private List> customProperties;
+
+ private final PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
@@ -133,154 +153,218 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft()));
+ updateSheet(new NodeProperty<>(
+ Bundle.BlackboardArtifactNode_createSheet_score_name(),
+ Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
+ scoData.getScoreAndDescription().getRight(),
+ scoData.getScoreAndDescription().getLeft()));
}
if (scoData.getComment() != null) {
- updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, scoData.getComment()));
+ updateSheet(new NodeProperty<>(
+ Bundle.BlackboardArtifactNode_createSheet_comment_name(),
+ Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
+ NO_DESCR, scoData.getComment()));
}
if (scoData.getCountAndDescription() != null) {
- updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
+ updateSheet(new NodeProperty<>(
+ Bundle.BlackboardArtifactNode_createSheet_count_name(),
+ Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
+ scoData.getCountAndDescription().getRight(),
+ scoData.getCountAndDescription().getLeft()));
}
+ } else if (eventType.equals(FileNameTransTask.getPropertyName())) {
+ /*
+ * Replace the value of the Source File property with the
+ * translated name via setDisplayName (see note in createSheet),
+ * and put the untranslated name in the Original Name property
+ * and in the tooltip.
+ */
+ String originalName = evt.getOldValue().toString();
+ translatedSourceName = evt.getNewValue().toString();
+ setDisplayName(translatedSourceName);
+ setShortDescription(originalName);
+ updateSheet(new NodeProperty<>(
+ Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
+ Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
+ NO_DESCR,
+ originalName));
}
}
};
- /**
- * We pass a weak reference wrapper around the listener to the event
- * publisher. This allows Netbeans to delete the node when the user
- * navigates to another part of the tree (previously, nodes were not being
- * deleted because the event publisher was holding onto a strong reference
- * to the listener. We need to hold onto the weak reference here to support
- * unregistering of the listener in removeListeners() below.
+ /*
+ * The node's event listener is wrapped in a weak reference that allows the
+ * node to be garbage collected when the NetBeans infrastructure discards
+ * it. If this is not done, it has been shown that strong references to the
+ * listener held by event publishers prevents garbage collection of this
+ * node.
*/
- private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
+ private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
/**
- * Construct blackboard artifact node from an artifact, overriding the
- * standard icon with the one at the path provided.
+ * Constructs a BlackboardArtifactNode, an AbstractNode implementation that
+ * can be used to represent an artifact of any type.
*
- *
- * @param artifact artifact to encapsulate
- * @param iconPath icon to use for the artifact
+ * @param artifact The artifact to represent.
+ * @param iconPath The path to the icon for the artifact type.
*/
public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
super(artifact, createLookup(artifact));
-
this.artifact = artifact;
-
- // Look for associated Content i.e. the source file for the artifact
for (Content lookupContent : this.getLookup().lookupAll(Content.class)) {
if ((lookupContent != null) && (!(lookupContent instanceof BlackboardArtifact))) {
- this.associated = lookupContent;
-
+ srcContent = lookupContent;
try {
- //See JIRA-5971
- //Attempt to cache file path during construction of this UI component.
- this.associated.getUniquePath();
+ /*
+ * Calling this getter causes the unique path of the source
+ * content to be cached in the Content object. This is
+ * advantageous as long as this node is constructed in a
+ * background thread instead of a UI thread.
+ */
+ srcContent.getUniquePath();
} catch (TskCoreException ex) {
- logger.log(Level.SEVERE, String.format("Failed attempt to cache the "
- + "unique path of the associated content instance. Name: %s (objID=%d)",
- this.associated.getName(), this.associated.getId()), ex);
+ logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
}
break;
}
}
-
- this.setName(Long.toString(artifact.getArtifactID()));
- this.setDisplayName();
- this.setIconBaseWithExtension(iconPath);
- Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
+ if (srcContent == null) {
+ throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
+ }
+ setName(Long.toString(artifact.getArtifactID()));
+ String displayName = srcContent.getName();
+ setDisplayName(displayName);
+ setShortDescription(displayName);
+ setIconBaseWithExtension(iconPath);
+ Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener);
}
/**
- * Construct blackboard artifact node from an artifact and using default
- * icon for artifact type
+ * Constructs a BlackboardArtifactNode, an AbstractNode implementation that
+ * can be used to represent an artifact of any type.
*
- * @param artifact artifact to encapsulate
+ * @param artifact The artifact to represent.
*/
public BlackboardArtifactNode(BlackboardArtifact artifact) {
this(artifact, ExtractedContent.getIconFilePath(artifact.getArtifactTypeID()));
}
/**
- * The finalizer removes event listeners as the BlackboardArtifactNode is
- * being garbage collected. Yes, we know that finalizers are considered to
- * be "bad" but since the alternative also relies on garbage collection
- * being run and we know that finalize will be called when the object is
- * being GC'd it seems like this is a reasonable solution.
+ * Creates a Lookup object for this node and populates it with both the
+ * artifact this node represents and its source content.
+ *
+ * @param artifact The artifact this node represents.
+ *
+ * @return The Lookup.
+ */
+ private static Lookup createLookup(BlackboardArtifact artifact) {
+ final long objectID = artifact.getObjectID();
+ try {
+ Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
+ if (content == null) {
+ return Lookups.fixed(artifact);
+ } else {
+ return Lookups.fixed(artifact, content);
+ }
+ } catch (ExecutionException ex) {
+ logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
+ return Lookups.fixed(artifact);
+ }
+ }
+
+ /**
+ * Unregisters the application event listener when this node is garbage
+ * collected, if this finalizer is actually called.
+ *
+ * RC: Isn't there some node lifecycle property change event that could be
+ * used to unregister the listener instead?
*
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
- removeListeners();
+ unregisterListener();
}
- private void removeListeners() {
- Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
+ /**
+ * Unregisters this node's application event listener.
+ */
+ private void unregisterListener() {
+ Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener);
}
+ /**
+ * Gets the artifact represented by this node.
+ *
+ * @return The artifact.
+ */
public BlackboardArtifact getArtifact() {
return this.artifact;
}
@Override
- @NbBundle.Messages({
- "BlackboardArtifactNode.getAction.errorTitle=Error getting actions",
- "BlackboardArtifactNode.getAction.resultErrorMessage=There was a problem getting actions for the selected result."
- + " The 'View Result in Timeline' action will not be available.",
- "BlackboardArtifactNode.getAction.linkedFileMessage=There was a problem getting actions for the selected result. "
- + " The 'View File in Timeline' action will not be available."})
public Action[] getActions(boolean context) {
List actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(super.getActions(context)));
- AbstractFile file = getLookup().lookup(AbstractFile.class);
- //if this artifact has a time stamp add the action to view it in the timeline
+ /*
+ * If the artifact represented by this node has a timestamp, add an
+ * action to view it in the timeline.
+ */
try {
if (ViewArtifactInTimelineAction.hasSupportedTimeStamp(artifact)) {
actionsList.add(new ViewArtifactInTimelineAction(artifact));
}
} catch (TskCoreException ex) {
- logger.log(Level.SEVERE, MessageFormat.format("Error getting arttribute(s) from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS
- MessageNotifyUtil.Notify.error(Bundle.BlackboardArtifactNode_getAction_errorTitle(), Bundle.BlackboardArtifactNode_getAction_resultErrorMessage());
+ logger.log(Level.SEVERE, MessageFormat.format("Error getting artifact timestamp (artifact objID={0})", artifact.getId()), ex); //NON-NLS
}
- // if the artifact links to another file, add an action to go to that file
+ /*
+ * If the artifact represented by this node is linked to a file via a
+ * TSK_PATH_ID attribute, add an action to view the file in the
+ * timeline.
+ */
try {
- AbstractFile c = findLinked(artifact);
- if (c != null) {
- actionsList.add(ViewFileInTimelineAction.createViewFileAction(c));
+ AbstractFile linkedFile = findLinked(artifact);
+ if (linkedFile != null) {
+ actionsList.add(ViewFileInTimelineAction.createViewFileAction(linkedFile));
}
} catch (TskCoreException ex) {
- logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS
- MessageNotifyUtil.Notify.error(Bundle.BlackboardArtifactNode_getAction_errorTitle(), Bundle.BlackboardArtifactNode_getAction_linkedFileMessage());
+ logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file of artifact (artifact objID={0})", artifact.getId()), ex); //NON-NLS
+
}
- //if the artifact has associated content, add the action to view the content in the timeline
+ /*
+ * If the source content of the artifact represented by this node is a
+ * file, add an action to view the file in the data source tree.
+ */
+ AbstractFile file = getLookup().lookup(AbstractFile.class
+ );
if (null != file) {
actionsList.add(ViewFileInTimelineAction.createViewSourceFileAction(file));
}
@@ -288,67 +372,21 @@ public class BlackboardArtifactNode extends AbstractContentNode map = new LinkedHashMap<>();
- fillPropertyMap(map, artifact);
-
- sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.name"),
- NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.displayName"),
+ /*
+ * Add the name of the source content of the artifact represented by
+ * this node to the sheet. The value of this property is the same as the
+ * display name of the node and this a "special" property that displays
+ * the node's icon as well as the display name.
+ */
+ sheetSet.put(new NodeProperty<>(
+ Bundle.BlackboardArtifactNode_createSheet_srcFile_name(),
+ Bundle.BlackboardArtifactNode_createSheet_srcFile_displayName(),
NO_DESCR,
- this.getSourceName()));
+ getDisplayName()));
- // Create place holders for S C O
- if (!UserPreferences.getHideSCOColumns()) {
- sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), VALUE_LOADING, ""));
- sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), VALUE_LOADING, ""));
- if (CentralRepository.isEnabled()) {
- sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), VALUE_LOADING, ""));
+ if (TextTranslationService.getInstance().hasProvider() && UserPreferences.displayTranslatedFileNames()) {
+ /*
+ * If machine translation is configured, add the original name of
+ * the of the source content of the artifact represented by this
+ * node to the sheet.
+ */
+ sheetSet.put(new NodeProperty<>(
+ Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
+ Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
+ NO_DESCR,
+ translatedSourceName != null ? srcContent.getName() : ""));
+ if (translatedSourceName == null) {
+ /*
+ * NOTE: The task makes its own weak reference to the listener.
+ */
+ new FileNameTransTask(srcContent.getName(), this, listener).submit();
}
- // Get the SCO columns data in a background task
- backgroundTasksPool.submit(new GetSCOTask(
- new WeakReference<>(this), weakPcl));
}
+ if (!UserPreferences.getHideSCOColumns()) {
+ /*
+ * Add S(core), C(omments), and O(ther occurences) columns to the
+ * sheet and start a background task to compute the value of these
+ * properties for the artifact represented by this node. The task
+ * will fire a PropertyChangeEvent when the computation is completed
+ * and this node's PropertyChangeListener will update the sheet.
+ */
+ sheetSet.put(new NodeProperty<>(
+ Bundle.BlackboardArtifactNode_createSheet_score_name(),
+ Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
+ VALUE_LOADING,
+ ""));
+ sheetSet.put(new NodeProperty<>(
+ Bundle.BlackboardArtifactNode_createSheet_comment_name(),
+ Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
+ VALUE_LOADING,
+ ""));
+ if (CentralRepository.isEnabled()) {
+ sheetSet.put(new NodeProperty<>(
+ Bundle.BlackboardArtifactNode_createSheet_count_name(),
+ Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
+ VALUE_LOADING,
+ ""));
+ }
+ backgroundTasksPool.submit(new GetSCOTask(new WeakReference<>(this), weakListener));
+ }
+
+ /*
+ * If the artifact represented by this node is an interesting artifact
+ * hit, add the type and description of the interesting artifact to the
+ * sheet.
+ */
if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
try {
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
if (attribute != null) {
BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
- sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.name"),
+ sheetSet.put(new NodeProperty<>(
+ NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.displayName"),
NO_DESCR,
associatedArtifact.getDisplayName()));
- sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.name"),
+ sheetSet.put(new NodeProperty<>(
+ NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
NO_DESCR,
associatedArtifact.getShortDescription()));
}
} catch (TskCoreException | NoCurrentCaseException ex) {
- // Do nothing since the display name will be set to the file name.
+ logger.log(Level.SEVERE, MessageFormat.format("Error getting associated artifact of TSK_INTERESTING_ARTIFACT_HIT artifact (objID={0}))", artifact.getId()), ex); //NON-NLS
}
}
+ /*
+ * Add the attributes of the artifact represented by this node to the
+ * sheet.
+ */
+ Map map = new LinkedHashMap<>();
+ fillPropertyMap(map, artifact);
for (Map.Entry entry : map.entrySet()) {
sheetSet.put(new NodeProperty<>(entry.getKey(),
entry.getKey(),
@@ -415,28 +508,35 @@ public class BlackboardArtifactNode extends AbstractContentNode np : customProperties) {
sheetSet.put(np);
}
}
+ /*
+ * If the artifact represented by this node is a file extension mismatch
+ * artifact, add the extension and type of the artifact's source file to
+ * the sheet.
+ */
final int artifactTypeId = artifact.getArtifactTypeID();
-
- // If mismatch, add props for extension and file type
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
String ext = ""; //NON-NLS
String actualMimeType = ""; //NON-NLS
- if (associated instanceof AbstractFile) {
- AbstractFile af = (AbstractFile) associated;
- ext = af.getNameExtension();
- actualMimeType = af.getMIMEType();
+ if (srcContent instanceof AbstractFile) {
+ AbstractFile file = (AbstractFile) srcContent;
+ ext = file.getNameExtension();
+ actualMimeType = file.getMIMEType();
if (actualMimeType == null) {
actualMimeType = ""; //NON-NLS
+
}
}
- sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.name"),
+ sheetSet.put(new NodeProperty<>(
+ NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.displayName"),
NO_DESCR,
ext));
@@ -447,12 +547,17 @@ public class BlackboardArtifactNode extends AbstractContentNode(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
+ AbstractFile file = srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null;
+ sheetSet.put(new NodeProperty<>(
+ NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"),
"",
file == null ? "" : ContentUtils.getStringTime(file.getMtime(), file)));
- sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"),
+ sheetSet.put(new NodeProperty<>(
+ NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.displayName"),
"",
file == null ? "" : ContentUtils.getStringTime(file.getCtime(), file)));
- sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"),
+ sheetSet.put(new NodeProperty<>(
+ NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.displayName"),
"",
file == null ? "" : ContentUtils.getStringTime(file.getAtime(), file)));
- sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"),
+ sheetSet.put(new NodeProperty<>(
+ NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.displayName"),
"",
file == null ? "" : ContentUtils.getStringTime(file.getCrtime(), file)));
- sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"),
+ sheetSet.put(new NodeProperty<>(
+ NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"),
"",
- associated.getSize()));
- sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(),
+ file == null ? "" : file.getSize()));
+ sheetSet.put(new NodeProperty<>(
+ Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(),
Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(),
"",
file == null ? "" : StringUtils.defaultString(file.getMd5Hash())));
@@ -493,14 +609,15 @@ public class BlackboardArtifactNode extends AbstractContentNode(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.name"),
+ sheetSet.put(new NodeProperty<>(
+ NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.displayName"),
NO_DESCR,
size));
- sheetSet.put(new NodeProperty<>(
- NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.name"),
- NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.displayName"),
- NO_DESCR,
- path));
+ sheetSet
+ .put(new NodeProperty<>(
+ NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.name"),
+ NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.displayName"),
+ NO_DESCR,
+ path));
}
return sheet;
}
/**
- * Get all tags from the case database relating to the artifact and the file
- * it is associated with.
+ * Gets all of the tags applied to the artifact represented by this node and
+ * its source content.
*
- * @return a list of tags which on the artifact or the file it is associated
- * with
+ * @return The tags.
*/
@Override
protected final List getAllTagsFromDatabase() {
List tags = new ArrayList<>();
try {
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact));
- tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(associated));
+ tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(srcContent));
} catch (TskCoreException | NoCurrentCaseException ex) {
- logger.log(Level.SEVERE, "Failed to get tags for artifact " + artifact.getDisplayName(), ex);
+ logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and its source content (artifact objID={0})", artifact.getId()), ex);
}
return tags;
}
/**
- * Used by (subclasses of) BlackboardArtifactNode to add the tags property
- * to their sheets.
+ * Gets the correlation attribute for the MD5 hash of the source file of the
+ * artifact represented by this node. The correlation attribute instance can
+ * only be returned if the central repository is enabled and the source
+ * content is a file.
*
- * @param sheetSet the modifiable Sheet.Set returned by
- * Sheet.get(Sheet.PROPERTIES)
- */
- @NbBundle.Messages({
- "BlackboardArtifactNode.createSheet.tags.displayName=Tags"})
- @Deprecated
- protected void addTagProperty(Sheet.Set sheetSet) throws MissingResourceException {
- // add properties for tags
- List tags = new ArrayList<>();
- try {
- tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact));
- tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(associated));
- } catch (TskCoreException | NoCurrentCaseException ex) {
- logger.log(Level.SEVERE, "Failed to get tags for artifact " + artifact.getDisplayName(), ex);
- }
- sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(),
- NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
- }
-
- /**
- * Used by (subclasses of) BlackboardArtifactNode to add the tags property
- * to their sheets.
- *
- * @param sheetSet the modifiable Sheet.Set returned by
- * Sheet.get(Sheet.PROPERTIES)
- * @param tags the list of tags which should appear as the value for the
- * property
- */
- @Deprecated
- protected final void addTagProperty(Sheet.Set sheetSet, List tags) {
- sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(),
- NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
- }
-
- /**
- * Gets the correlation attribute for the associated file
- *
- * @return the correlation attribute for the file associated with this
- * BlackboardArtifactNode
+ * @return The correlation attribute instance, may be null.
*/
@Override
protected final CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance correlationAttribute = null;
- if (CentralRepository.isEnabled() && associated instanceof AbstractFile) {
- correlationAttribute = CorrelationAttributeUtil.getCorrAttrForFile((AbstractFile)associated);
+ if (CentralRepository.isEnabled() && srcContent instanceof AbstractFile) {
+ correlationAttribute = CorrelationAttributeUtil.getCorrAttrForFile((AbstractFile) srcContent);
}
return correlationAttribute;
}
/**
- * Used by (subclasses of) BlackboardArtifactNode to add the comment
- * property to their sheets.
+ * Computes the value of the comment property ("C" in S, C, O) for the
+ * artifact represented by this node.
*
- * @param sheetSet the modifiable Sheet.Set to add the property to
- * @param tags the list of tags associated with the file
- * @param attribute the correlation attribute associated with this
- * artifact's associated file, null if central repo is not
- * enabled
+ * An icon is displayed in the property sheet if a commented tag has been
+ * applied to the artifact or its source content, or if there is a
+ * corresponding commented correlation attribute instance in the central
+ * repository.
*
- * @deprecated Use the GetSCOTask to get this data on a background
- * thread..., and then update the property sheet asynchronously
- */
- @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
- "BlackboardArtifactNode.createSheet.comment.displayName=C"})
- @Deprecated
- protected final void addCommentProperty(Sheet.Set sheetSet, List tags, CorrelationAttributeInstance attribute) {
- HasCommentStatus status = getCommentProperty(tags, attribute);
- sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR,
- status));
- }
-
- /**
- * Gets the comment property for the node
+ * @param tags The tags applied to the artifact and its source content.
+ * @param attribute A correlation attribute instance Ffor the central
+ * repository lookup.
*
- * @param tags the list of tags associated with the file
- * @param attribute the correlation attribute associated with this
- * artifact's associated file, null if central repo is not
- * enabled
- *
- * @return comment property
+ * @return The value of the comment property.
*/
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) {
+ /*
+ * Has a tag with a comment been applied to the artifact or its source
+ * content?
+ */
HasCommentStatus status = tags.size() > 0 ? HasCommentStatus.TAG_NO_COMMENT : HasCommentStatus.NO_COMMENT;
for (Tag tag : tags) {
if (!StringUtils.isBlank(tag.getComment())) {
- //if the tag is null or empty or contains just white space it will indicate there is not a comment
status = HasCommentStatus.TAG_COMMENT;
break;
}
}
- //currently checks for a comment on the associated file in the central repo not the artifact itself
- //what we want the column property to reflect should be revisted when we have added a way to comment
- //on the artifact itself
+
+ /*
+ * Does the given correlation attribute instance have a comment in the
+ * central repository?
+ */
if (attribute != null && !StringUtils.isBlank(attribute.getComment())) {
if (status == HasCommentStatus.TAG_COMMENT) {
status = HasCommentStatus.CR_AND_TAG_COMMENTS;
@@ -665,53 +738,51 @@ public class BlackboardArtifactNode extends AbstractContentNode tags) {
- Pair scoreAndDescription = getScorePropertyAndDescription(tags);
- sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
- }
-
- /**
- * Get the score property for the node.
+ * A red icon will be displayed in the property sheet if the hash of the
+ * source file has been found in a notable hash set or if either the
+ * artifact or its source content has been tagged with a notable tag. A
+ * yellow icon will be displayed if the source file belongs to an
+ * interesting file set or either the artifact or its source content has
+ * been tagged with a non-notable tag.
*
- * @param tags the list of tags associated with the file
+ * @param tags The tags that have been applied to the artifact and its
+ * source content.
*
- * @return score property and description
+ * @return The value of the score property as an enum element and a
+ * description string for dislpay in a tool tip.
*/
@Override
protected Pair getScorePropertyAndDescription(List tags) {
+ /*
+ * Is the artifact's source content marked as notable?
+ */
Score score = Score.NO_SCORE;
String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
- if (associated instanceof AbstractFile) {
- if (((AbstractFile) associated).getKnown() == TskData.FileKnown.BAD) {
+ if (srcContent instanceof AbstractFile) {
+ if (((AbstractFile) srcContent).getKnown() == TskData.FileKnown.BAD) {
score = Score.NOTABLE_SCORE;
description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description();
}
}
- //if the artifact being viewed is a hashhit check if the hashset is notable
- if ((score == Score.NO_SCORE || score == Score.INTERESTING_SCORE) && content.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
+
+ /*
+ * If the artifact is a hash set hit, is the hash set a notable hashes
+ * hash set?
+ */
+ if (score == Score.NO_SCORE && artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
try {
- BlackboardAttribute attr = content.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME));
+ BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME));
List notableHashsets = HashDbManager.getInstance().getKnownBadFileHashSets();
for (HashDbManager.HashDb hashDb : notableHashsets) {
if (hashDb.getHashSetName().equals(attr.getValueString())) {
@@ -721,18 +792,29 @@ public class BlackboardArtifactNode extends AbstractContentNode 0 && (score == Score.NO_SCORE || score == Score.INTERESTING_SCORE)) {
score = Score.INTERESTING_SCORE;
description = Bundle.BlackboardArtifactNode_createSheet_taggedItem_description();
@@ -749,124 +831,117 @@ public class BlackboardArtifactNode extends AbstractContentNode countAndDescription = getCountPropertyAndDescription(attribute.getCorrelationType(), attribute.getCorrelationValue(), Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationAttributes_description());
- sheetSet.put(
- new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft()));
- }
-
- /**
- * Gets the Occurrences property for the node.
- *
- * @param attributeType the type of the attribute to count
- * @param attributeValue the value of the attribute to count
- * @param defaultDescription a description to use when none is determined by
- * the getCountPropertyAndDescription method
- *
- * @return count and description
+ * @return The value of the occurrences property as a data sources count and
+ * a description string.
*
*/
@Override
- protected Pair getCountPropertyAndDescription(Type attributeType, String attributeValue, String defaultDescription) {
+ protected Pair getCountPropertyAndDescription(Type corrAttrType, String attributeValue, String defaultDescription) {
Long count = -1L;
String description = defaultDescription;
try {
- //don't perform the query if there is no correlation value
- if (attributeType != null && StringUtils.isNotBlank(attributeValue)) {
- count = CentralRepository.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attributeType, attributeValue);
- description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, attributeType.getDisplayName());
- } else if (attributeType != null) {
+ if (corrAttrType != null && StringUtils.isNotBlank(attributeValue)) {
+ count = CentralRepository.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(corrAttrType, attributeValue);
+ description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, corrAttrType.getDisplayName());
+ } else if (corrAttrType != null) {
description = Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationValues_description();
}
} catch (CentralRepoException ex) {
- logger.log(Level.WARNING, "Error getting count of datasources with correlation attribute", ex);
+ logger.log(Level.SEVERE, MessageFormat.format("Error querying central repository for other occurences count (artifact objID={0}, corrAttrType={1}, corrAttrValue={2})", artifact.getId(), corrAttrType, attributeValue), ex);
} catch (CorrelationAttributeNormalizationException ex) {
- logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex);
+ logger.log(Level.SEVERE, MessageFormat.format("Error normalizing correlation attribute for central repository query (artifact objID={0}, corrAttrType={2}, corrAttrValue={3})", artifact.getId(), corrAttrType, attributeValue), ex);
}
return Pair.of(count, description);
}
+ /**
+ * Refreshes this node's property sheet.
+ */
private void updateSheet() {
this.setSheet(createSheet());
}
- private String getRootParentName() {
- String parentName = associated.getName();
- Content parent = associated;
+ /**
+ * Gets the name of the root ancestor of the source content for the artifact
+ * represented by this node.
+ *
+ * @return The root ancestor name or the empty string if an error occurs.
+ */
+ private String getRootAncestorName() {
+ String parentName = srcContent.getName();
+ Content parent = srcContent;
try {
while ((parent = parent.getParent()) != null) {
parentName = parent.getName();
}
} catch (TskCoreException ex) {
- logger.log(Level.WARNING, "Failed to get parent name from {0}", associated.getName()); //NON-NLS
+ logger.log(Level.SEVERE, MessageFormat.format("Error getting root ancestor name for source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
return "";
}
return parentName;
}
/**
- * Add an additional custom node property to that node before it is
- * displayed
+ * Adds a "custom" property to the property sheet of this node, indepoendent
+ * of the artifact this node represents or its source content.
*
- * @param np NodeProperty to add
+ * @param property The custom property.
*/
- public void addNodeProperty(NodeProperty> np) {
- if (null == customProperties) {
- //lazy create the list
+ public void addNodeProperty(NodeProperty> property) {
+ if (customProperties == null) {
customProperties = new ArrayList<>();
}
- customProperties.add(np);
+ customProperties.add(property);
}
/**
- * Fill map with Artifact properties
+ * Converts the attributes of the artifact this node represents to a map of
+ * name-value pairs, where the names are attribute type display names.
*
- * @param map map with preserved ordering, where property names/values
- * are put
- * @param artifact to extract properties from
+ * @param map The map to be populated with the artifact attribute
+ * name-value pairs.
+ * @param artifact The artifact.
*/
@SuppressWarnings("deprecation")
private void fillPropertyMap(Map map, BlackboardArtifact artifact) {
try {
for (BlackboardAttribute attribute : artifact.getAttributes()) {
final int attributeTypeID = attribute.getAttributeType().getTypeID();
- //skip some internal attributes that user shouldn't see
if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
- || attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
+ || attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
|| attribute.getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
- continue;
+ /*
+ * Do nothing.
+ */
} else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
addEmailMsgProperty(map, attribute);
} else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
- map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), associated));
+ map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), srcContent));
} else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID()
&& attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) {
/*
- * This was added because the RegRipper output would often
- * cause the UI to get a black line accross it and hang if
- * you hovered over large output or selected it. This
- * reduces the amount of data in the table. Could consider
- * doing this for all fields in the UI.
+ * The truncation of text attributes appears to have been
+ * motivated by the statement that "RegRipper output would
+ * often cause the UI to get a black line accross it and
+ * hang if you hovered over large output or selected it.
+ * This reduces the amount of data in the table. Could
+ * consider doing this for all fields in the UI."
*/
String value = attribute.getDisplayString();
if (value.length() > 512) {
@@ -878,43 +953,41 @@ public class BlackboardArtifactNode extends AbstractContentNode map, BlackboardAttribute attribute) {
-
final int attributeTypeID = attribute.getAttributeType().getTypeID();
-
- // Skip certain Email msg attributes
if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()) {
-
- // do nothing
+ /*
+ * Do nothing.
+ */
} else if (attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN.getTypeID()) {
-
String value = attribute.getDisplayString();
if (value.length() > 160) {
value = value.substring(0, 160) + "...";
}
map.put(attribute.getAttributeType().getDisplayName(), value);
} else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
- map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), associated));
+ map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), srcContent));
} else {
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
}
-
}
@Override
@@ -922,30 +995,6 @@ public class BlackboardArtifactNode extends AbstractContentNode artifact.getSleuthkitCase().getContentById(objectID));
- if (content == null) {
- return Lookups.fixed(artifact);
- } else {
- return Lookups.fixed(artifact, content);
- }
- } catch (ExecutionException ex) {
- logger.log(Level.WARNING, "Getting associated content for artifact failed", ex); //NON-NLS
- return Lookups.fixed(artifact);
- }
- }
-
@Override
public boolean isLeafTypeNode() {
return true;
@@ -960,4 +1009,117 @@ public class BlackboardArtifactNode extends AbstractContentNode T accept(ContentNodeVisitor visitor) {
return visitor.visit(this);
}
+
+ /**
+ * Adds the score property for the artifact represented by this node to the
+ * node property sheet.
+ *
+ * @param sheetSet The property sheet.
+ * @param tags The tags that have been applied to the artifact and its
+ * source content.
+ *
+ * @deprecated Do not use. The score property is now computed in a
+ * background thread and added to the property sheet via property change
+ * event.
+ */
+ @NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
+ "BlackboardArtifactNode.createSheet.score.displayName=S",
+ "BlackboardArtifactNode.createSheet.notableFile.description=Associated file recognized as notable.",
+ "BlackboardArtifactNode.createSheet.interestingResult.description=Result has an interesting result associated with it.",
+ "BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.",
+ "BlackboardArtifactNode.createSheet.notableTaggedItem.description=Result or associated file tagged with notable tag.",
+ "BlackboardArtifactNode.createSheet.noScore.description=No score"})
+ @Deprecated
+ protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List tags) {
+ Pair scoreAndDescription = getScorePropertyAndDescription(tags);
+ sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
+ }
+
+ /**
+ * Adds the tags property for the artifact represented by this node to the
+ * node property sheet.
+ *
+ * @param sheetSet The property sheet.
+ *
+ * @deprecated Do not use. The tags property is now computed in a background
+ * thread and added to the property sheet via property change event.
+ */
+ @NbBundle.Messages({
+ "BlackboardArtifactNode.createSheet.tags.displayName=Tags"}
+ )
+ @Deprecated
+ protected void addTagProperty(Sheet.Set sheetSet) throws MissingResourceException {
+ List tags = new ArrayList<>();
+ try {
+ tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact));
+ tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(srcContent));
+ } catch (TskCoreException | NoCurrentCaseException ex) {
+ logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and source content (artifact objID={0})", artifact.getId()), ex);
+ }
+ sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
+ }
+
+ /**
+ * Adds the tags property for the artifact represented by this node to the
+ * node property sheet.
+ *
+ * @param sheetSet The property sheet.
+ * @param tags The tags that have been applied to the artifact and its
+ * source content.
+ *
+ * @deprecated Do not use. The tags property is now computed in a background
+ * thread and added to the property sheet via property change event.
+ */
+ @Deprecated
+ protected final void addTagProperty(Sheet.Set sheetSet, List tags) {
+ sheetSet.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
+ }
+
+ /**
+ * Adds the count property for the artifact represented by this node to the
+ * node property sheet.
+ *
+ * @param sheetSet The property sheet.
+ * @param attribute The correlation attribute instance to use for the
+ * central repository lookup.
+ *
+ * @deprecated Do not use. The count property is now computed in a
+ * background thread and added to the property sheet via property change
+ * event.
+ */
+ @NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
+ "BlackboardArtifactNode.createSheet.count.displayName=O",
+ "BlackboardArtifactNode.createSheet.count.noCorrelationAttributes.description=No correlation properties found",
+ "BlackboardArtifactNode.createSheet.count.noCorrelationValues.description=Unable to find other occurrences because no value exists for the available correlation property",
+ "# {0} - occurrenceCount",
+ "# {1} - attributeType",
+ "BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}"})
+ @Deprecated
+ protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
+ Pair countAndDescription = getCountPropertyAndDescription(attribute.getCorrelationType(), attribute.getCorrelationValue(), Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationAttributes_description());
+ sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft()));
+ }
+
+ /**
+ * Adds the other occurrences property for the artifact represented by this
+ * node to the node property sheet.
+ *
+ * @param sheetSet The property sheet.
+ * @param tags The tags that have been applied to the artifact and its
+ * source content.
+ * @param attribute The correlation attribute instance to use for the
+ * central repository lookup.
+ *
+ * @deprecated Do not use. The other occurrences property is now computed in
+ * a background thread and added to the property sheet via property change
+ * event.
+ */
+ @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
+ "BlackboardArtifactNode.createSheet.comment.displayName=C"})
+ @Deprecated
+ protected final void addCommentProperty(Sheet.Set sheetSet, List tags, CorrelationAttributeInstance attribute) {
+ HasCommentStatus status = getCommentProperty(tags, attribute);
+ sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, status));
+ }
+
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactTagNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactTagNode.java
index 16cf40680c..6068615592 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactTagNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactTagNode.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2013-2018 Basis Technology Corp.
+ * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,13 +24,11 @@ import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import javax.swing.Action;
-import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.coreutils.Logger;
-import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
import org.sleuthkit.datamodel.AbstractFile;
@@ -46,14 +44,14 @@ import static org.sleuthkit.autopsy.datamodel.Bundle.*;
* tag name nodes have tag type child nodes; tag type nodes are the parents of
* either content or blackboard artifact tag nodes.
*/
-public class BlackboardArtifactTagNode extends DisplayableItemNode {
+public class BlackboardArtifactTagNode extends TagNode {
private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactTagNode.class.getName());
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/green-tag-icon-16.png"; //NON-NLS
private final BlackboardArtifactTag tag;
public BlackboardArtifactTagNode(BlackboardArtifactTag tag) {
- super(Children.LEAF, Lookups.fixed(tag, tag.getArtifact(), tag.getContent()));
+ super(Lookups.fixed(tag, tag.getArtifact(), tag.getContent()), tag.getContent());
super.setName(tag.getContent().getName());
super.setDisplayName(tag.getContent().getName());
this.setIconBaseWithExtension(ICON_PATH);
@@ -75,6 +73,7 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFile.text"),
"",
tag.getContent().getName()));
+ addOriginalNameProp(properties);
String contentPath;
try {
contentPath = tag.getContent().getUniquePath();
@@ -82,7 +81,6 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
Logger.getLogger(ContentTagNode.class.getName()).log(Level.SEVERE, "Failed to get path for content (id = " + tag.getContent().getId() + ")", ex); //NON-NLS
contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text");
}
-
properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
@@ -119,7 +117,6 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, MessageFormat.format("Error getting arttribute(s) from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS
- MessageNotifyUtil.Notify.error(Bundle.BlackboardArtifactNode_getAction_errorTitle(), Bundle.BlackboardArtifactNode_getAction_resultErrorMessage());
}
// if the artifact links to another file, add an action to go to that file
@@ -130,7 +127,6 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, MessageFormat.format("Error getting linked file from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS
- MessageNotifyUtil.Notify.error(Bundle.BlackboardArtifactNode_getAction_errorTitle(), Bundle.BlackboardArtifactNode_getAction_linkedFileMessage());
}
//if this artifact has associated content, add the action to view the content in the timeline
AbstractFile file = getLookup().lookup(AbstractFile.class);
@@ -148,11 +144,6 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
return visitor.visit(this);
}
- @Override
- public boolean isLeafTypeNode() {
- return true;
- }
-
@Override
public String getItemType() {
return getClass().getName();
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties
index 3fa8cd0341..83225be1c2 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties
@@ -11,8 +11,6 @@ ArtifactTypeNode.createSheet.childCnt.name=Child Count
ArtifactTypeNode.createSheet.childCnt.displayName=Child Count
ArtifactTypeNode.createSheet.childCnt.desc=no description
BlackboardArtifactNode.noDesc.text=no description
-BlackboardArtifactNode.createSheet.srcFile.name=Source File
-BlackboardArtifactNode.createSheet.srcFile.displayName=Source File
BlackboardArtifactNode.createSheet.ext.name=Extension
BlackboardArtifactNode.createSheet.ext.displayName=Extension
BlackboardArtifactNode.createSheet.mimeType.name=MIME Type
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED
index 9ac85bd1e7..e7310882a3 100755
--- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED
@@ -74,13 +74,12 @@ BlackboardArtifactNode.createSheet.path.displayName=Path
BlackboardArtifactNode.createSheet.path.name=Path
BlackboardArtifactNode.createSheet.score.displayName=S
BlackboardArtifactNode.createSheet.score.name=S
+BlackboardArtifactNode.createSheet.srcFile.displayName=Source File
+BlackboardArtifactNode.createSheet.srcFile.name=Source File
+BlackboardArtifactNode.createSheet.srcFile.origDisplayName=Original Name
+BlackboardArtifactNode.createSheet.srcFile.origName=Original Name
BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.
BlackboardArtifactNode.createSheet.tags.displayName=Tags
-# {0} - artifactDisplayName
-BlackboardArtifactNode.displayName.artifact={0} Artifact
-BlackboardArtifactNode.getAction.errorTitle=Error getting actions
-BlackboardArtifactNode.getAction.linkedFileMessage=There was a problem getting actions for the selected result. The 'View File in Timeline' action will not be available.
-BlackboardArtifactNode.getAction.resultErrorMessage=There was a problem getting actions for the selected result. The 'View Result in Timeline' action will not be available.
BlackboardArtifactTagNode.createSheet.userName.text=User Name
BlackboardArtifactTagNode.viewSourceArtifact.text=View Source Result
Category.five=CAT-5: Non-pertinent
@@ -91,6 +90,7 @@ Category.two=CAT-2: Child Exploitation (Non-Illegal/Age Difficult)
Category.zero=CAT-0: Uncategorized
ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash
ContentTagNode.createSheet.artifactMD5.name=MD5 Hash
+ContentTagNode.createSheet.origFileName=Original Name
ContentTagNode.createSheet.userName.text=User Name
DeletedContent.allDelFilter.text=All
DeletedContent.createSheet.filterType.desc=no description
@@ -178,8 +178,6 @@ ArtifactTypeNode.createSheet.childCnt.name=Child Count
ArtifactTypeNode.createSheet.childCnt.displayName=Child Count
ArtifactTypeNode.createSheet.childCnt.desc=no description
BlackboardArtifactNode.noDesc.text=no description
-BlackboardArtifactNode.createSheet.srcFile.name=Source File
-BlackboardArtifactNode.createSheet.srcFile.displayName=Source File
BlackboardArtifactNode.createSheet.ext.name=Extension
BlackboardArtifactNode.createSheet.ext.displayName=Extension
BlackboardArtifactNode.createSheet.mimeType.name=MIME Type
@@ -348,6 +346,8 @@ TagNameNode.bbArtTagTypeNodeKey.text=Result Tags
TagNameNode.bookmark.text=Bookmark
TagNameNode.createSheet.name.name=Name
TagNameNode.createSheet.name.displayName=Name
+TagNode.propertySheet.origName=Original Name
+TagNode.propertySheet.origNameDisplayName=Original Name
TagsNode.displayName.text=Tags
TagsNode.createSheet.name.name=Name
TagsNode.createSheet.name.displayName=Name
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ContentTagNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ContentTagNode.java
index e213e460cb..89d6c2fae2 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/ContentTagNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/ContentTagNode.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2013-2016 Basis Technology Corp.
+ * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,6 @@ import java.util.List;
import java.util.logging.Level;
import javax.swing.Action;
import org.apache.commons.lang3.StringUtils;
-import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
@@ -39,18 +38,16 @@ import org.sleuthkit.datamodel.TskCoreException;
/**
* Instances of this class wrap ContentTag objects. In the Autopsy presentation
* of the SleuthKit data model, they are leaf nodes of a tree consisting of
- * content and blackboard artifact tags, grouped first by tag type, then by tag
- * name.
+ * content and artifact tags, grouped first by tag type, then by tag name.
*/
-class ContentTagNode extends DisplayableItemNode {
+class ContentTagNode extends TagNode {
private static final Logger LOGGER = Logger.getLogger(ContentTagNode.class.getName());
-
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/blue-tag-icon-16.png"; //NON-NLS
private final ContentTag tag;
- public ContentTagNode(ContentTag tag) {
- super(Children.LEAF, Lookups.fixed(tag, tag.getContent()));
+ ContentTagNode(ContentTag tag) {
+ super(Lookups.fixed(tag, tag.getContent()), tag.getContent());
super.setName(tag.getContent().getName());
super.setDisplayName(tag.getContent().getName());
this.setIconBaseWithExtension(ICON_PATH);
@@ -58,6 +55,7 @@ class ContentTagNode extends DisplayableItemNode {
}
@Messages({
+ "ContentTagNode.createSheet.origFileName=Original Name",
"ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash",
"ContentTagNode.createSheet.artifactMD5.name=MD5 Hash",
"ContentTagNode.createSheet.userName.text=User Name"})
@@ -79,15 +77,19 @@ class ContentTagNode extends DisplayableItemNode {
properties = Sheet.createPropertiesSet();
propertySheet.put(properties);
}
- properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.file.name"),
+ properties.put(new NodeProperty<>(
+ NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.file.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.file.displayName"),
"",
content.getName()));
- properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.filePath.name"),
+ addOriginalNameProp(properties);
+ properties.put(new NodeProperty<>(
+ NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.filePath.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.filePath.displayName"),
"",
contentPath));
- properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.comment.name"),
+ properties.put(new NodeProperty<>(
+ NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.comment.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.comment.displayName"),
"",
tag.getComment()));
@@ -95,23 +97,28 @@ class ContentTagNode extends DisplayableItemNode {
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.displayName"),
"",
file != null ? ContentUtils.getStringTime(file.getMtime(), file) : ""));
- properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.name"),
+ properties.put(new NodeProperty<>(
+ NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.displayName"),
"",
file != null ? ContentUtils.getStringTime(file.getCtime(), file) : ""));
- properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.name"),
+ properties.put(new NodeProperty<>(
+ NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.displayName"),
"",
file != null ? ContentUtils.getStringTime(file.getAtime(), file) : ""));
- properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.name"),
+ properties.put(new NodeProperty<>(
+ NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.displayName"),
"",
file != null ? ContentUtils.getStringTime(file.getCrtime(), file) : ""));
- properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.name"),
+ properties.put(new NodeProperty<>(
+ NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.displayName"),
"",
content.getSize()));
- properties.put(new NodeProperty<>(Bundle.ContentTagNode_createSheet_artifactMD5_name(),
+ properties.put(new NodeProperty<>(
+ Bundle.ContentTagNode_createSheet_artifactMD5_name(),
Bundle.ContentTagNode_createSheet_artifactMD5_displayName(),
"",
file != null ? StringUtils.defaultString(file.getMd5Hash()) : ""));
@@ -128,8 +135,7 @@ class ContentTagNode extends DisplayableItemNode {
List actions = new ArrayList<>();
actions.addAll(Arrays.asList(super.getActions(context)));
- AbstractFile file = getLookup().lookup(AbstractFile.class
- );
+ AbstractFile file = getLookup().lookup(AbstractFile.class);
if (file != null) {
actions.add(ViewFileInTimelineAction.createViewFileAction(file));
}
@@ -144,13 +150,9 @@ class ContentTagNode extends DisplayableItemNode {
return visitor.visit(this);
}
- @Override
- public boolean isLeafTypeNode() {
- return true;
- }
-
@Override
public String getItemType() {
return getClass().getName();
}
+
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNode.java
index c723d99b55..c6bc88129a 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNode.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2011-2017 Basis Technology Corp.
+ * Copyright 2012-2020 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datamodel;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
+import org.openide.nodes.Sheet;
import org.openide.util.Lookup;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
@@ -142,4 +143,26 @@ public abstract class DisplayableItemNode extends AbstractNode {
return selectedChildNodeInfo;
}
+ /**
+ * Updates the node property sheet by replacing existing properties with new
+ * properties with the same property name.
+ *
+ * @param newProps The replacement property objects.
+ */
+ protected synchronized final void updatePropertySheet(NodeProperty>... newProps) {
+ Sheet currentSheet = this.getSheet();
+ Sheet.Set currentPropsSet = currentSheet.get(Sheet.PROPERTIES);
+ Property>[] currentProps = currentPropsSet.getProperties();
+ for (NodeProperty> newProp : newProps) {
+ for (int i = 0; i < currentProps.length; i++) {
+ if (currentProps[i].getName().equals(newProp.getName())) {
+ currentProps[i] = newProp;
+ }
+ }
+ }
+ currentPropsSet.put(currentProps);
+ currentSheet.put(currentPropsSet);
+ this.setSheet(currentSheet);
+ }
+
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/TagNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/TagNode.java
new file mode 100755
index 0000000000..9653c44d04
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/TagNode.java
@@ -0,0 +1,128 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2020-2020 Basis Technology Corp.
+ * Contact: carrier sleuthkit 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 java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import org.openide.nodes.Children;
+import org.openide.nodes.Sheet;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.sleuthkit.autopsy.core.UserPreferences;
+import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask;
+import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
+import org.sleuthkit.datamodel.Content;
+
+/**
+ * An abstract superclass for a node that represents a tag, uses the name of a
+ * given Content object as its display name, and has a property sheet with an
+ * original name property when machine translation is enabled.
+ *
+ * The translation of the Content name is done in a background thread. The
+ * translated name is made the display name of the node and the untranslated
+ * name is put into both the original name property and into the node's tooltip.
+ *
+ * TODO (Jira-6174): Consider modifying this class to be able to use it more broadly
+ * within the Autopsy data model (i.e., AbstractNode suclasses). It's not really
+ * specific to a tag node.
+ */
+@NbBundle.Messages({
+ "TagNode.propertySheet.origName=Original Name",
+ "TagNode.propertySheet.origNameDisplayName=Original Name"
+})
+abstract class TagNode extends DisplayableItemNode {
+
+ private final static String ORIG_NAME_PROP_NAME = Bundle.TagNode_propertySheet_origName();
+ private final static String ORIG_NAME_PROP_DISPLAY_NAME = Bundle.TagNode_propertySheet_origNameDisplayName();
+
+ private final String originalName;
+ private volatile String translatedName;
+
+ /**
+ * An abstract superclass for a node that represents a tag, uses the name of
+ * a given Content object as its display name, and has a property sheet with
+ * an untranslated file name property when machine translation is enabled.
+ *
+ * @param lookup The Lookup of the node.
+ * @param content The Content to use for the node display name.
+ */
+ TagNode(Lookup lookup, Content content) {
+ super(Children.LEAF, lookup);
+ originalName = content.getName();
+ }
+
+ @Override
+ public boolean isLeafTypeNode() {
+ return true;
+ }
+
+ @Override
+ abstract public String getItemType();
+
+ @Override
+ abstract public T accept(DisplayableItemNodeVisitor visitor);
+
+ /**
+ * Adds an original name property to the node's property sheet and submits
+ * an original name translation task.
+ *
+ * The translation of the original name is done in a background thread. The
+ * translated name is made the display name of the node and the untranslated
+ * name is put into both the original name property and into the node's
+ * tooltip.
+ *
+ * @param properties The node's property sheet.
+ */
+ protected void addOriginalNameProp(Sheet.Set properties) {
+ if (TextTranslationService.getInstance().hasProvider() && UserPreferences.displayTranslatedFileNames()) {
+ properties.put(new NodeProperty<>(
+ ORIG_NAME_PROP_NAME,
+ ORIG_NAME_PROP_DISPLAY_NAME,
+ "",
+ translatedName != null ? originalName : ""));
+ if (translatedName == null) {
+ new FileNameTransTask(originalName, this, new NameTranslationListener()).submit();
+ }
+ }
+ }
+
+ /**
+ * A listener for PropertyChangeEvents from a background task used to
+ * translate the original display name associated with the node.
+ */
+ private class NameTranslationListener implements PropertyChangeListener {
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ String eventType = evt.getPropertyName();
+ if (eventType.equals(FileNameTransTask.getPropertyName())) {
+ translatedName = evt.getNewValue().toString();
+ String originalName = evt.getOldValue().toString();
+ setDisplayName(translatedName);
+ setShortDescription(originalName);
+ updatePropertySheet(new NodeProperty<>(
+ ORIG_NAME_PROP_NAME,
+ ORIG_NAME_PROP_DISPLAY_NAME,
+ "",
+ originalName));
+ }
+ }
+ }
+
+}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/utils/AbstractNodePropertySheetTask.java b/Core/src/org/sleuthkit/autopsy/datamodel/utils/AbstractNodePropertySheetTask.java
index 368b09b142..0a7e0a6f3f 100755
--- a/Core/src/org/sleuthkit/autopsy/datamodel/utils/AbstractNodePropertySheetTask.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/utils/AbstractNodePropertySheetTask.java
@@ -64,7 +64,7 @@ public abstract class AbstractNodePropertySheetTask impl
* @return The Future of the task, may be used for task cancellation by
* calling Future.cancel(true).
*/
- public static Future> submitTask(AbstractNodePropertySheetTask> task) {
+ private static Future> submitTask(AbstractNodePropertySheetTask> task) {
return executor.submit(task);
}
@@ -104,12 +104,22 @@ public abstract class AbstractNodePropertySheetTask impl
*
* @param node The AbstractNode.
*
- * @return The result of the computation as a PropertyChangeEvent.
+ * @return The result of the computation as a PropertyChangeEvent, may be
+ * null.
*/
protected abstract PropertyChangeEvent computePropertyValue(T node) throws Exception;
+ /**
+ * Submits this task to the ExecutorService for the thread pool.
+ *
+ * @return The task's Future from the ExecutorService.
+ */
+ public final Future> submit() {
+ return submitTask(this);
+ }
+
@Override
- final public void run() {
+ public final void run() {
try {
T node = this.weakNodeRef.get();
PropertyChangeListener listener = this.weakListenerRef.get();
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/utils/FileNameTransTask.java b/Core/src/org/sleuthkit/autopsy/datamodel/utils/FileNameTransTask.java
new file mode 100755
index 0000000000..8b755ec3dc
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/utils/FileNameTransTask.java
@@ -0,0 +1,61 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2020 Basis Technology Corp.
+ * Contact: carrier sleuthkit 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.utils;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import org.openide.nodes.AbstractNode;
+import org.sleuthkit.autopsy.texttranslation.utils.FileNameTranslationUtil;
+
+/**
+ * An AbstractNodePropertySheetTask that translates a file name for an
+ * AbstractNode's property sheet.
+ */
+public class FileNameTransTask extends AbstractNodePropertySheetTask {
+
+ private final static String EVENT_SOURCE = FileNameTransTask.class.getName();
+ private final static String PROPERTY_NAME = EVENT_SOURCE + ".TranslatedFileName";
+ private final String fileName;
+
+ public static String getPropertyName() {
+ return PROPERTY_NAME;
+ }
+
+ /**
+ * Constructs an AbstractNodePropertySheetTask that translates a file name
+ * for an AbstractNode's property sheet. When the translation is complete, a
+ * PropertyChangeEvent will be fired to the node's PropertyChangeListener.
+ * Call getPropertyName() to identify the property.
+ *
+ * @param node The node.
+ * @param listener The node's PropertyChangeListener.
+ * @param fileName THe file name.
+ */
+ public FileNameTransTask(String fileName, AbstractNode node, PropertyChangeListener listener) {
+ super(node, listener);
+ this.fileName = fileName;
+ }
+
+ @Override
+ protected PropertyChangeEvent computePropertyValue(AbstractNode node) throws Exception {
+ String translatedFileName = FileNameTranslationUtil.translate(fileName);
+ return translatedFileName.isEmpty() ? null : new PropertyChangeEvent(EVENT_SOURCE, PROPERTY_NAME, fileName, translatedFileName);
+ }
+
+}
diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java
index 522d3836c3..a412bb5970 100644
--- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java
+++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2011-2019 Basis Technology Corp.
+ * Copyright 2012-2020 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -187,27 +187,6 @@ public class DataResultFilterNode extends FilterNode {
return propertySets;
}
- /**
- * Gets the display name for the wrapped node.
- *
- * OutlineView used in the DataResult table uses getDisplayName() to
- * populate the first column, which is Source File.
- *
- * Hence this override to return the 'correct' displayName for the wrapped
- * node.
- *
- * @return The display name for the node.
- */
- @Override
- public String getDisplayName() {
- final Node orig = getOriginal();
- String name = orig.getDisplayName();
- if ((orig instanceof BlackboardArtifactNode)) {
- name = ((BlackboardArtifactNode) orig).getSourceName();
- }
- return name;
- }
-
/**
* Adds information about which child node of this node, if any, should be
* selected. Can be null.
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED
index ea9e846568..50086b7c9b 100644
--- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED
+++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED
@@ -16,6 +16,10 @@ DiscoveryUiUtility.sizeLabel.text=Size: {0} {1}
DiscoveryUiUtility.terraBytes.text=TB
# {0} - otherInstanceCount
DocumentPanel.nameLabel.more.text=\ and {0} more
+DocumentPanel.noImageExtraction.text=0 of ? images
+DocumentPanel.numberOfImages.noImages=No images
+# {0} - numberOfImages
+DocumentPanel.numberOfImages.text=1 of {0} images
DocumentWrapper.previewInitialValue=Preview not generated yet.
FileGroup.groupSortingAlgorithm.groupName.text=Group Name
FileGroup.groupSortingAlgorithm.groupSize.text=Group Size
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryUiUtils.java b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryUiUtils.java
index fc2752a659..c9ad380a32 100644
--- a/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryUiUtils.java
+++ b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryUiUtils.java
@@ -35,9 +35,11 @@ final class DiscoveryUiUtils {
private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png";
private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png";
private static final String DELETE_ICON_PATH = "/org/sleuthkit/autopsy/images/file-icon-deleted.png";
+ private static final String UNSUPPORTED_DOC_PATH = "/org/sleuthkit/autopsy/images/image-extraction-not-supported.png";
private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false));
private static final ImageIcon NOTABLE_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false));
private static final ImageIcon DELETED_ICON = new ImageIcon(ImageUtilities.loadImage(DELETE_ICON_PATH, false));
+ private static final ImageIcon UNSUPPORTED_DOCUMENT_THUMBNAIL = new ImageIcon(ImageUtilities.loadImage(UNSUPPORTED_DOC_PATH, false));
@NbBundle.Messages({"# {0} - fileSize",
"# {1} - units",
@@ -83,6 +85,16 @@ final class DiscoveryUiUtils {
return Bundle.DiscoveryUiUtility_sizeLabel_text(size, units);
}
+ /**
+ * Get the image to use when the document type does not support image
+ * extraction.
+ *
+ * @return An image that indicates we don't know if there are images.
+ */
+ static ImageIcon getUnsupportedImageThumbnail() {
+ return UNSUPPORTED_DOCUMENT_THUMBNAIL;
+ }
+
/**
* Helper method to see if point is on the icon.
*
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/DocumentPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/DocumentPanel.form
index 05ed6fc0c8..beacd1f271 100644
--- a/Core/src/org/sleuthkit/autopsy/filequery/DocumentPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/filequery/DocumentPanel.form
@@ -24,19 +24,28 @@
-
+
-
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -44,9 +53,15 @@
-
+
+
+
+
-
+
+
+
+
@@ -104,36 +119,39 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/DocumentPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/DocumentPanel.java
index 849d3843ec..87919cab87 100644
--- a/Core/src/org/sleuthkit/autopsy/filequery/DocumentPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/filequery/DocumentPanel.java
@@ -23,10 +23,12 @@ import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseEvent;
+import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle.Messages;
+import org.sleuthkit.autopsy.corecomponents.AutoWrappingJTextPane;
/**
* Class which displays a preview and details about a document.
@@ -56,8 +58,9 @@ public class DocumentPanel extends javax.swing.JPanel implements ListCellRendere
scoreLabel = new javax.swing.JLabel();
fileSizeLabel = new javax.swing.JLabel();
nameLabel = new javax.swing.JLabel();
- javax.swing.JScrollPane previewScrollPane = new javax.swing.JScrollPane();
- previewTextArea = new javax.swing.JTextArea();
+ sampleImageLabel = new javax.swing.JLabel();
+ numberOfImagesLabel = new javax.swing.JLabel();
+ previewTextPane = new AutoWrappingJTextPane();
setBorder(javax.swing.BorderFactory.createEtchedBorder());
@@ -75,18 +78,14 @@ public class DocumentPanel extends javax.swing.JPanel implements ListCellRendere
fileSizeLabel.setToolTipText(org.openide.util.NbBundle.getMessage(DocumentPanel.class, "DocumentPanel.fileSizeLabel.toolTipText")); // NOI18N
- previewScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
+ sampleImageLabel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
+ sampleImageLabel.setIconTextGap(0);
+ sampleImageLabel.setMaximumSize(new java.awt.Dimension(100, 100));
+ sampleImageLabel.setMinimumSize(new java.awt.Dimension(100, 100));
+ sampleImageLabel.setPreferredSize(new java.awt.Dimension(100, 100));
- previewTextArea.setEditable(false);
- previewTextArea.setColumns(20);
- previewTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N
- previewTextArea.setLineWrap(true);
- previewTextArea.setRows(5);
- previewTextArea.setWrapStyleWord(true);
- previewTextArea.setEnabled(false);
- previewTextArea.setFocusable(false);
- previewTextArea.setMaximumSize(new java.awt.Dimension(164, 94));
- previewScrollPane.setViewportView(previewTextArea);
+ previewTextPane.setEditable(false);
+ previewTextPane.setBorder(javax.swing.BorderFactory.createEtchedBorder());
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
@@ -97,21 +96,31 @@ public class DocumentPanel extends javax.swing.JPanel implements ListCellRendere
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(fileSizeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(isDeletedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(scoreLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addComponent(previewScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 649, Short.MAX_VALUE)
- .addComponent(nameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(nameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 586, Short.MAX_VALUE)
+ .addComponent(previewTextPane, javax.swing.GroupLayout.DEFAULT_SIZE, 586, Short.MAX_VALUE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(numberOfImagesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(sampleImageLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
- .addComponent(nameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(numberOfImagesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 17, Short.MAX_VALUE)
+ .addComponent(nameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(previewScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(sampleImageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(previewTextPane, javax.swing.GroupLayout.PREFERRED_SIZE, 98, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scoreLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
@@ -126,12 +135,20 @@ public class DocumentPanel extends javax.swing.JPanel implements ListCellRendere
private javax.swing.JLabel fileSizeLabel;
private javax.swing.JLabel isDeletedLabel;
private javax.swing.JLabel nameLabel;
- private javax.swing.JTextArea previewTextArea;
+ private javax.swing.JLabel numberOfImagesLabel;
+ private javax.swing.JTextPane previewTextPane;
+ private javax.swing.JLabel sampleImageLabel;
private javax.swing.JLabel scoreLabel;
// End of variables declaration//GEN-END:variables
- @Messages({"# {0} - otherInstanceCount",
- "DocumentPanel.nameLabel.more.text= and {0} more"})
+ @Messages({
+ "# {0} - otherInstanceCount",
+ "DocumentPanel.nameLabel.more.text= and {0} more",
+ "# {0} - numberOfImages",
+ "DocumentPanel.numberOfImages.text=1 of {0} images",
+ "DocumentPanel.numberOfImages.noImages=No images",
+ "DocumentPanel.noImageExtraction.text=0 of ? images"})
+
@Override
public Component getListCellRendererComponent(JList extends DocumentWrapper> list, DocumentWrapper value, int index, boolean isSelected, boolean cellHasFocus) {
fileSizeLabel.setText(DiscoveryUiUtils.getFileSizeString(value.getResultFile().getFirstInstance().getSize()));
@@ -139,9 +156,19 @@ public class DocumentPanel extends javax.swing.JPanel implements ListCellRendere
if (value.getResultFile().getAllInstances().size() > 1) {
nameText += Bundle.DocumentPanel_nameLabel_more_text(value.getResultFile().getAllInstances().size() - 1);
}
+ if (value.getSummary().getNumberOfImages() > 0) {
+ numberOfImagesLabel.setText(Bundle.DocumentPanel_numberOfImages_text(value.getSummary().getNumberOfImages()));
+ sampleImageLabel.setIcon(new ImageIcon(value.getSummary().getSampleImage()));
+ } else if (FileSearchData.getDocTypesWithoutImageExtraction().contains(value.getResultFile().getFirstInstance().getMIMEType())) {
+ numberOfImagesLabel.setText(Bundle.DocumentPanel_noImageExtraction_text());
+ sampleImageLabel.setIcon(DiscoveryUiUtils.getUnsupportedImageThumbnail());
+ } else {
+ numberOfImagesLabel.setText(Bundle.DocumentPanel_numberOfImages_noImages());
+ sampleImageLabel.setIcon(null);
+ }
nameLabel.setText(nameText);
- previewTextArea.setText(value.getPreview());
- previewTextArea.setCaretPosition(0);
+ previewTextPane.setText(value.getSummary().getSummaryText());
+ previewTextPane.setCaretPosition(0);
DiscoveryUiUtils.setDeletedIcon(value.getResultFile().isDeleted(), isDeletedLabel);
DiscoveryUiUtils.setScoreIcon(value.getResultFile(), scoreLabel);
setBackground(isSelected ? SELECTION_COLOR : list.getBackground());
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/DocumentWrapper.java b/Core/src/org/sleuthkit/autopsy/filequery/DocumentWrapper.java
index a15e151673..9bf3b173ff 100644
--- a/Core/src/org/sleuthkit/autopsy/filequery/DocumentWrapper.java
+++ b/Core/src/org/sleuthkit/autopsy/filequery/DocumentWrapper.java
@@ -19,55 +19,57 @@
package org.sleuthkit.autopsy.filequery;
import org.openide.util.NbBundle.Messages;
+import org.sleuthkit.autopsy.textsummarizer.TextSummary;
/**
- * Class to wrap all the information necessary for a document preview to be
+ * Class to wrap all the information necessary for a document summary to be
* displayed.
*/
public class DocumentWrapper {
- private String preview;
+ private TextSummary summary;
private final ResultFile resultFile;
/**
* Construct a new DocumentWrapper.
*
* @param file The ResultFile which represents the document which the
- * preview summary is created for.
+ * summary is created for.
*/
@Messages({"DocumentWrapper.previewInitialValue=Preview not generated yet."})
DocumentWrapper(ResultFile file) {
- this.preview = Bundle.DocumentWrapper_previewInitialValue();
+ this.summary = new TextSummary(Bundle.DocumentWrapper_previewInitialValue(), null, 0);
this.resultFile = file;
}
/**
- * Set the preview summary which exists.
+ * Set the summary which exists.
*
- * @param preview The String which should be displayed as a preview for this
- * document.
+ * @param textSummary The TextSummary object which contains the text and
+ * image which should be displayed as a summary for this
+ * document.
*/
- void setPreview(String preview) {
- this.preview = preview;
+ void setSummary(TextSummary textSummary) {
+ this.summary = textSummary;
}
/**
- * Get the ResultFile which represents the document the preview summary was
- * created for.
+ * Get the ResultFile which represents the document the summary was created
+ * for.
*
* @return The ResultFile which represents the document file which the
- * preview was created for.
+ * summary was created for.
*/
ResultFile getResultFile() {
return resultFile;
}
/**
- * Get the preview summary of the document.
+ * Get the summary of the document.
*
- * @return The String which is the preview of the document.
+ * @return The TextSummary which is the summary of the document.
*/
- String getPreview() {
- return preview;
+ TextSummary getSummary() {
+ return summary;
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java
index daa4124e8d..5832625b73 100644
--- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java
+++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java
@@ -23,7 +23,6 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.io.Files;
import java.awt.Image;
import java.awt.image.BufferedImage;
-import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Paths;
@@ -95,6 +94,7 @@ class FileSearch {
.build();
private static final int PREVIEW_SIZE = 256;
private static volatile TextSummarizer summarizerToUse = null;
+ private static final BufferedImage VIDEO_DEFAULT_IMAGE = getDefaultVideoThumbnail();
/**
* Run the file search and returns the SearchResults object for debugging.
@@ -280,11 +280,35 @@ class FileSearch {
}
if (summary == null || StringUtils.isBlank(summary.getSummaryText())) {
//summary text was empty grab the beginning of the file
- summary = new TextSummary(getFirstLines(file), null, 0);
+ summary = getDefaultSummary(file);
}
return summary;
}
+ private static TextSummary getDefaultSummary(AbstractFile file) {
+ Image image = null;
+ int countOfImages = 0;
+ try {
+ Content largestChild = null;
+ for (Content child : file.getChildren()) {
+ if (child instanceof AbstractFile && ImageUtils.isImageThumbnailSupported((AbstractFile) child)) {
+ countOfImages++;
+ if (largestChild == null || child.getSize() > largestChild.getSize()) {
+ largestChild = child;
+ }
+ }
+ }
+ if (largestChild != null) {
+ image = ImageUtils.getThumbnail(largestChild, ImageUtils.ICON_SIZE_LARGE);
+ }
+ } catch (TskCoreException ex) {
+ logger.log(Level.WARNING, "Error getting children for file: " + file.getId(), ex);
+ }
+ image = image == null ? image : image.getScaledInstance(ImageUtils.ICON_SIZE_MEDIUM, ImageUtils.ICON_SIZE_MEDIUM,
+ Image.SCALE_SMOOTH);
+ return new TextSummary(getFirstLines(file), image, countOfImages);
+ }
+
/**
* Get the beginning of text from the specified AbstractFile.
*
@@ -456,6 +480,20 @@ class FileSearch {
+ "AND blackboard_artifacts.obj_id IN (" + objIdList + ") "; // NON-NLS
}
+ /**
+ * Get the default image to display when a thumbnail is not available.
+ *
+ * @return The default video thumbnail.
+ */
+ private static BufferedImage getDefaultVideoThumbnail() {
+ try {
+ return ImageIO.read(ImageUtils.class.getResourceAsStream("/org/sleuthkit/autopsy/images/failedToCreateVideoThumb.png"));//NON-NLS
+ } catch (IOException ex) {
+ logger.log(Level.SEVERE, "Failed to load 'failed to create video' placeholder.", ex); //NON-NLS
+ }
+ return null;
+ }
+
/**
* Get the video thumbnails for a file which exists in a
* VideoThumbnailsWrapper and update the VideoThumbnailsWrapper to include
@@ -476,7 +514,6 @@ class FileSearch {
cacheDirectory = null;
logger.log(Level.WARNING, "Unable to get cache directory, video thumbnails will not be saved", ex);
}
-
if (cacheDirectory == null || file.getMd5Hash() == null || !Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile().exists()) {
java.io.File tempFile;
try {
@@ -488,7 +525,7 @@ class FileSearch {
0,
0,
0};
- thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
+ thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return;
}
if (tempFile.exists() == false || tempFile.length() < file.getSize()) {
@@ -502,7 +539,7 @@ class FileSearch {
0,
0,
0};
- thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
+ thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return;
}
ContentUtils.writeToFile(file, tempFile, progress, null, true);
@@ -523,7 +560,7 @@ class FileSearch {
0,
0,
0};
- thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
+ thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return;
}
double fps = videoFile.get(5); // gets frame per second
@@ -535,7 +572,7 @@ class FileSearch {
0,
0,
0};
- thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
+ thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return;
}
if (Thread.interrupted()) {
@@ -544,7 +581,7 @@ class FileSearch {
0,
0,
0};
- thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
+ thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return;
}
@@ -573,10 +610,10 @@ class FileSearch {
logger.log(Level.WARNING, "Error seeking to " + framePositions[i] + "ms in {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
// If we can't set the time, continue to the next frame position and try again.
- videoThumbnails.add(ImageUtils.getDefaultThumbnail());
+ videoThumbnails.add(VIDEO_DEFAULT_IMAGE);
if (cacheDirectory != null) {
try {
- ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
+ ImageIO.write(VIDEO_DEFAULT_IMAGE, THUMBNAIL_FORMAT,
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
} catch (IOException ex) {
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
@@ -588,10 +625,10 @@ class FileSearch {
if (!videoFile.read(imageMatrix)) {
logger.log(Level.WARNING, "Error reading frame at " + framePositions[i] + "ms from {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
// If the image is bad for some reason, continue to the next frame position and try again.
- videoThumbnails.add(ImageUtils.getDefaultThumbnail());
+ videoThumbnails.add(VIDEO_DEFAULT_IMAGE);
if (cacheDirectory != null) {
try {
- ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
+ ImageIO.write(VIDEO_DEFAULT_IMAGE, THUMBNAIL_FORMAT,
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
} catch (IOException ex) {
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
@@ -602,10 +639,10 @@ class FileSearch {
}
// If the image is empty, return since no buffered image can be created.
if (imageMatrix.empty()) {
- videoThumbnails.add(ImageUtils.getDefaultThumbnail());
+ videoThumbnails.add(VIDEO_DEFAULT_IMAGE);
if (cacheDirectory != null) {
try {
- ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
+ ImageIO.write(VIDEO_DEFAULT_IMAGE, THUMBNAIL_FORMAT,
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
} catch (IOException ex) {
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
@@ -660,7 +697,7 @@ class FileSearch {
videoFile.release(); // close the file}
}
} else {
- loadSavedThumbnails(cacheDirectory, thumbnailWrapper);
+ loadSavedThumbnails(cacheDirectory, thumbnailWrapper, VIDEO_DEFAULT_IMAGE);
}
}
@@ -674,7 +711,7 @@ class FileSearch {
* information about the file and the thumbnails
* associated with it.
*/
- private static void loadSavedThumbnails(String cacheDirectory, VideoThumbnailsWrapper thumbnailWrapper) {
+ private static void loadSavedThumbnails(String cacheDirectory, VideoThumbnailsWrapper thumbnailWrapper, BufferedImage failedVideoThumbImage) {
int[] framePositions = new int[4];
List videoThumbnails = new ArrayList<>();
int thumbnailNumber = 0;
@@ -683,7 +720,7 @@ class FileSearch {
try {
videoThumbnails.add(ImageIO.read(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, md5, fileName).toFile()));
} catch (IOException ex) {
- videoThumbnails.add(ImageUtils.getDefaultThumbnail());
+ videoThumbnails.add(failedVideoThumbImage);
logger.log(Level.WARNING, "Unable to read saved video thumbnail " + fileName + " for " + md5, ex);
}
int framePos = Integer.valueOf(FilenameUtils.getBaseName(fileName).substring(2));
@@ -699,12 +736,12 @@ class FileSearch {
*
* @return List containing the default thumbnail.
*/
- private static List createDefaultThumbnailList() {
+ private static List createDefaultThumbnailList(BufferedImage failedVideoThumbImage) {
List videoThumbnails = new ArrayList<>();
- videoThumbnails.add(ImageUtils.getDefaultThumbnail());
- videoThumbnails.add(ImageUtils.getDefaultThumbnail());
- videoThumbnails.add(ImageUtils.getDefaultThumbnail());
- videoThumbnails.add(ImageUtils.getDefaultThumbnail());
+ videoThumbnails.add(failedVideoThumbImage);
+ videoThumbnails.add(failedVideoThumbImage);
+ videoThumbnails.add(failedVideoThumbImage);
+ videoThumbnails.add(failedVideoThumbImage);
return videoThumbnails;
}
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java
index d86d470102..8efabf2704 100644
--- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java
+++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java
@@ -273,7 +273,6 @@ final class FileSearchData {
= new ImmutableSet.Builder()
.add("text/html", //NON-NLS
"text/csv", //NON-NLS
- "text/x-log", //NON-NLS
"application/rtf", //NON-NLS
"application/pdf", //NON-NLS
"application/xhtml+xml", //NON-NLS
@@ -292,6 +291,14 @@ final class FileSearchData {
"application/vnd.oasis.opendocument.text" //NON-NLS
).build();
+ private static final ImmutableSet IMAGE_UNSUPPORTED_DOC_TYPES
+ = new ImmutableSet.Builder()
+ .add("application/pdf", //NON-NLS
+ "application/xhtml+xml").build(); //NON-NLS
+
+ static Collection getDocTypesWithoutImageExtraction(){
+ return Collections.unmodifiableCollection(IMAGE_UNSUPPORTED_DOC_TYPES);
+ }
/**
* Enum representing the file type. We don't simply use
* FileTypeUtils.FileTypeCategory because: - Some file types categories
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java
index 7ba0e452ca..7a750f50eb 100644
--- a/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java
@@ -782,7 +782,7 @@ public class ResultsPanel extends javax.swing.JPanel {
if (preview == null) {
preview = new TextSummary(Bundle.ResultsPanel_unableToCreate_text(), null, 0);
}
- documentWrapper.setPreview(preview.getSummaryText());
+ documentWrapper.setSummary(preview);
return null;
}
@@ -792,10 +792,10 @@ public class ResultsPanel extends javax.swing.JPanel {
try {
get();
} catch (InterruptedException | ExecutionException ex) {
- documentWrapper.setPreview(ex.getMessage());
+ documentWrapper.setSummary(new TextSummary(ex.getMessage(), null, 0));
logger.log(Level.WARNING, "Document Worker Exception", ex);
} catch (CancellationException ignored) {
- documentWrapper.setPreview(Bundle.ResultsPanel_documentPreview_text());
+ documentWrapper.setSummary(new TextSummary(Bundle.ResultsPanel_documentPreview_text(), null, 0));
//we want to do nothing in response to this since we allow it to be cancelled
}
documentPreviewViewer.repaint();
diff --git a/Core/src/org/sleuthkit/autopsy/images/failedToCreateVideoThumb.png b/Core/src/org/sleuthkit/autopsy/images/failedToCreateVideoThumb.png
new file mode 100644
index 0000000000..876402c150
Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/failedToCreateVideoThumb.png differ
diff --git a/Core/src/org/sleuthkit/autopsy/images/image-extraction-not-supported.png b/Core/src/org/sleuthkit/autopsy/images/image-extraction-not-supported.png
new file mode 100644
index 0000000000..99a40745a7
Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/image-extraction-not-supported.png differ
diff --git a/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java
index fbc4ebc12b..d359332f84 100755
--- a/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java
@@ -269,7 +269,7 @@ public class PlasoIngestModule implements DataSourceIngestModule {
String architectureFolder = PlatformUtil.is64BitOS() ? PLASO64 : PLASO32;
String executableToFindName = Paths.get(PLASO, architectureFolder, executableName).toString();
- File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, "org.sleuthkit.autopsy.core", false);
+ File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, PlasoIngestModule.class.getPackage().getName(), false);
if (null == exeFile || exeFile.canExecute() == false) {
throw new FileNotFoundException(executableName + " executable not found.");
}
diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/TikaTextExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/TikaTextExtractor.java
index 6a84a2285c..a8bf0591fb 100644
--- a/Core/src/org/sleuthkit/autopsy/textextractors/TikaTextExtractor.java
+++ b/Core/src/org/sleuthkit/autopsy/textextractors/TikaTextExtractor.java
@@ -423,7 +423,7 @@ final class TikaTextExtractor implements TextExtractor {
}
String executableToFindName = Paths.get(TESSERACT_DIR_NAME, TESSERACT_EXECUTABLE).toString();
- File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, "org.sleuthkit.autopsy.core", false);
+ File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, TikaTextExtractor.class.getPackage().getName(), false);
if (null == exeFile) {
return null;
}
diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java
index 4d8dea3d4a..845679b79b 100644
--- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java
@@ -55,16 +55,19 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel {
authenticationKeyField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
+ testResultValueLabel.setText("");
firePropertyChange("SettingChanged", true, false);
}
@Override
public void removeUpdate(DocumentEvent e) {
+ testResultValueLabel.setText("");
firePropertyChange("SettingChanged", true, false);
}
@Override
public void changedUpdate(DocumentEvent e) {
+ testResultValueLabel.setText("");
firePropertyChange("SettingChanged", true, false);
}
@@ -256,6 +259,7 @@ public class BingTranslatorSettingsPanel extends javax.swing.JPanel {
String selectedCode = ((LanguageWrapper) targetLanguageComboBox.getSelectedItem()).getLanguageCode();
if (!StringUtils.isBlank(selectedCode) && !selectedCode.equals(targetLanguageCode)) {
targetLanguageCode = selectedCode;
+ testResultValueLabel.setText("");
firePropertyChange("SettingChanged", true, false);
}
}//GEN-LAST:event_targetLanguageComboBoxSelected
diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java
index b81ce9fdb1..26387e9353 100644
--- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslatorSettingsPanel.java
@@ -337,6 +337,7 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
if (dialogResult == JFileChooser.APPROVE_OPTION) {
credentialsPathField.setText(fileChooser.getSelectedFile().getPath());
populateTargetLanguageComboBox();
+ testResultValueLabel.setText("");
firePropertyChange("SettingChanged", true, false);
}
}//GEN-LAST:event_browseButtonActionPerformed
@@ -407,6 +408,7 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
if (!StringUtils.isBlank(selectedCode) && !selectedCode.equals(targetLanguageCode)) {
targetLanguageCode = selectedCode;
populateTargetLanguageComboBox();
+ testResultValueLabel.setText("");
firePropertyChange("SettingChanged", true, false);
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/utils/FileNameTranslationUtil.java b/Core/src/org/sleuthkit/autopsy/texttranslation/utils/FileNameTranslationUtil.java
new file mode 100755
index 0000000000..752e981990
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/texttranslation/utils/FileNameTranslationUtil.java
@@ -0,0 +1,74 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2020-2020 Basis Technology Corp.
+ * Contact: carrier sleuthkit 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.texttranslation.utils;
+
+import org.apache.commons.io.FilenameUtils;
+import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
+import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
+import org.sleuthkit.autopsy.texttranslation.TranslationException;
+
+/**
+ * A utility to translate file names.
+ */
+public final class FileNameTranslationUtil {
+
+ /**
+ * Translates a file name using the configured machine translation service.
+ *
+ * @param fileName The file name.
+ *
+ * @return The translation of the file name.
+ *
+ * @throws NoServiceProviderException If machine translation is not
+ * configured.
+ * @throws TranslationException If there is an error doing the
+ * translation.
+ */
+ public static String translate(String fileName) throws NoServiceProviderException, TranslationException {
+ /*
+ * Don't attempt translation if the characters of the file name are all
+ * ASCII chars.
+ *
+ * TODO (Jira-6175): This filter prevents translation of many
+ * non-English file names composed entirely of Latin chars.
+ */
+ if (fileName.matches("^\\p{ASCII}+$")) {
+ return "";
+ }
+
+ TextTranslationService translator = TextTranslationService.getInstance();
+ String baseName = FilenameUtils.getBaseName(fileName);
+ String translation = translator.translate(baseName);
+ if (!translation.isEmpty()) {
+ String extension = FilenameUtils.getExtension(fileName);
+ if (!extension.isEmpty()) {
+ String extensionDelimiter = (extension.isEmpty()) ? "" : ".";
+ translation += extensionDelimiter + extension;
+ }
+ }
+ return translation;
+ }
+
+ /**
+ * Prevent instantiation of this utility class
+ */
+ private FileNameTranslationUtil() {
+ }
+
+}
diff --git a/CoreLibs/ivy.xml b/CoreLibs/ivy.xml
index 6819dac82d..4853d1f90e 100644
--- a/CoreLibs/ivy.xml
+++ b/CoreLibs/ivy.xml
@@ -14,8 +14,7 @@
-
-
+
@@ -73,8 +72,5 @@
-
-
-
diff --git a/CoreLibs/nbproject/project.properties b/CoreLibs/nbproject/project.properties
index 677f823e67..42f8292d5b 100644
--- a/CoreLibs/nbproject/project.properties
+++ b/CoreLibs/nbproject/project.properties
@@ -21,7 +21,6 @@ file.reference.dom4j-1.6.1.jar=release/modules/ext/dom4j-1.6.1.jar
file.reference.geronimo-jms_1.1_spec-1.0.jar=release/modules/ext/geronimo-jms_1.1_spec-1.0.jar
file.reference.gson-2.8.5.jar=release/modules/ext/gson-2.8.5.jar
file.reference.gst1-java-core-1.0.0.jar=release\\modules\\ext\\gst1-java-core-1.0.0.jar
-file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar
file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar
file.reference.imageio-bmp-3.2.jar=release/modules/ext/imageio-bmp-3.2.jar
file.reference.imageio-core-3.2.jar=release/modules/ext/imageio-core-3.2.jar
@@ -44,6 +43,8 @@ file.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-
file.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4.jar
file.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4.jar
file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar
+file.reference.jna-5.5.0.jar=release\\modules\\ext\\jna-5.5.0.jar
+file.reference.jna-platform-5.5.0.jar=release\\modules\\ext\\jna-platform-5.5.0.jar
file.reference.joda-time-2.4.jar=release/modules/ext/joda-time-2.4.jar
file.reference.jsr305-1.3.9.jar=release/modules/ext/jsr305-1.3.9.jar
file.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10.3.1.jar
@@ -52,7 +53,6 @@ file.reference.logkit-1.0.1.jar=release/modules/ext/logkit-1.0.1.jar
file.reference.mail-1.4.3.jar=release/modules/ext/mail-1.4.3.jar
file.reference.opencv-248.jar=release/modules/ext/opencv-248.jar
file.reference.openjfx-dialogs-1.0.2.jar=release/modules/ext/openjfx-dialogs-1.0.3.jar
-file.reference.platform-3.4.0.jar=release/modules/ext/platform-3.4.0.jar
file.reference.poi-4.0.1.jar=release\\modules\\ext\\poi-4.0.1.jar
file.reference.poi-excelant-4.0.1.jar=release\\modules\\ext\\poi-excelant-4.0.1.jar
file.reference.poi-ooxml-4.0.1.jar=release\\modules\\ext\\poi-ooxml-4.0.1.jar
diff --git a/CoreLibs/nbproject/project.xml b/CoreLibs/nbproject/project.xml
index 0498669b04..d5169a8965 100644
--- a/CoreLibs/nbproject/project.xml
+++ b/CoreLibs/nbproject/project.xml
@@ -806,10 +806,6 @@
ext/sigar-1.6.4.jar
release/modules/ext/sigar-1.6.4.jar