diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDbUpgrader13To14.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDbUpgrader13To14.java index f4ddbca089..9d6473055e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDbUpgrader13To14.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDbUpgrader13To14.java @@ -39,7 +39,7 @@ public class CentralRepoDbUpgrader13To14 implements CentralRepoDbUpgrader { try (Statement statement = connection.createStatement();) { - CentralRepoPlatforms selectedPlatform = CentralRepoPlatforms.getSelectedPlatform(); + CentralRepoPlatforms selectedPlatform = CentralRepoDbManager.getSavedDbChoice().getDbPlatform(); // Create account_types and accounts tables which are referred by X_instances tables statement.execute(RdbmsCentralRepoFactory.getCreateAccountTypesTableStatement(selectedPlatform)); diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java index c518abdd45..53cf9e8bf4 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java @@ -241,7 +241,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 +264,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 +292,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 +330,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()); + } } }; } @@ -556,10 +561,12 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie //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 (gstPlayBin != null) { + Bus playBinBus = gstPlayBin.getBus(); + playBinBus.connect(endOfStreamListener); + playBinBus.connect(stateChangeListener); + playBinBus.connect(errorListener); + } if (this.isCancelled()) { return; @@ -570,15 +577,16 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS)); videoPanel.add(fxPanel); fxAppSink = new JavaFxAppSink("JavaFxAppSink", fxPanel); - gstPlayBin.setVideoSink(fxAppSink); - + if (gstPlayBin != null) { + gstPlayBin.setVideoSink(fxAppSink); + } if (this.isCancelled()) { return; } - - gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0); - gstPlayBin.pause(); - + if (gstPlayBin != null) { + gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0); + gstPlayBin.pause(); + } timer.start(); enableComponents(true); } catch (CancellationException ex) { @@ -598,7 +606,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie @Override public void actionPerformed(ActionEvent e) { - if (!progressSlider.getValueIsAdjusting()) { + if (!progressSlider.getValueIsAdjusting() && gstPlayBin != null) { sliderLock.acquireUninterruptibly(); long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS); @@ -635,13 +643,13 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie * thumb at the given width and height. It also paints the track blue as * the thumb progresses. * - * @param slider JSlider component + * @param slider JSlider component * @param thumbDimension */ 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 +663,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 +713,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,87 +991,95 @@ 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); - }//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; - } - - 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 - - private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playButtonActionPerformed - if (gstPlayBin.isPlaying()) { - gstPlayBin.pause(); - } else { - double playBackRate = getPlayBackRate(); + if (gstPlayBin != null) { long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); - //Set playback rate before play. + //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, currentTime, + SeekType.SET, newTime, //Do nothing for the end position SeekType.NONE, -1); - gstPlayBin.play(); + } + }//GEN-LAST:event_rewindButtonActionPerformed + + private void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fastForwardButtonActionPerformed + 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; + } + + 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; + } + + 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 + + private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playButtonActionPerformed + 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(); + } } }//GEN-LAST:event_playButtonActionPerformed 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); + 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); + } }//GEN-LAST:event_playBackSpeedComboBoxActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 17b8248f06..b4aafc2bcc 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; @@ -79,45 +78,62 @@ import org.sleuthkit.datamodel.TskData; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; /** - * 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; // May be null. + + /* + * 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 appEventListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { String eventType = evt.getPropertyName(); @@ -132,24 +148,32 @@ 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.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, weakAppEventListener); } + /** + * 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,64 +341,18 @@ public class BlackboardArtifactNode extends AbstractContentNode map = new LinkedHashMap<>(); - fillPropertyMap(map, artifact); - + /* + * Add the name of the source content of the artifact represented by + * this node to the sheet. + */ sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.displayName"), NO_DESCR, this.getSourceName())); - // Create place holders for S C O 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, "")); } - // Get the SCO columns data in a background task - backgroundTasksPool.submit(new GetSCOTask( - new WeakReference<>(this), weakPcl)); + backgroundTasksPool.submit(new GetSCOTask(new WeakReference<>(this), weakAppEventListener)); } + /* + * 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)); @@ -404,10 +424,16 @@ public class BlackboardArtifactNode extends AbstractContentNode map = new LinkedHashMap<>(); + fillPropertyMap(map, artifact); for (Map.Entry entry : map.entrySet()) { sheetSet.put(new NodeProperty<>(entry.getKey(), entry.getKey(), @@ -415,23 +441,28 @@ 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 != null && srcContent instanceof AbstractFile) { + AbstractFile file = (AbstractFile) srcContent; + ext = file.getNameExtension(); + actualMimeType = file.getMIMEType(); if (actualMimeType == null) { actualMimeType = ""; //NON-NLS } @@ -447,12 +478,18 @@ public class BlackboardArtifactNode extends AbstractContentNode(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"), "", @@ -484,7 +526,7 @@ public class BlackboardArtifactNode extends AbstractContentNode(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"), "", - associated.getSize())); + file == null ? "" : file.getSize())); sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(), Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(), "", @@ -492,15 +534,17 @@ public class BlackboardArtifactNode extends AbstractContentNode getAllTagsFromDatabase() { List tags = new ArrayList<>(); try { tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact)); - tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(associated)); + if (srcContent != null) { + 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 (srcContent != null && 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 +664,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 != null && 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 +718,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 +757,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())); //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 +879,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 +921,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 +935,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/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index 9ac85bd1e7..8289f947da 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -78,9 +78,6 @@ BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated f 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