diff --git a/Core/manifest.mf b/Core/manifest.mf index dbfb89dc59..494ba39afc 100644 --- a/Core/manifest.mf +++ b/Core/manifest.mf @@ -2,7 +2,7 @@ Manifest-Version: 1.0 OpenIDE-Module: org.sleuthkit.autopsy.core/10 OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml -OpenIDE-Module-Implementation-Version: 29 +OpenIDE-Module-Implementation-Version: 30 OpenIDE-Module-Requires: org.openide.windows.WindowManager AutoUpdate-Show-In-Client: true AutoUpdate-Essential-Module: true diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index 3c994d58ac..c01abc4855 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -79,7 +79,7 @@ file.reference.javax.ws.rs-api-2.0.1.jar=release/modules/ext/javax.ws.rs-api-2.0 file.reference.cxf-core-3.0.16.jar=release/modules/ext/cxf-core-3.0.16.jar file.reference.cxf-rt-frontend-jaxrs-3.0.16.jar=release/modules/ext/cxf-rt-frontend-jaxrs-3.0.16.jar file.reference.cxf-rt-transports-http-3.0.16.jar=release/modules/ext/cxf-rt-transports-http-3.0.16.jar -file.reference.sleuthkit-postgresql-4.7.0.jar=release/modules/ext/sleuthkit-postgresql-4.7.0.jar +file.reference.sleuthkit-postgresql-4.8.0.jar=release/modules/ext/sleuthkit-postgresql-4.8.0.jar file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar @@ -127,5 +127,5 @@ nbm.homepage=http://www.sleuthkit.org/ nbm.module.author=Brian Carrier nbm.needs.restart=true source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar -spec.version.base=10.17 +spec.version.base=10.18 diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 83aefea7c5..b2e17a7e32 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -345,6 +345,7 @@ org.sleuthkit.autopsy.textextractors.configs org.sleuthkit.autopsy.texttranslation org.sleuthkit.datamodel + org.sleuthkit.datamodel.blackboardutils ext/commons-lang3-3.8.1.jar @@ -527,8 +528,8 @@ release/modules/ext/google-http-client-1.29.0.jar - ext/sleuthkit-postgresql-4.7.0.jar - release/modules/ext/sleuthkit-postgresql-4.7.0.jar + ext/sleuthkit-postgresql-4.8.0.jar + release/modules/ext/sleuthkit-postgresql-4.8.0.jar ext/bcpkix-jdk15on-1.60.jar diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index ca2deebaa4..b56e46b377 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -354,7 +354,7 @@ UnpackagePortableCaseProgressDialog.title.text=Unpackage Portable Case Progress UnpackageWorker.doInBackground.canceled=Unpackaging canceled by user UnpackageWorker.doInBackground.errorCompressingCase=Error unpackaging case UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executable -UnpackageWorker.doInBackground.previousSeenCase=Case with name {0} has been previously opened do you want to open it again? +UnpackageWorker.doInBackground.previouslySeenCase=Case has been previously opened. Open it again? UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.empty=-Empty- AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index f63cfa1a81..e684b4af4b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -761,7 +761,7 @@ public class Case { @Messages({ "Case.progressIndicatorTitle.deletingDataSource=Removing Data Source" }) - public static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException { + static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException { synchronized (caseActionSerializationLock) { if (null == currentCase) { return; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/UnpackagePortableCaseProgressDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/UnpackagePortableCaseProgressDialog.java index fe26843e28..b5f495806b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/UnpackagePortableCaseProgressDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/UnpackagePortableCaseProgressDialog.java @@ -150,14 +150,14 @@ class UnpackagePortableCaseProgressDialog extends javax.swing.JDialog implements "UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executable", "UnpackageWorker.doInBackground.errorCompressingCase=Error unpackaging case", "UnpackageWorker.doInBackground.canceled=Unpackaging canceled by user", - "UnpackageWorker.doInBackground.previousSeenCase=Case with name {0} has been previously opened do you want to open it again?"}) + "UnpackageWorker.doInBackground.previouslySeenCase=Case has been previously opened. Open it again?",}) @Override protected Void doInBackground() throws Exception { // Check to see if this case has been already opened before String caseUnpackedBefore = getCaseIfUnpackedBefore(packagedCase); if ((!caseUnpackedBefore.isEmpty()) - && (MessageNotifyUtil.Message.confirm(Bundle.UnpackageWorker_doInBackground_previousSeenCase(packagedCase)))) { + && (MessageNotifyUtil.Message.confirm(Bundle.UnpackageWorker_doInBackground_previouslySeenCase()))) { try { Case.openAsCurrentCase(caseUnpackedBefore); success.set(true); diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/AccountSummary.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/AccountSummary.java index 5fc7ae8aa7..5c14125d87 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/AccountSummary.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/AccountSummary.java @@ -18,16 +18,21 @@ */ package org.sleuthkit.autopsy.communications.relationships; +import com.google.gson.Gson; +import java.util.Collection; import java.util.List; import java.util.Set; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.blackboardutils.FileAttachment; +import org.sleuthkit.datamodel.blackboardutils.MessageAttachments; /** * @@ -121,10 +126,29 @@ class AccountSummary { } } try { - attachmentCnt += artifact.getChildrenCount(); - for (Content childContent : artifact.getChildren()) { - if (ImageUtils.thumbnailSupported(childContent)) { - mediaCnt++; + // count the attachments from the TSK_ATTACHMENTS attribute. + BlackboardAttribute attachmentsAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS)); + if (attachmentsAttr != null) { + String jsonVal = attachmentsAttr.getValueString(); + MessageAttachments msgAttachments = new Gson().fromJson(jsonVal, MessageAttachments.class); + + Collection fileAttachments = msgAttachments.getFileAttachments(); + for (FileAttachment fileAttachment : fileAttachments) { + attachmentCnt++; + long attachedFileObjId = fileAttachment.getObjectId(); + if (attachedFileObjId >= 0) { + AbstractFile attachedFile = artifact.getSleuthkitCase().getAbstractFileById(attachedFileObjId); + if (ImageUtils.thumbnailSupported(attachedFile)) { + mediaCnt++; + } + } + } + } else { // backward compatibility - email message attachments are derived files, children of the message. + attachmentCnt += artifact.getChildrenCount(); + for (Content childContent : artifact.getChildren()) { + if (ImageUtils.thumbnailSupported(childContent)) { + mediaCnt++; + } } } } catch (TskCoreException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java index 769ba682a6..a5a40ff924 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java @@ -36,7 +36,6 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBU import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.communications.Utils; -import org.sleuthkit.autopsy.coreutils.PhoneNumUtil; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/PhoneNumUtil.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/PhoneNumUtil.java similarity index 97% rename from Core/src/org/sleuthkit/autopsy/coreutils/PhoneNumUtil.java rename to Core/src/org/sleuthkit/autopsy/communications/relationships/PhoneNumUtil.java index 1fa56d1bfd..7bb87e823a 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/PhoneNumUtil.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/PhoneNumUtil.java @@ -16,12 +16,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.coreutils; +package org.sleuthkit.autopsy.communications.relationships; import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.Phonenumber; import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; /** * diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java index a8694c2619..f373b8a333 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java @@ -29,7 +29,6 @@ import org.openide.util.Lookup; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.datamodel.Account; -import org.sleuthkit.autopsy.coreutils.PhoneNumUtil; /** * Account Summary View Panel. This panel shows a list of various counts related diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index f8119b88da..cf07682e5d 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -87,13 +87,9 @@ HtmlPanel.showImagesToggleButton.text=Download Images MediaViewImagePanel.tagsMenu.text_1=Tags Menu MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00 MediaPlayerPanel.audioSlider.toolTipText= -MediaPlayerPanel.rewindButton.text=\u2bc7\u2bc7 -MediaPlayerPanel.fastForwardButton.text=\u2bc8\u2bc8 -MediaPlayerPanel.playButton.text=\u25ba +MediaPlayerPanel.rewindButton.text= +MediaPlayerPanel.fastForwardButton.text= +MediaPlayerPanel.playButton.text= MediaPlayerPanel.infoLabel.text=No Errors MediaPlayerPanel.VolumeIcon.text=Volume MediaPlayerPanel.playBackSpeedLabel.text=Speed: -ContextViewer.jSourceGoToResultButton.text=Go to Result -ContextViewer.jSourceNameLabel.text=jSourceNameLabel -ContextViewer.jSourceTextLabel.text=jLabel2 -ContextViewer.jSourceLabel.text=Source diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index ceec760a69..13f3ef0710 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -10,17 +10,6 @@ AnnotationsContentViewer.title=Annotations AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content. ApplicationContentViewer.title=Application ApplicationContentViewer.toolTip=Displays file contents. -ContextViewer.attachmentSource=Attached to: -ContextViewer.downloadedOn=On -ContextViewer.downloadSource=Downloaded from: -ContextViewer.downloadURL=URL -ContextViewer.email=Email -ContextViewer.message=Message -ContextViewer.messageFrom=From -ContextViewer.messageOn=On -ContextViewer.messageTo=From -ContextViewer.title=Context Viewer -ContextViewer.toolTip=Displays context for selected file. FXVideoPanel.pauseButton.infoLabel.playbackErr=Unable to play video. FXVideoPanel.progress.bufferingCancelled=media buffering was canceled FXVideoPanel.progress.bufferingInterrupted=media buffering was interrupted @@ -169,16 +158,12 @@ HtmlPanel.showImagesToggleButton.text=Download Images MediaViewImagePanel.tagsMenu.text_1=Tags Menu MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00 MediaPlayerPanel.audioSlider.toolTipText= -MediaPlayerPanel.rewindButton.text=\u2bc7\u2bc7 -MediaPlayerPanel.fastForwardButton.text=\u2bc8\u2bc8 -MediaPlayerPanel.playButton.text=\u25ba +MediaPlayerPanel.rewindButton.text= +MediaPlayerPanel.fastForwardButton.text= +MediaPlayerPanel.playButton.text= MediaPlayerPanel.infoLabel.text=No Errors MediaPlayerPanel.VolumeIcon.text=Volume MediaPlayerPanel.playBackSpeedLabel.text=Speed: -ContextViewer.jSourceGoToResultButton.text=Go to Result -ContextViewer.jSourceNameLabel.text=jSourceNameLabel -ContextViewer.jSourceTextLabel.text=jLabel2 -ContextViewer.jSourceLabel.text=Source # {0} - tableName SQLiteViewer.readTable.errorText=Error getting rows for table: {0} # {0} - tableName diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.form index d8433a907b..0b00c43d8a 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.form @@ -41,7 +41,7 @@ - + @@ -83,7 +83,7 @@ - + @@ -107,7 +107,7 @@ - + @@ -123,9 +123,21 @@ + + + + + + + + + + + + @@ -138,6 +150,9 @@ + + + @@ -153,6 +168,9 @@ + + + @@ -172,6 +190,15 @@ + + + + + + + + + @@ -188,15 +215,19 @@ + + + - + - + + - + @@ -223,7 +254,7 @@ - + @@ -233,12 +264,15 @@ - - - - + + + + + + + - + @@ -260,14 +294,15 @@ - + - + - + + @@ -281,6 +316,15 @@ + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java index 84f7da6c36..b434b5fec0 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2019 Basis Technology Corp. + * Copyright 2013-2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.io.File; import java.io.IOException; import java.util.Arrays; @@ -61,6 +62,7 @@ import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskData; import javafx.embed.swing.JFXPanel; +import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JSlider; import javax.swing.SwingUtilities; @@ -72,6 +74,7 @@ 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; /** * This is a video player that is part of the Media View layered pane. It uses @@ -109,7 +112,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie ".wav", ".webm", ".wma", - ".wmv",}; //NON-NLS + ".wmv"}; //NON-NLS private static final List MIME_TYPES = Arrays.asList( "video/3gpp", "video/3gpp2", @@ -200,15 +203,18 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie private static final int PROGRESS_SLIDER_SIZE = 2000; private static final int SKIP_IN_SECONDS = 30; + private final ImageIcon playIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/contentviewers/images/Play-arrow-01.png")); + private final ImageIcon pauseIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/contentviewers/images/Pause-01.png")); + private ExtractMedia extractMediaWorker; //Serialize setting the value of the Video progress slider. //The slider is a shared resource between the VideoPanelUpdater - //and the TrackListener of the JSliderUI. + //and the TrackListener on the slider itself. private final Semaphore sliderLock; /** - * Creates new form MediaViewVideoPanel + * Creates a new MediaPlayerPanel */ public MediaPlayerPanel() throws GstException, UnsatisfiedLinkError { initComponents(); @@ -216,6 +222,14 @@ 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() { @@ -247,7 +261,37 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie } } }); - //Manage the audio level when the user is adjusting the volumn slider + //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(); + } + + @Override + public void mouseReleased(MouseEvent e) { + if(previousState.equals(State.PLAYING)) { + gstPlayBin.play(); + } + previousState = State.NULL; + } + @Override + public void mouseClicked(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @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()) { double audioPercent = (audioSlider.getValue() * 2.0) / 100.0; @@ -271,11 +315,11 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie public void stateChanged(GstObject go, State oldState, State currentState, State pendingState) { if (State.PLAYING.equals(currentState)) { SwingUtilities.invokeLater(() -> { - playButton.setText("||"); + playButton.setIcon(pauseIcon); }); } else { SwingUtilities.invokeLater(() -> { - playButton.setText("►"); + playButton.setIcon(playIcon); }); } } @@ -504,8 +548,10 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie // 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. - Gst.init(); + // earlier resulted in conflicts on Linux. See JIRA-5888. + if (!PlatformUtil.isWindowsOS()) { + Gst.init(); + } //Video is ready for playback. Create new components gstPlayBin = new PlayBin("VideoPlayer", tempFile.toURI()); @@ -574,63 +620,15 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie } } - /** - * Represents the default configuration for the circular JSliderUI. - */ - private class CircularJSliderConfiguration { - - //Thumb configurations - private final Color thumbColor; - private final Dimension thumbDimension; - - //Track configurations - //Progress bar can be bisected into a seen group - //and an unseen group. - private final Color unseen; - private final Color seen; - - /** - * Default configuration - * - * JSlider is light blue RGB(0,130,255). Seen track is light blue - * RGB(0,130,255). Unseen track is light grey RGB(192, 192, 192). - * - * @param thumbDimension Size of the oval thumb. - */ - public CircularJSliderConfiguration(Dimension thumbDimension) { - Color lightBlue = new Color(0, 130, 255); - - seen = lightBlue; - unseen = Color.LIGHT_GRAY; - - thumbColor = lightBlue; - - this.thumbDimension = new Dimension(thumbDimension); - } - - public Color getThumbColor() { - return thumbColor; - } - - public Color getUnseenTrackColor() { - return unseen; - } - - public Color getSeenTrackColor() { - return seen; - } - - public Dimension getThumbDimension() { - return new Dimension(thumbDimension); - } - } - /** * Custom view for the JSlider. */ private class CircularJSliderUI extends BasicSliderUI { - private final CircularJSliderConfiguration config; + private final Dimension thumbDimension; + private final Color thumbColor; + private final Color trackUnseen; + private final Color trackSeen; /** * Creates a custom view for the JSlider. This view draws a blue oval @@ -641,18 +639,25 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie * @param config Configuration object. Contains info about thumb * dimensions and colors. */ - public CircularJSliderUI(JSlider slider, CircularJSliderConfiguration config) { + public CircularJSliderUI(JSlider slider, Dimension thumbDimension) { super(slider); - this.config = config; + this.thumbDimension = thumbDimension; + + //Configure track and thumb colors. + Color lightBlue = new Color(0, 130, 255); + thumbColor = lightBlue; + trackSeen = lightBlue; + trackUnseen = Color.LIGHT_GRAY; } @Override protected Dimension getThumbSize() { - return config.getThumbDimension(); + return new Dimension(thumbDimension); } /** - * Modifies the View to be an oval rather than the rectangle Controller. + * Modifies the View to be an oval rather than the underlying + * rectangle Controller. */ @Override public void paintThumb(Graphics graphic) { @@ -662,8 +667,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie //Change the thumb view from the rectangle //controller to an oval. - graphic.setColor(config.getThumbColor()); - Dimension thumbDimension = config.getThumbDimension(); + graphic.setColor(thumbColor); graphic.fillOval(thumb.x, thumb.y, thumbDimension.width, thumbDimension.height); //Preserve the graphics original color @@ -686,12 +690,12 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie Color original = graphic.getColor(); //Paint the seen side - graphic.setColor(config.getSeenTrackColor()); + graphic.setColor(trackSeen); graphic.drawLine(track.x, track.y + track.height / 2, thumbX, thumbY + track.height / 2); //Paint the unseen side - graphic.setColor(config.getUnseenTrackColor()); + graphic.setColor(trackUnseen); graphic.drawLine(thumbX, thumbY + track.height / 2, track.x + track.width, track.y + track.height / 2); @@ -701,7 +705,26 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie @Override protected TrackListener createTrackListener(JSlider slider) { - return new CustomTrackListener(); + /** + * 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) { + if (!slider.isEnabled() || !SwingUtilities.isLeftMouseButton(e)) { + return; + } + //Snap the thumb to position of the mouse + scrollDueToClickInTrack(0); + + //Handle the event as normal. + super.mousePressed(e); + } + }; } @Override @@ -715,7 +738,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie int value = this.valueForXPosition(mousePosition.x); //Lock the slider down, which is a shared resource. - //The VideoPanelUpdater (dedicated thread) keeps the + //The VideoPanelUpdater keeps the //slider in sync with the video position, so without //proper locking our change could be overwritten. sliderLock.acquireUninterruptibly(); @@ -738,43 +761,6 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie super.update(graphic, component); } - - /** - * 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. - */ - private class CustomTrackListener extends CircularJSliderUI.TrackListener { - - @Override - public void mousePressed(MouseEvent e) { - if (!slider.isEnabled()) { - return; - } - //Snap the thumb to position of the mouse - scrollDueToClickInTrack(0); - - //Pause the video for convenience - gstPlayBin.pause(); - - //Handle the event as normal. - super.mousePressed(e); - } - - @Override - public void mouseReleased(MouseEvent e) { - if (!slider.isEnabled()) { - return; - } - - super.mouseReleased(e); - - //Unpause once the mouse has been released. - gstPlayBin.play(); - } - } } /** @@ -810,7 +796,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie ); videoPanelLayout.setVerticalGroup( videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 131, Short.MAX_VALUE) + .addGap(0, 117, Short.MAX_VALUE) ); progressSlider.setValue(0); @@ -818,13 +804,17 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie progressSlider.setDoubleBuffered(true); progressSlider.setMinimumSize(new java.awt.Dimension(36, 21)); progressSlider.setPreferredSize(new java.awt.Dimension(200, 21)); - progressSlider.setUI(new CircularJSliderUI(progressSlider, new CircularJSliderConfiguration(new Dimension(18,18)))); + progressSlider.setUI(new CircularJSliderUI(progressSlider, new Dimension(18,18))); org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.progressLabel.text")); // NOI18N buttonPanel.setLayout(new java.awt.GridBagLayout()); + playButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/contentviewers/images/Play-arrow-01.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(playButton, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.playButton.text")); // NOI18N + playButton.setMaximumSize(new java.awt.Dimension(53, 29)); + playButton.setMinimumSize(new java.awt.Dimension(53, 29)); + playButton.setPreferredSize(new java.awt.Dimension(49, 29)); playButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { playButtonActionPerformed(evt); @@ -838,6 +828,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie gridBagConstraints.insets = new java.awt.Insets(5, 6, 0, 0); buttonPanel.add(playButton, gridBagConstraints); + fastForwardButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/contentviewers/images/Fast-forward-01.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(fastForwardButton, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.fastForwardButton.text")); // NOI18N fastForwardButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -851,6 +842,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie gridBagConstraints.insets = new java.awt.Insets(5, 6, 0, 0); buttonPanel.add(fastForwardButton, gridBagConstraints); + rewindButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/contentviewers/images/Fast-rewind-01.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(rewindButton, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.rewindButton.text")); // NOI18N rewindButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -866,6 +858,9 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie org.openide.awt.Mnemonics.setLocalizedText(VolumeIcon, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.VolumeIcon.text")); // NOI18N VolumeIcon.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); + VolumeIcon.setMaximumSize(new java.awt.Dimension(34, 29)); + VolumeIcon.setMinimumSize(new java.awt.Dimension(34, 29)); + VolumeIcon.setPreferredSize(new java.awt.Dimension(34, 19)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 3; gridBagConstraints.gridy = 0; @@ -880,9 +875,11 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie audioSlider.setMinorTickSpacing(5); audioSlider.setToolTipText(org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.audioSlider.toolTipText")); // NOI18N audioSlider.setValue(25); - audioSlider.setMinimumSize(new java.awt.Dimension(200, 21)); - audioSlider.setPreferredSize(new java.awt.Dimension(200, 21)); - audioSlider.setUI(new CircularJSliderUI(audioSlider, new CircularJSliderConfiguration(new Dimension(15,15)))); + audioSlider.setMaximumSize(new java.awt.Dimension(32767, 19)); + audioSlider.setMinimumSize(new java.awt.Dimension(200, 19)); + audioSlider.setPreferredSize(new java.awt.Dimension(200, 30)); + audioSlider.setRequestFocusEnabled(false); + audioSlider.setUI(new CircularJSliderUI(audioSlider, new Dimension(15,15))); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 4; gridBagConstraints.gridy = 0; @@ -898,9 +895,10 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie playBackSpeedComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "0.25x", "0.50x", "0.75x", "1x", "1.25x", "1.50x", "1.75x", "2x" })); playBackSpeedComboBox.setSelectedIndex(3); - playBackSpeedComboBox.setMaximumSize(new java.awt.Dimension(53, 23)); - playBackSpeedComboBox.setMinimumSize(new java.awt.Dimension(53, 23)); - playBackSpeedComboBox.setPreferredSize(new java.awt.Dimension(53, 23)); + playBackSpeedComboBox.setMaximumSize(new java.awt.Dimension(53, 29)); + playBackSpeedComboBox.setMinimumSize(new java.awt.Dimension(53, 29)); + playBackSpeedComboBox.setPreferredSize(new java.awt.Dimension(53, 29)); + playBackSpeedComboBox.setRequestFocusEnabled(false); playBackSpeedComboBox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { playBackSpeedComboBoxActionPerformed(evt); @@ -908,13 +906,16 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie }); org.openide.awt.Mnemonics.setLocalizedText(playBackSpeedLabel, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.playBackSpeedLabel.text")); // NOI18N + playBackSpeedLabel.setMaximumSize(new java.awt.Dimension(34, 19)); + playBackSpeedLabel.setMinimumSize(new java.awt.Dimension(34, 19)); + playBackSpeedLabel.setPreferredSize(new java.awt.Dimension(34, 19)); javax.swing.GroupLayout playBackPanelLayout = new javax.swing.GroupLayout(playBackPanel); playBackPanel.setLayout(playBackPanelLayout); playBackPanelLayout.setHorizontalGroup( playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(playBackPanelLayout.createSequentialGroup() - .addComponent(playBackSpeedLabel) + .addComponent(playBackSpeedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(playBackSpeedComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(13, 13, 13)) @@ -922,11 +923,13 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie playBackPanelLayout.setVerticalGroup( playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(playBackPanelLayout.createSequentialGroup() - .addGap(6, 6, 6) - .addGroup(playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(playBackSpeedComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(playBackSpeedLabel)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGap(7, 7, 7) + .addGroup(playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(playBackPanelLayout.createSequentialGroup() + .addGap(2, 2, 2) + .addComponent(playBackSpeedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(playBackSpeedComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(10, 10, 10)) ); javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel); @@ -958,7 +961,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie .addGap(5, 5, 5) .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(buttonPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(playBackPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)) + .addComponent(playBackPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGap(14, 14, 14) .addComponent(infoLabel)) ); @@ -1002,13 +1005,21 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); //Skip 30 seconds. long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS); - - //Ignore fast forward requests if there are less than 30 seconds left. - if (currentTime + fastForwardDelta >= duration) { + //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; + } - long newTime = currentTime + fastForwardDelta; double playBackRate = getPlayBackRate(); gstPlayBin.seek(playBackRate, Format.TIME, diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 35f9efeddb..d93a46ab0a 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -39,6 +39,8 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; @@ -47,6 +49,7 @@ import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE; @@ -67,6 +70,7 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHO import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.blackboardutils.FileAttachment; import org.sleuthkit.datamodel.blackboardutils.MessageAttachments; @@ -370,7 +374,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont return; } - artifact = node.getLookup().lookup(BlackboardArtifact.class); + artifact = getNodeArtifact(node); if (artifact == null) { resetComponent(); return; @@ -465,7 +469,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont @Override public boolean isSupported(Node node) { - BlackboardArtifact nodeArtifact = node.getLookup().lookup(BlackboardArtifact.class); + BlackboardArtifact nodeArtifact = getNodeArtifact(node); if (nodeArtifact == null) { return false; @@ -498,10 +502,56 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont || artifactTypeID == TSK_MESSAGE.getTypeID(); } + /** + * Returns the artifact represented by node. + * + * If the node lookup has an artifact, that artifact is returned. However, + * if the node lookup is a file, then we look for a TSK_ASSOCIATED_OBJECT + * artifact on the file, and if a message artifact is found associated with + * the file, that artifact is returned. + * + * @param node Node to check. + * @return Blackboard artifact for the node, null if there isn't any. + */ + private BlackboardArtifact getNodeArtifact(Node node) { + BlackboardArtifact nodeArtifact = node.getLookup().lookup(BlackboardArtifact.class); + + if (nodeArtifact == null) { + try { + SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + AbstractFile file = node.getLookup().lookup(AbstractFile.class); + if (file != null) { + List artifactsList = tskCase.getBlackboardArtifacts(TSK_ASSOCIATED_OBJECT, file.getId()); + + for (BlackboardArtifact fileArtifact : artifactsList) { + BlackboardAttribute associatedArtifactAttribute = fileArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); + if (associatedArtifactAttribute != null) { + BlackboardArtifact associatedArtifact = fileArtifact.getSleuthkitCase().getBlackboardArtifact(associatedArtifactAttribute.getValueLong()); + if (isMessageArtifact(associatedArtifact)) { + nodeArtifact = associatedArtifact; + } + } + } + } + } catch (NoCurrentCaseException | TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Failed to get file for selected node.", ex); //NON-NLS + } + } + + return nodeArtifact; + } + @Override public int isPreferred(Node node) { + // For message artifacts this is a high priority viewer, + // but for attachment files, this a lower priority vewer. if (isSupported(node)) { - return 7; + BlackboardArtifact nodeArtifact = node.getLookup().lookup(BlackboardArtifact.class); + if (nodeArtifact != null) { + return 7; + } else { + return 1; + } } return 0; } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties new file mode 100755 index 0000000000..efe92bd0f6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties @@ -0,0 +1,4 @@ +ContextViewer.jSourceGoToResultButton.text=Go to Result +ContextViewer.jSourceTextLabel.text=jLabel2 +ContextViewer.jSourceNameLabel.text=jSourceNameLabel +ContextViewer.jSourceLabel.text=Source diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties-MERGED new file mode 100755 index 0000000000..79f4f61bfa --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties-MERGED @@ -0,0 +1,15 @@ +ContextViewer.attachmentSource=Attached to: +ContextViewer.downloadedOn=On +ContextViewer.downloadSource=Downloaded from: +ContextViewer.downloadURL=URL +ContextViewer.email=Email +ContextViewer.jSourceGoToResultButton.text=Go to Result +ContextViewer.jSourceTextLabel.text=jLabel2 +ContextViewer.jSourceNameLabel.text=jSourceNameLabel +ContextViewer.jSourceLabel.text=Source +ContextViewer.message=Message +ContextViewer.messageFrom=From +ContextViewer.messageOn=On +ContextViewer.messageTo=To +ContextViewer.title=Context +ContextViewer.toolTip=Displays context for selected file. diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.form similarity index 82% rename from Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form rename to Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.form index e1f171ad36..f7e70919c0 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.form @@ -65,16 +65,12 @@ - + - - - - @@ -82,25 +78,21 @@ - + - - - - - + - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.java similarity index 94% rename from Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java rename to Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.java index 212321e178..2e183755f1 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.contentviewers; +package org.sleuthkit.autopsy.contentviewers.contextviewer; import java.awt.Component; import java.util.ArrayList; @@ -79,8 +79,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte // //GEN-BEGIN:initComponents private void initComponents() { - javax.swing.JButton jSourceGoToResultButton = new javax.swing.JButton(); - javax.swing.JLabel jSourceLabel = new javax.swing.JLabel(); + jSourceGoToResultButton = new javax.swing.JButton(); + jSourceLabel = new javax.swing.JLabel(); jSourceNameLabel = new javax.swing.JLabel(); jSourceTextLabel = new javax.swing.JLabel(); @@ -165,7 +165,7 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte "ContextViewer.title=Context", "ContextViewer.toolTip=Displays context for selected file." }) - + @Override public String getTitle() { return Bundle.ContextViewer_title(); @@ -188,6 +188,7 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte @Override public void resetComponent() { + jSourceGoToResultButton.setVisible(false); setSourceName(""); setSourceText(""); } @@ -238,23 +239,23 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte boolean foundASource = false; for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SOURCE_CONTEXT_ARTIFACTS) { List artifactsList = tskCase.getBlackboardArtifacts(artifactType, sourceFile.getId()); - + foundASource = !artifactsList.isEmpty(); for (BlackboardArtifact contextArtifact : artifactsList) { addSourceEntry(contextArtifact); } } + jSourceGoToResultButton.setVisible(true); if (foundASource == false) { setSourceName("Unknown"); showSourceText(false); } + } - - /** - * Adds a source context entry for the selected file based on the given context - * providing artifact. + * Adds a source context entry for the selected file based on the given + * context providing artifact. * * @param artifact Artifact that may provide context. * @@ -315,15 +316,17 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte /** * Sets the source text string. * - * @param nameLabel String value for source text. + * @param text String value for source text. */ private void setSourceText(String text) { jSourceTextLabel.setText(text); - showSourceText(true); + showSourceText(!text.isEmpty()); } - private void showSourceText(boolean isVisible) { - jSourceTextLabel.setVisible(isVisible); + private void showSourceText(boolean show) { + jSourceTextLabel.setVisible(show); + jSourceGoToResultButton.setEnabled(show); + jSourceLabel.setVisible(show); } /** @@ -366,8 +369,7 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte "ContextViewer.email=Email", "ContextViewer.messageFrom=From", "ContextViewer.messageTo=To", - "ContextViewer.messageOn=On", - }) + "ContextViewer.messageOn=On",}) private String msgArtifactToAbbreviatedString(BlackboardArtifact artifact) throws TskCoreException { StringBuilder sb = new StringBuilder(ARTIFACT_STR_MAX_LEN); @@ -391,11 +393,11 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte * Looks up specified attribute in the given map and, if found, appends its * value to the given string builder. * - * @param sb String builder to append to. - * @param attribType Attribute type to look for. + * @param sb String builder to append to. + * @param attribType Attribute type to look for. * @param attributesMap Attributes map. - * @param prependStr Optional string that is prepended before the attribute - * value. + * @param prependStr Optional string that is prepended before the + * attribute value. */ private void appendAttributeString(StringBuilder sb, BlackboardAttribute.ATTRIBUTE_TYPE attribType, Map attributesMap, String prependStr) { @@ -436,6 +438,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton jSourceGoToResultButton; + private javax.swing.JLabel jSourceLabel; private javax.swing.JLabel jSourceNameLabel; private javax.swing.JLabel jSourceTextLabel; // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/images/Fast-forward-01.png b/Core/src/org/sleuthkit/autopsy/contentviewers/images/Fast-forward-01.png new file mode 100755 index 0000000000..8261b2962a Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/contentviewers/images/Fast-forward-01.png differ diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/images/Fast-rewind-01.png b/Core/src/org/sleuthkit/autopsy/contentviewers/images/Fast-rewind-01.png new file mode 100755 index 0000000000..a941bae845 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/contentviewers/images/Fast-rewind-01.png differ diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/images/Pause-01.png b/Core/src/org/sleuthkit/autopsy/contentviewers/images/Pause-01.png new file mode 100755 index 0000000000..8f3db42f5b Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/contentviewers/images/Pause-01.png differ diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/images/Play-arrow-01.png b/Core/src/org/sleuthkit/autopsy/contentviewers/images/Play-arrow-01.png new file mode 100755 index 0000000000..624c1841d8 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/contentviewers/images/Play-arrow-01.png differ diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java index 9d464c2585..8ff89d111e 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java @@ -610,7 +610,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat * display string. * * @param element JSON element to convert - * @param indentStr Starting indentation for the element. + * @param startIndent Starting indentation for the element. * * @return A multi-line display string. */ @@ -634,7 +634,8 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat /** * Converts the given JSON element into string and appends to the given string builder. * - * @param entry JSON entry to parse + * @param jsonKey + * @param jsonElement * @param startIndent Starting indentation for the element. * @param sb String builder to append to. */ diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index ae4211741a..9ac85bd1e7 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -304,9 +304,6 @@ OpenReportAction.actionPerformed.ReportFileOpenPermissionDeniedMessage=Permissio PoolNode.createSheet.name.desc=no description PoolNode.createSheet.name.displayName=Name PoolNode.createSheet.name.name=Name -PoolNode.createSheet.offset.desc=no description -PoolNode.createSheet.offset.displayName=Starting offset -PoolNode.createSheet.offset.name=Starting offset PoolNode.createSheet.type.desc=no description PoolNode.createSheet.type.displayName=Type PoolNode.createSheet.type.name=Type diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index d97e980a8b..9de0e95462 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -227,6 +227,7 @@ public class ExtractedContent implements AutopsyVisitableItem { // maps the artifact type to its child node private final HashMap typeNodeList = new HashMap<>(); + @SuppressWarnings("deprecation") TypeFactory() { super(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java index ce5948ce68..ca73a64468 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/SpecialDirectoryNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017-2018 Basis Technology Corp. + * Copyright 2017-2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.List; import javax.swing.Action; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction; import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.directorytree.ExportCSVAction; @@ -37,7 +38,7 @@ import org.sleuthkit.datamodel.SpecialDirectory; * Parent class for special directory types (Local and Virtual) */ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode { - + public SpecialDirectoryNode(SpecialDirectory sd) { super(sd); } @@ -68,6 +69,7 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNodesingletonList(content))); + actions.add(new DeleteDataSourceAction(content.getId())); } else { actions.add(new RunIngestModulesAction(content)); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYMessagesFileParser.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYMessagesFileParser.java index 74242ddca5..48b542615f 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYMessagesFileParser.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYMessagesFileParser.java @@ -106,7 +106,7 @@ final class XRYMessagesFileParser implements XRYFileParser { /** * Indicates if the display name of the XRY key is a recognized type. * - * @param xryKey + * @param name * @return */ public static boolean contains(String name) { @@ -125,7 +125,7 @@ final class XRYMessagesFileParser implements XRYFileParser { * IllegalArgumentException is thrown. Test all membership with * contains() before hand. * - * @param xryKey + * @param name * @return */ public static XryKey fromDisplayName(String name) { @@ -217,7 +217,7 @@ final class XRYMessagesFileParser implements XRYFileParser { /** * Indicates if the display name of the XRY key is a recognized type. * - * @param xryKey + * @param name * @return */ public static boolean contains(String name) { @@ -236,7 +236,7 @@ final class XRYMessagesFileParser implements XRYFileParser { * IllegalArgumentException is thrown. Test all membership with * contains() before hand. * - * @param xryKey + * @param name * @return */ public static XryMetaKey fromDisplayName(String name) { diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index bb43f64c84..522d3836c3 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -41,7 +41,6 @@ import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType; import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory; @@ -259,15 +258,20 @@ public class DataResultFilterNode extends FilterNode { @Override protected Node[] createNodes(Node key) { - // filter out all non-message artifacts, if displaying the results from the Data Source tree + // if displaying the results from the Data Source tree + // filter out artifacts + + // In older versions of Autopsy, attachments were children of email/message artifacts + // and hence email/messages with attachments are shown in the tree data source tree, BlackboardArtifact art = key.getLookup().lookup(BlackboardArtifact.class); - if (art != null - && filterArtifacts - && art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() - && art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) { + if (art != null && filterArtifacts + && ((FilterNodeUtils.showMessagesInDatasourceTree() == false) + || (FilterNodeUtils.showMessagesInDatasourceTree() + && art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() + && art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()))) { return new Node[]{}; } - + return new Node[]{new DataResultFilterNode(key, sourceEm)}; } } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java index c9070015f2..06a0b18c10 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.directorytree; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.logging.Level; import javax.swing.Action; @@ -33,17 +32,12 @@ import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.AbstractContentNode; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; -import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.Directory; -import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; -import org.sleuthkit.datamodel.VirtualDirectory; -import org.sleuthkit.datamodel.Volume; /** * A node filter (decorator) that sets the actions for a node in the tree view @@ -137,11 +131,18 @@ class DirectoryTreeFilterNode extends FilterNode { numVisibleChildren--; } } else if (child instanceof BlackboardArtifact) { - BlackboardArtifact bba = (BlackboardArtifact) child; - - // Only message type artifacts are displayed in the tree - if ((bba.getArtifactTypeID() != ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) - && (bba.getArtifactTypeID() != ARTIFACT_TYPE.TSK_MESSAGE.getTypeID())) { + + if (FilterNodeUtils.showMessagesInDatasourceTree()) { + // In older versions of Autopsy, attachments were children of email/message artifacts + // and hence email/messages with attachments are shown in the directory tree. + BlackboardArtifact bba = (BlackboardArtifact) child; + // Only message type artifacts are displayed in the tree + if ((bba.getArtifactTypeID() != ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) + && (bba.getArtifactTypeID() != ARTIFACT_TYPE.TSK_MESSAGE.getTypeID())) { + numVisibleChildren--; + } + } + else { numVisibleChildren--; } } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/FilterNodeUtils.java b/Core/src/org/sleuthkit/autopsy/directorytree/FilterNodeUtils.java new file mode 100644 index 0000000000..ce8344862c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/directorytree/FilterNodeUtils.java @@ -0,0 +1,66 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.directorytree; + +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber; + +/** + * Utility class for Directory tree. + * + */ +final class FilterNodeUtils { + + private static final int ATTACHMENT_CHILDOF_MSG_MAX_DB_MAJOR_VER = 8; + private static final int ATTACHMENT_CHILDOF_MSG_MAX_DB_MINOR_VER = 4; + + /** + * Empty private constructor + */ + private FilterNodeUtils() { + + } + + /** + * Prior to schema version 8.4, attachments were children of messages and + * hence messages with any attachment children are shown in the directory + * tree. + * + * At 8.4 and later, attachments are tracked as an attribute, and the message + * artifacts don't need to be shown in the directory tree. + * + * This method may be used to check the schema version and behave + * accordingly, in order to maintain backward compatibility. + * + * @return True if messages with attachment children should be shown in + * directory tree. + */ + static boolean showMessagesInDatasourceTree() { + boolean showMessagesInDatasourceTree = true; + if (Case.isCaseOpen()) { + CaseDbSchemaVersionNumber version = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion(); + showMessagesInDatasourceTree + = ((version.getMajor() < ATTACHMENT_CHILDOF_MSG_MAX_DB_MAJOR_VER) + || (version.getMajor() == ATTACHMENT_CHILDOF_MSG_MAX_DB_MAJOR_VER && version.getMinor() < ATTACHMENT_CHILDOF_MSG_MAX_DB_MINOR_VER)); + } + return showMessagesInDatasourceTree; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/AutopsyExceptionHandler.java b/Core/src/org/sleuthkit/autopsy/exceptions/AutopsyExceptionHandler.java similarity index 96% rename from Core/src/org/sleuthkit/autopsy/coreutils/AutopsyExceptionHandler.java rename to Core/src/org/sleuthkit/autopsy/exceptions/AutopsyExceptionHandler.java index 3b7c333bc3..13b73583a2 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/AutopsyExceptionHandler.java +++ b/Core/src/org/sleuthkit/autopsy/exceptions/AutopsyExceptionHandler.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.coreutils; +package org.sleuthkit.autopsy.exceptions; import java.util.logging.Filter; import java.util.logging.Handler; @@ -26,6 +26,9 @@ import java.util.logging.SimpleFormatter; import javax.swing.JOptionPane; import org.openide.util.lookup.ServiceProvider; import org.netbeans.core.NbErrorManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.coreutils.Version; /** * Replaces default NetBeans exception handler. Displays messages in a dialog. diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties index 42c8538d93..9a408d5c24 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties @@ -44,7 +44,7 @@ FileSearchPanel.hashSetCheckbox.text=Hash Set: FileSearchPanel.tagsCheckbox.text=Tag: FileSearchPanel.interestingItemsCheckbox.text=Interesting Item: FileSearchPanel.scoreCheckbox.text=Has Score: -FileSearchPanel.exifCheckbox.text=Must contain EXIF data +FileSearchPanel.exifCheckbox.text=Possibly User Created FileSearchPanel.notableCheckbox.text=Must have been tagged as notable FileSearchPanel.objectsCheckbox.text=Object Detected: ResultsPanel.currentPageLabel.text=Page: - diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED index 6626742f46..a36a61b825 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED @@ -150,7 +150,7 @@ FileSearchPanel.hashSetCheckbox.text=Hash Set: FileSearchPanel.tagsCheckbox.text=Tag: FileSearchPanel.interestingItemsCheckbox.text=Interesting Item: FileSearchPanel.scoreCheckbox.text=Has Score: -FileSearchPanel.exifCheckbox.text=Must contain EXIF data +FileSearchPanel.exifCheckbox.text=Possibly User Created FileSearchPanel.notableCheckbox.text=Must have been tagged as notable FileSearchPanel.objectsCheckbox.text=Object Detected: FileSorter.SortingMethod.datasource.displayName=Data Source diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java index 93d17b21bb..a260d2066f 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchData.java @@ -47,7 +47,7 @@ final class FileSearchData { UNIQUE(0, 1, Bundle.FileSearchData_Frequency_unique_displayName()), RARE(1, 10, Bundle.FileSearchData_Frequency_rare_displayName()), COMMON(2, 100, Bundle.FileSearchData_Frequency_common_displayName()), - VERY_COMMON(3, 0, Bundle.FileSearchData_Frequency_common_displayName()), + VERY_COMMON(3, 0, Bundle.FileSearchData_Frequency_verycommon_displayName()), KNOWN(4, 0, Bundle.FileSearchData_Frequency_known_displayName()), UNKNOWN(5, 0, Bundle.FileSearchData_Frequency_unknown_displayName()); diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java index b35434be0e..7c558ef592 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchFiltering.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2019-2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,21 +22,17 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeIns import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.filequery.FileSearchData.FileSize; import org.sleuthkit.autopsy.filequery.FileSearchData.FileType; import org.sleuthkit.autopsy.filequery.FileSearchData.Frequency; import org.sleuthkit.autopsy.filequery.FileSearchData.Score; - import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; - import java.util.ArrayList; import java.util.List; -import java.util.logging.Level; import java.util.stream.Collectors; import org.openide.util.NbBundle; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -48,8 +44,6 @@ import org.sleuthkit.datamodel.TskData; */ class FileSearchFiltering { - private final static Logger logger = Logger.getLogger(FileSearchFiltering.class.getName()); - /** * Run the given filters to get a list of matching files. * @@ -61,18 +55,9 @@ class FileSearchFiltering { * @return */ static List runQueries(List filters, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { - if (caseDb == null) { throw new FileSearchException("Case DB parameter is null"); // NON-NLS } - - // Record the selected filters - String filterStr = ""; - for (FileFilter filter : filters) { - filterStr += " " + filter.getDesc() + "\n"; - } - logger.log(Level.INFO, "Running filters:\n{0}", filterStr); - // Combine all the SQL queries from the filters into one query String combinedQuery = ""; for (FileFilter filter : filters) { @@ -112,8 +97,6 @@ class FileSearchFiltering { private static List getResultList(List filters, String combinedQuery, SleuthkitCase caseDb, EamDb centralRepoDb) throws TskCoreException, FileSearchException { // Get all matching abstract files List resultList = new ArrayList<>(); - - logger.log(Level.INFO, "Running SQL query: {0}", combinedQuery); List sqlResults = caseDb.findAllFilesWhere(combinedQuery); // If there are no results, return now diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form index c8a68294e2..ead4b9f264 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.form @@ -404,15 +404,6 @@ - - - - - - - - - @@ -457,12 +448,6 @@ - - - - - - @@ -573,12 +558,6 @@ - - - - - - @@ -631,12 +610,6 @@ - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java index d751911379..f81d3d1945 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchPanel.java @@ -1328,9 +1328,6 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener parentList.setModel(new DefaultListModel()); parentList.setEnabled(false); - parentList.setMaximumSize(null); - parentList.setMinimumSize(new java.awt.Dimension(0, 30)); - parentList.setPreferredSize(new java.awt.Dimension(0, 30)); parentList.setVisibleRowCount(4); parentList.addListSelectionListener(new javax.swing.event.ListSelectionListener() { public void valueChanged(javax.swing.event.ListSelectionEvent evt) { @@ -1366,8 +1363,6 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener hashSetList.setModel(new DefaultListModel()); hashSetList.setEnabled(false); - hashSetList.setMinimumSize(new java.awt.Dimension(0, 30)); - hashSetList.setPreferredSize(new java.awt.Dimension(0, 30)); hashSetList.setVisibleRowCount(3); hashSetScrollPane.setViewportView(hashSetList); @@ -1454,8 +1449,6 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener objectsList.setModel(new DefaultListModel()); objectsList.setEnabled(false); - objectsList.setMinimumSize(new java.awt.Dimension(0, 30)); - objectsList.setPreferredSize(new java.awt.Dimension(0, 30)); objectsList.setVisibleRowCount(2); objectsScrollPane.setViewportView(objectsList); @@ -1487,8 +1480,6 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener interestingItemsList.setModel(new DefaultListModel()); interestingItemsList.setEnabled(false); - interestingItemsList.setMinimumSize(new java.awt.Dimension(0, 30)); - interestingItemsList.setPreferredSize(new java.awt.Dimension(0, 30)); interestingItemsList.setVisibleRowCount(2); interestingItemsScrollPane.setViewportView(interestingItemsList); diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.form index c21dca33e5..c7d9f2b4ee 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.form @@ -290,11 +290,11 @@ - + - + @@ -331,9 +331,6 @@ - - - diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java index 3e4f8b7a68..eaeb0b3c06 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java @@ -146,14 +146,22 @@ public class ResultsPanel extends javax.swing.JPanel { */ synchronized void populateInstancesList() { SwingUtilities.invokeLater(() -> { - instancesList.removeListSelectionListener(listener); - instancesListModel.removeAllElements(); - for (AbstractFile file : getInstancesForSelected()) { - instancesListModel.addElement(file); - } - instancesList.addListSelectionListener(listener); - if (!instancesListModel.isEmpty()) { - instancesList.setSelectedIndex(0); + List files = getInstancesForSelected(); + if (files.isEmpty()) { + //if there are no files currently remove the current items without removing listener to cause content viewer to reset + instancesListModel.removeAllElements(); + } else { + //remove listener so content viewer node is not set multiple times + instancesList.removeListSelectionListener(listener); + instancesListModel.removeAllElements(); + for (AbstractFile file : files) { + instancesListModel.addElement(file); + } + //add listener back to allow selection of first index to cause content viewer node to be set + instancesList.addListSelectionListener(listener); + if (!instancesListModel.isEmpty()) { + instancesList.setSelectedIndex(0); + } } }); } @@ -488,7 +496,6 @@ public class ResultsPanel extends javax.swing.JPanel { instancesList.setModel(instancesListModel); instancesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); instancesList.setCellRenderer(new InstancesCellRenderer()); - instancesList.setPreferredSize(new java.awt.Dimension(0, 50)); instancesList.setVisibleRowCount(2); instancesScrollPane.setViewportView(instancesList); @@ -502,11 +509,11 @@ public class ResultsPanel extends javax.swing.JPanel { ); instancesPanelLayout.setVerticalGroup( instancesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 221, Short.MAX_VALUE) + .addGap(0, 68, Short.MAX_VALUE) .addGroup(instancesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, instancesPanelLayout.createSequentialGroup() .addGap(0, 0, 0) - .addComponent(instancesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 221, Short.MAX_VALUE))) + .addComponent(instancesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) ); resultsSplitPane.setRightComponent(instancesPanel); diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java index b1dee1cbb4..d06f35c272 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java @@ -85,6 +85,8 @@ public final class GeolocationTopComponent extends TopComponent { // This is the hardcoded report name from KMLReport.java private static final String REPORT_KML = "ReportKML.kml"; + + private boolean mapInitalized = false; @Messages({ "GLTopComponent_name=Geolocation", @@ -194,20 +196,26 @@ public final class GeolocationTopComponent extends TopComponent { @Override public void open() { super.open(); + mapPanel.clearWaypoints(); geoFilterPanel.clearDataSourceList(); geoFilterPanel.updateDataSourceList(); - try { - mapPanel.initMap(); - } catch (GeoLocationDataException ex) { - JOptionPane.showMessageDialog(this, - Bundle.GeolocationTC_connection_failure_message(), - Bundle.GeolocationTC_connection_failure_message_title(), - JOptionPane.ERROR_MESSAGE); - MessageNotifyUtil.Notify.error( - Bundle.GeolocationTC_connection_failure_message_title(), - Bundle.GeolocationTC_connection_failure_message()); - logger.log(Level.SEVERE, ex.getMessage(), ex); - return; // Doen't set the waypoints. + + // Let's make sure we only do this on the first open + if (!mapInitalized) { + try { + mapPanel.initMap(); + mapInitalized = true; + } catch (GeoLocationDataException ex) { + JOptionPane.showMessageDialog(this, + Bundle.GeolocationTC_connection_failure_message(), + Bundle.GeolocationTC_connection_failure_message_title(), + JOptionPane.ERROR_MESSAGE); + MessageNotifyUtil.Notify.error( + Bundle.GeolocationTC_connection_failure_message_title(), + Bundle.GeolocationTC_connection_failure_message()); + logger.log(Level.SEVERE, ex.getMessage(), ex); + return; // Doen't set the waypoints. + } } mapPanel.setWaypoints(new ArrayList<>()); updateWaypoints(); @@ -288,7 +296,7 @@ public final class GeolocationTopComponent extends TopComponent { DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss", Locale.US); Date date = new Date(); String dateNoTime = dateFormat.format(date); - String reportPath = String.format(REPORT_PATH_FMT_STR, currentCase.getReportDirectory(), currentCase.getDisplayName(), "Goggle Earth KML", dateNoTime); + String reportPath = String.format(REPORT_PATH_FMT_STR, currentCase.getReportDirectory(), currentCase.getDisplayName(), "Google Earth KML", dateNoTime); // Create the root reports directory. try { FileUtil.createFolder(new File(reportPath)); @@ -431,6 +439,8 @@ public final class GeolocationTopComponent extends TopComponent { Bundle.GeoTopComponent_filter_exception_Title(), Bundle.GeoTopComponent_filter_exception_msg(), JOptionPane.ERROR_MESSAGE); + + setWaypointLoading(false); } }); } @@ -444,7 +454,7 @@ public final class GeolocationTopComponent extends TopComponent { private class WaypointCallBack implements WaypointFilterQueryCallBack { @Override - public void process(List waypoints) { + public void process(final List waypoints) { // Make sure that the waypoints are added to the map panel in // the correct thread. SwingUtilities.invokeLater(new Runnable() { @@ -453,13 +463,16 @@ public final class GeolocationTopComponent extends TopComponent { // If the list is empty, tell the user and do not change // the visible waypoints. if (waypoints == null || waypoints.isEmpty()) { + mapPanel.clearWaypoints(); JOptionPane.showMessageDialog(GeolocationTopComponent.this, Bundle.GeoTopComponent_no_waypoints_returned_Title(), Bundle.GeoTopComponent_no_waypoints_returned_mgs(), JOptionPane.INFORMATION_MESSAGE); - + setWaypointLoading(false); + geoFilterPanel.setEnabled(true); return; } + mapPanel.clearWaypoints(); mapPanel.setWaypoints(MapWaypoint.getWaypoints(waypoints)); setWaypointLoading(false); geoFilterPanel.setEnabled(true); diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/KdTree.java b/Core/src/org/sleuthkit/autopsy/geolocation/KdTree.java index 656abc9489..3a2f305083 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/KdTree.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/KdTree.java @@ -417,8 +417,6 @@ public class KdTree implements Iterable { } Double nodeDistance = node.id.euclideanDistance(value); if (nodeDistance.compareTo(lastDistance) < 0) { - if (results.size() == K && lastNode != null) - results.remove(lastNode); results.add(node); } else if (nodeDistance.equals(lastDistance)) { results.add(node); diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTileFactory.java b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTileFactory.java index 372d2e1561..04065f3057 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTileFactory.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MBTilesTileFactory.java @@ -256,7 +256,7 @@ final class MBTilesTileFactory extends TileFactory { /** * An inner class which actually loads the tiles. Used by the thread queue. - * Subclasses can override this via {@link #createTileRunner(Tile)} if + * Subclasses can override this via createTileRunner(Tile) if * necessary. */ private class TileRunner implements Runnable { diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanMouseInputListener.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanMouseInputListener.java new file mode 100755 index 0000000000..9f75b13d2a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanMouseInputListener.java @@ -0,0 +1,115 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation; + +import java.awt.Cursor; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import javax.swing.SwingUtilities; +import javax.swing.event.MouseInputAdapter; +import org.jxmapviewer.JXMapViewer; + +/** + * MouseInputListener for panning a JXMapViewer + * + * This class is adapted from org.jxmapviewer.input.PanMouseInputListener. + */ +final class MapPanMouseInputListener extends MouseInputAdapter { + + private Point prev; + private final JXMapViewer viewer; + private Cursor priorCursor; + private boolean dragging = false; + + /** + * Construct a new listener. + * + * @param viewer + */ + MapPanMouseInputListener(JXMapViewer viewer) { + this.viewer = viewer; + } + + @Override + public void mousePressed(MouseEvent evt) { + if (!SwingUtilities.isLeftMouseButton(evt)) { + return; + } + if (!viewer.isPanningEnabled()) { + return; + } + + // Store the current click point and current cursor + prev = evt.getPoint(); + priorCursor = viewer.getCursor(); + } + + @Override + public void mouseDragged(MouseEvent evt) { + if (!SwingUtilities.isLeftMouseButton(evt)) { + return; + } + + if (!viewer.isPanningEnabled()) { + return; + } + + // If the map wasn't previously being dragged, set the cursor + if (!dragging) { + viewer.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + dragging = true; + } + + // Figure out the new map center + Point current = evt.getPoint(); + double x = viewer.getCenter().getX(); + double y = viewer.getCenter().getY(); + + if (prev != null) { + x += prev.x - current.x; + y += prev.y - current.y; + } + + int maxHeight = (int) (viewer.getTileFactory().getMapSize(viewer.getZoom()).getHeight() * viewer + .getTileFactory().getTileSize(viewer.getZoom())); + if (y > maxHeight) { + y = maxHeight; + } + + prev = current; + viewer.setCenter(new Point2D.Double(x, y)); + viewer.repaint(); + } + + @Override + public void mouseReleased(MouseEvent evt) { + if (!SwingUtilities.isLeftMouseButton(evt)) { + return; + } + + prev = null; + + // If we were dragging set the cursor back + if (dragging) { + viewer.setCursor(priorCursor); + dragging = false; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java index 7be6ac25a8..7645662e28 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -26,7 +26,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; -import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; @@ -54,7 +53,6 @@ import org.jxmapviewer.JXMapViewer; import org.jxmapviewer.OSMTileFactoryInfo; import org.jxmapviewer.VirtualEarthTileFactoryInfo; import org.jxmapviewer.input.CenterMapListener; -import org.jxmapviewer.input.PanMouseInputListener; import org.jxmapviewer.input.ZoomMouseWheelListenerCursor; import org.jxmapviewer.viewer.DefaultTileFactory; import org.jxmapviewer.viewer.GeoPosition; @@ -70,6 +68,7 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; import org.sleuthkit.datamodel.TskCoreException; import javax.imageio.ImageIO; +import javax.swing.SwingUtilities; import org.jxmapviewer.viewer.DefaultWaypointRenderer; /** @@ -139,6 +138,8 @@ final public class MapPanel extends javax.swing.JPanel { } } }); + + } /** @@ -171,7 +172,7 @@ final public class MapPanel extends javax.swing.JPanel { mapViewer.setTileFactory(tileFactory); // Add Mouse interactions - MouseInputListener mia = new PanMouseInputListener(mapViewer); + MouseInputListener mia = new MapPanMouseInputListener(mapViewer); mapViewer.addMouseListener(mia); mapViewer.addMouseMotionListener(mia); @@ -204,13 +205,12 @@ final public class MapPanel extends javax.swing.JPanel { Iterator iterator = waypointTree.iterator(); while (iterator.hasNext()) { MapWaypoint point = iterator.next(); - if (point != currentlySelectedWaypoint) { - set.add(point); - } + set.add(point); } // Add the currentlySelectedWaypoint to the end so that // it will be painted last. if (currentlySelectedWaypoint != null) { + set.remove(currentlySelectedWaypoint); set.add(currentlySelectedWaypoint); } } @@ -279,7 +279,7 @@ final public class MapPanel extends javax.swing.JPanel { /** * Create the TileFactoryInfo for OSM zip File * - * @param zipPath Path to zip file. + * @param path Path to zip file. * * @return TileFactoryInfo for zip file. * @@ -332,6 +332,11 @@ final public class MapPanel extends javax.swing.JPanel { */ void clearWaypoints() { waypointTree = null; + currentlySelectedWaypoint = null; + if (currentPopup != null) { + currentPopup.hide(); + } + mapViewer.repaint(); } /** @@ -342,7 +347,11 @@ final public class MapPanel extends javax.swing.JPanel { */ private void showPopupMenu(Point point) { try { - MapWaypoint waypoint = findClosestWaypoint(point); + List waypoints = findClosestWaypoint(point); + MapWaypoint waypoint = null; + if(waypoints.size() > 0) { + waypoint = waypoints.get(0); + } showPopupMenu(waypoint, point); // Change the details popup to the currently selected point only if // it the popup is currently visible @@ -410,6 +419,7 @@ final public class MapPanel extends javax.swing.JPanel { currentPopup = popupFactory.getPopup(this, detailPane, popupLocation.x, popupLocation.y); currentPopup.show(); + mapViewer.revalidate(); mapViewer.repaint(); } } @@ -437,7 +447,7 @@ final public class MapPanel extends javax.swing.JPanel { * @return A waypoint that is within 10 pixels of the given point, or null * if none was found. */ - private MapWaypoint findClosestWaypoint(Point mouseClickPoint) { + private List findClosestWaypoint(Point mouseClickPoint) { if (waypointTree == null) { return null; } @@ -446,7 +456,7 @@ final public class MapPanel extends javax.swing.JPanel { GeoPosition geopos = mapViewer.getTileFactory().pixelToGeo(mouseClickPoint, mapViewer.getZoom()); // Get the 5 nearest neightbors to the point - Collection waypoints = waypointTree.nearestNeighbourSearch(20, MapWaypoint.getDummyWaypoint(geopos)); + Collection waypoints = waypointTree.nearestNeighbourSearch(10, MapWaypoint.getDummyWaypoint(geopos)); if (waypoints == null || waypoints.isEmpty()) { return null; @@ -456,6 +466,7 @@ final public class MapPanel extends javax.swing.JPanel { // These maybe the points closest to lat/log was clicked but // that doesn't mean they are close in terms of pixles. + List closestPoints = new ArrayList<>(); while (iterator.hasNext()) { MapWaypoint nextWaypoint = iterator.next(); @@ -466,11 +477,11 @@ final public class MapPanel extends javax.swing.JPanel { (int) point.getY() - rect.y); if (converted_gp_pt.distance(mouseClickPoint) < 10) { - return nextWaypoint; + closestPoints.add(nextWaypoint); } } - return null; + return closestPoints; } /** @@ -519,7 +530,7 @@ final public class MapPanel extends javax.swing.JPanel { } /** - * Called when the resize event has completed\timed out + * Called when the resize event has completed or timed out */ public abstract void resizeTimedOut(); } @@ -629,8 +640,14 @@ final public class MapPanel extends javax.swing.JPanel { }//GEN-LAST:event_mapViewerMouseMoved private void mapViewerMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseClicked - if(!evt.isPopupTrigger() && (evt.getButton() == MouseEvent.BUTTON1)) { - currentlySelectedWaypoint = findClosestWaypoint(evt.getPoint()); + if(!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) { + List waypoints = findClosestWaypoint(evt.getPoint()); + if(waypoints.size() > 0) { + currentlySelectedWaypoint = waypoints.get(0); + } + + +// currentlySelectedWaypoint = findClosestWaypoint(evt.getPoint()); showDetailsPopup(); } }//GEN-LAST:event_mapViewerMouseClicked diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/CustomArtifactWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/CustomArtifactWaypoint.java new file mode 100755 index 0000000000..bc3480ea40 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/CustomArtifactWaypoint.java @@ -0,0 +1,79 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.geolocation.datamodel; + +import java.util.Map; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; + +/** + * Class wraps any artifact that is not one of the known types, but have the + * TSK_GEO_LONGITUDE and TSK_GEO_LATITUDE attributes. + * + */ +final class CustomArtifactWaypoint extends Waypoint { + + /** + * Constructs a new waypoint from the given artifact. + * + * @param artifact BlackboardArtifact for this waypoint + * + * @throws GeoLocationDataException + */ + CustomArtifactWaypoint(BlackboardArtifact artifact) throws GeoLocationDataException { + this(artifact, getAttributesFromArtifactAsMap(artifact)); + } + + /** + * Constructs a new CustomArtifactWaypoint. + * + * @param artifact BlackboardArtifact for this waypoint + * @param attributeMap A Map of the BlackboardAttributes for the given + * artifact. + * + * @throws GeoLocationDataException + */ + private CustomArtifactWaypoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { + super(artifact, + getLabelFromArtifact(attributeMap), + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME).getValueLong() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, + null, attributeMap, null); + } + + /** + * Gets the label for this waypoint. + * + * @param artifact BlackboardArtifact for waypoint + * + * @return Returns a label for the waypoint, or empty string if no label was + * found. + */ + private static String getLabelFromArtifact(Map attributeMap) { + BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); + if (attribute != null) { + return attribute.getDisplayString(); + } + + return ""; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/LastKnownWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/LastKnownWaypoint.java index 7bf85874ff..483dfd4689 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/LastKnownWaypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/LastKnownWaypoint.java @@ -71,7 +71,7 @@ final class LastKnownWaypoint extends Waypoint { "LastKnownWaypoint_Label=Last Known Location",}) private static String getLabelFromArtifact(Map attributeMap) throws GeoLocationDataException { BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); - String label = attribute.getDisplayString(); + String label = attribute != null ? attribute.getDisplayString() : Bundle.LastKnownWaypoint_Label(); if (label == null || label.isEmpty()) { label = Bundle.LastKnownWaypoint_Label(); diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java index 7c608d6f08..acc9c9d86a 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java @@ -125,6 +125,7 @@ public final class Route { /** * Get the route start point. * + * @param artifact * @param attributeMap Map of artifact attributes for this waypoint. * * An exception will be thrown if longitude or latitude is null. diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java index 8cc35555eb..a63087f413 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java @@ -187,7 +187,7 @@ public class Waypoint { /** * Gets the label for this waypoint. * - * @param artifact BlackboardArtifact for waypoint + * @param attributeMap Attributes for waypoint * * @return Returns a label for the waypoint, or empty string if no label was * found. @@ -232,7 +232,7 @@ public class Waypoint { * will not include attributes that the Waypoint interfact has get functions * for. * - * @param artifact Blackboard artifact to get attributes\properties from + * @param attributeMap Attributes for the given artifact * * @return A List of Waypoint.Property objects * diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java index 86539412be..24b14dc81c 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java @@ -56,7 +56,8 @@ public final class WaypointBuilder { final static String GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY = "SELECT blackboard_attributes.artifact_id " + "FROM blackboard_attributes, blackboard_artifacts " - + "WHERE blackboard_attributes.attribute_type_id IN(%d, %d) " + + "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id " + + "AND blackboard_attributes.attribute_type_id IN(%d, %d) " + "AND data_source_obj_id IN (%s)"; //NON-NLS // Select will return the "most recent" timestamp from all waypoings @@ -469,6 +470,18 @@ public final class WaypointBuilder { * @return SQL SELECT statement */ static private String buildQueryForWaypointsWOTimeStamps(List dataSources) { + +// SELECT_WO_TIMESTAMP +// SELECT DISTINCT artifact_id, artifact_type_id +// FROM blackboard_attributes +// WHERE artifact_id NOT IN (%s) +// AND artifact_id IN (%s) + +// GEO_ARTIFACT_QUERY_ID_ONLY +// SELECT artifact_id +// FROM blackboard_attributes +// WHERE attribute_type_id IN (%d, %d) + return String.format(SELECT_WO_TIMESTAMP, String.format(GEO_ARTIFACT_QUERY_ID_ONLY, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), @@ -502,16 +515,26 @@ public final class WaypointBuilder { String mostRecentQuery = ""; if (!showAll && cntDaysFromRecent > 0) { - mostRecentQuery = String.format("AND value_int64 > (%s)", //NON-NLS - String.format(MOST_RECENT_TIME, - cntDaysFromRecent, - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID(), - getWaypointListQuery(dataSources) - )); +// MOST_RECENT_TIME +// SELECT MAX(value_int64) - (%d * 86400) +// FROM blackboard_attributes +// WHERE attribute_type_id IN(%d, %d) +// AND artifact_id +// IN ( %s ) +// + mostRecentQuery = String.format("AND value_int64 > (%s)", //NON-NLS + String.format(MOST_RECENT_TIME, + cntDaysFromRecent, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID(), + getWaypointListQuery(dataSources) + )); } - // This givens us all artifact_ID that have time stamp +// GEO_ARTIFACT_QUERY +// SELECT artifact_id, artifact_type_id +// FROM blackboard_attributes +// WHERE attribute_type_id IN (%d, %d) String query = String.format(GEO_ARTIFACT_QUERY, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID()); @@ -542,6 +565,10 @@ public final class WaypointBuilder { static private String getWaypointListQuery(List dataSources) { if (dataSources == null || dataSources.isEmpty()) { +// GEO_ARTIFACT_QUERY +// SELECT artifact_id, artifact_type_id +// FROM blackboard_attributes +// WHERE attribute_type_id IN (%d, %d) return String.format(GEO_ARTIFACT_QUERY, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID()); @@ -592,8 +619,11 @@ public final class WaypointBuilder { Route route = new Route(artifact); waypoints.addAll(route.getRoute()); break; + case TSK_GPS_LAST_KNOWN_LOCATION: + waypoints.add(new LastKnownWaypoint(artifact)); + break; default: - throw new GeoLocationDataException(String.format("Unable to create waypoint for artifact of type %s", type.toString())); + waypoints.add(new CustomArtifactWaypoint(artifact)); } return waypoints; diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties index 1831874d40..5d1d27b060 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle.properties @@ -16,7 +16,6 @@ EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=Possib EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=Compression ratio is {0}, skipping items in {1}. EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnMsg.zipBomb=Possible ZIP bomb detected: {0} EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb=The archive is {0} levels deep, skipping processing of {1} -EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg=Unknown item path in archive: {0}, will use: {1} EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg=Not enough disk space to unpack archive item: {0}, {1} EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details=The archive item is too large to unpack, skipping unpacking this item. EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg=Error unpacking {0} diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle_ja.properties index 355c94be83..d032b4f3d1 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/Bundle_ja.properties @@ -28,7 +28,6 @@ EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=\u30a2 EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=\u5727\u7e2e\u7387\u306f {0} \u3067\u3059\u3002{1} \u306e\u9805\u76ee\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059\u3002 EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnMsg.zipBomb=ZIP\u7206\u5f3e\u304c\u691c\u51fa\u3055\u308c\u305f\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059: {0} EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb=\u30a2\u30fc\u30ab\u30a4\u30d6\u306f {0} \u30ec\u30d9\u30eb\u306e\u6df1\u3055\u3067\u3059\u3002{1} \u306e\u51e6\u7406\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059 -EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg=\u30a2\u30fc\u30ab\u30a4\u30d6\u306e\u4e0d\u660e\u306a\u9805\u76ee\u30d1\u30b9: {0}\u3001\u6b21\u3092\u4f7f\u7528\u3057\u307e\u3059: {1} EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg=\u30a2\u30fc\u30ab\u30a4\u30d6\u9805\u76ee\u3092\u958b\u5c01\u3059\u308b\u305f\u3081\u306e\u5341\u5206\u306a\u30c7\u30a3\u30b9\u30af\u9818\u57df\u304c\u3042\u308a\u307e\u305b\u3093: {0}\u3001{1} EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details=\u30a2\u30fc\u30ab\u30a4\u30d6\u9805\u76ee\u304c\u5927\u304d\u3059\u304e\u3067\u958b\u5c01\u3067\u304d\u307e\u305b\u3093\u3002\u3053\u306e\u9805\u76ee\u306e\u958b\u5c01\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059\u3002 EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg={0} \u306e\u958b\u5c01\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index 958813a905..fca75c2c56 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -449,10 +449,6 @@ class SevenZipExtractor { } else { pathInArchive = "/" + useName; } - String msg = NbBundle.getMessage(SevenZipExtractor.class, - "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg", - getArchiveFilePath(archiveFile), pathInArchive); - logger.log(Level.WARNING, msg); } return pathInArchive; } @@ -1196,6 +1192,7 @@ class SevenZipExtractor { * * @param parent * @param tokenPath + * @param tokenPathBytes * * @return */ diff --git a/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED index 2fdb54d654..4915d5a124 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/exif/Bundle.properties-MERGED @@ -1,5 +1,6 @@ CannotRunFileTypeDetection=Cannot run file type detection. ExifParserFileIngestModule.indexError.message=Failed to post EXIF Metadata artifact(s). +ExifParserFileIngestModule.userContent.description=EXIF metadata exists for this file. OpenIDE-Module-Display-Category=Ingest Module OpenIDE-Module-Long-Description=Exif metadata ingest module. \n\nThe ingest module analyzes image files, extracts Exif information and posts the Exif data as results. OpenIDE-Module-Name=ExifParser diff --git a/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java index 885468f483..b275a35846 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java @@ -49,6 +49,7 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_USER_CONTENT_SUSPECTED; import org.sleuthkit.datamodel.BlackboardAttribute; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE; @@ -130,6 +131,7 @@ public final class ExifParserFileIngestModule implements FileIngestModule { return processFile(content); } + @Messages({"ExifParserFileIngestModule.userContent.description=EXIF metadata exists for this file."}) private ProcessResult processFile(AbstractFile file) { try (BufferedInputStream bin = new BufferedInputStream(new ReadContentInputStream(file));) { @@ -193,11 +195,13 @@ public final class ExifParserFileIngestModule implements FileIngestModule { // Create artifact if it doesn't already exist. if (!blackboard.artifactExists(file, TSK_METADATA_EXIF, attributes)) { BlackboardArtifact bba = file.newArtifact(TSK_METADATA_EXIF); + BlackboardArtifact bba2 = file.newArtifact(TSK_USER_CONTENT_SUSPECTED); bba.addAttributes(attributes); - + bba2.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION, MODULE_NAME, Bundle.ExifParserFileIngestModule_userContent_description())); try { // index the artifact for keyword search blackboard.postArtifact(bba, MODULE_NAME); + blackboard.postArtifact(bba2, MODULE_NAME); } catch (Blackboard.BlackboardException ex) { logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS MessageNotifyUtil.Notify.error( diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java index 46b60d9b1e..477788df21 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java @@ -254,10 +254,10 @@ public class FileTypeDetector { } else { /* * If the file was marked as an octet stream and the extension is .txt, try to detect a text - * encoding with Decodetect. + * encoding */ if (file.getNameExtension().equals("txt")) { - Charset detectedCharset = TextFileExtractor.getEncoding(file); + Charset detectedCharset = new TextFileExtractor(file).getEncoding(); if (detectedCharset != TextFileExtractor.UNKNOWN_CHARSET) { mimeType = MimeTypes.PLAIN_TEXT; } diff --git a/Core/src/org/sleuthkit/autopsy/modules/plaso/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/plaso/Bundle.properties-MERGED index c823f391fc..b3573b9d5e 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/plaso/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/plaso/Bundle.properties-MERGED @@ -3,7 +3,7 @@ PlasoIngestModule.artifact.progress=Adding events to case: {0} PlasoIngestModule.bad.imageFile=Cannot find image file name and path PlasoIngestModule.completed=Plaso Processing Completed PlasoIngestModule.create.artifacts.cancelled=Cancelled Plaso Artifact Creation -PlasoIngestModule.dataSource.not.an.image=Datasource is not an Image. +PlasoIngestModule.dataSource.not.an.image=Skipping non-disk image datasource PlasoIngestModule.error.creating.output.dir=Error creating Plaso module output directory. PlasoIngestModule.error.running.log2timeline=Error running log2timeline, see log file. PlasoIngestModule.error.running.psort=Error running Psort, see log file. @@ -11,7 +11,7 @@ PlasoIngestModule.event.datetime=Event Date Time PlasoIngestModule.event.description=Event Description PlasoIngestModule.exception.posting.artifact=Exception Posting artifact. PlasoIngestModule.executable.not.found=Plaso Executable Not Found. -PlasoIngestModule.has.run=Plaso Plugin has been run. +PlasoIngestModule.has.run=Plaso PlasoIngestModule.info.empty.database=Plaso database was empty. PlasoIngestModule.log2timeline.cancelled=Log2timeline run was canceled PlasoIngestModule.psort.cancelled=psort run was canceled diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java b/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java index a002a0279d..8db758e53a 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java @@ -117,6 +117,7 @@ public final class KMLReport implements GeneralReportModule { * * @param baseReportDir path to save the report * @param progressPanel panel to update the report's progress + * @param waypointList */ @Messages({ "KMLReport.unableToExtractPhotos=Could not extract photo information.", @@ -524,10 +525,8 @@ public final class KMLReport implements GeneralReportModule { * * @param startLatitude Starting latitude * @param startLongitude Starting longitude - * @param startAltitude Starting altitude. Currently ignored. * @param stopLatitude Ending latitude * @param stopLongitude Ending longitude - * @param stopAltitude Ending altitude. Currently ignored. * * @return the Line as an Element */ diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/TextExtractorFactory.java b/Core/src/org/sleuthkit/autopsy/textextractors/TextExtractorFactory.java index ff0ba51dd1..9bc5af74bd 100755 --- a/Core/src/org/sleuthkit/autopsy/textextractors/TextExtractorFactory.java +++ b/Core/src/org/sleuthkit/autopsy/textextractors/TextExtractorFactory.java @@ -85,14 +85,14 @@ public class TextExtractorFactory { * @param content AbstractFile content * @param context Lookup containing extractor configurations * - * @return + * @return List of all extractors in priority order. Not all will support the passed in content. @@@ PERHAPS ONLY SUPPORTED SHOULD BE RETURNED */ private static List getFileExtractors(AbstractFile content, Lookup context) { List fileExtractors = Arrays.asList( new TextFileExtractor(content), new HtmlTextExtractor(content), new SqliteTextExtractor(content), - new TikaTextExtractor(content)); + new TikaTextExtractor(content)); /// This should go last to ensure the more specific ones are picked first. fileExtractors.forEach((fileExtractor) -> { fileExtractor.setExtractionSettings(context); diff --git a/Core/src/org/sleuthkit/autopsy/textextractors/TextFileExtractor.java b/Core/src/org/sleuthkit/autopsy/textextractors/TextFileExtractor.java index 3efb6b1aed..e6c52fb19c 100644 --- a/Core/src/org/sleuthkit/autopsy/textextractors/TextFileExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/textextractors/TextFileExtractor.java @@ -31,17 +31,24 @@ import java.nio.charset.CharsetEncoder; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import java.util.List; +import java.util.logging.Level; import org.apache.tika.parser.txt.CharsetDetector; import org.apache.tika.parser.txt.CharsetMatch; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ReadContentInputStream; +import org.sleuthkit.datamodel.TskCoreException; /** - * Extract text from text files + * A TextExtractor that is used to extract text from a text file. */ public final class TextFileExtractor implements TextExtractor { - public static Charset UNKNOWN_CHARSET = new Charset("unknown", null) { + + /* + * The char set returned if a text file extractor fails to detect the + * encoding of the file from which it is extracting text. + */ + public static final Charset UNKNOWN_CHARSET = new Charset("unknown", null) { @Override public boolean contains(Charset cs) { return false; @@ -59,33 +66,45 @@ public final class TextFileExtractor implements TextExtractor { }; // This value will be used as a threshold for determining which encoding - // detection library to use. If Tika's own confidence is at least - // MIN_MATCH_CONFIDENCE, Tika's result will be used for decoding. + // detection library to use. If CharsetDetector's own confidence is at least + // MIN_MATCH_CONFIDENCE, CharsetDetector's result will be used for decoding. // Otherwise, Decodetect will be used. - static final private int MIN_TIKA_MATCH_CONFIDENCE = 35; + // + // Note: We initially used a confidence of 35, but it was causing some + // Chrome Cache files to get flagged as UTF-16 with confidence 40. + // These files had a small amount of binary data and then ASCII. + static final private int MIN_CHARSETDETECT_MATCH_CONFIDENCE = 41; // This value determines whether we will consider Decodetect's top-scoring - // result a legitimate match or if we will disregard its findings + // result a legitimate match or if we will disregard its findings. // - // Possible values are 0 to 1, inclusive + // Possible values are 0 to 1, inclusive. static final private double MIN_DECODETECT_MATCH_CONFIDENCE = 0.4; + private static final Logger logger = Logger.getLogger(SqliteTextExtractor.class.getName()); private final AbstractFile file; + private Charset encoding = null; + + /** + * Constructs a TextExtractor that is used to extract text from a text file. + * + * @param file The file. + */ public TextFileExtractor(AbstractFile file) { this.file = file; } @Override public Reader getReader() { - Charset encoding = getEncoding(file); - if (encoding.equals(UNKNOWN_CHARSET)) { - encoding = StandardCharsets.UTF_8; + Charset enc = getEncoding(); + if (enc.equals(UNKNOWN_CHARSET)) { + enc = StandardCharsets.UTF_8; } - return getReader(encoding); + return getReader(enc); } - public Reader getReader(Charset encoding) { + private Reader getReader(Charset encoding) { return new InputStreamReader(new BufferedInputStream(new ReadContentInputStream(file)), encoding); } @@ -94,42 +113,60 @@ public final class TextFileExtractor implements TextExtractor { return file.getMIMEType().equals("text/plain"); } - public class TextFileExtractorException extends Exception { - public TextFileExtractorException(String msg, Throwable ex) { - super(msg, ex); + /** + * Returns the encoding of the file. + * + * @return Detected encoding or UNKNOWN_CHARSET. + */ + public Charset getEncoding() { + if (encoding != null) { + return encoding; } - public TextFileExtractorException(String msg) { - super(msg); - } - } - public static Charset getEncoding(Content content) { - try (InputStream stream = new BufferedInputStream(new ReadContentInputStream(content))) { - // Tika first + // Encoding detection is hard. We use several libraries since the data passed in is often messy. + // First try CharsetDetector (from Tika / ICU4J). + // It is a rule-based detection approach. + try (InputStream stream = new BufferedInputStream(new ReadContentInputStream(file))) { CharsetDetector detector = new CharsetDetector(); detector.setText(stream); CharsetMatch tikaResult = detector.detect(); - if (tikaResult != null && tikaResult.getConfidence() >= MIN_TIKA_MATCH_CONFIDENCE) { + if (tikaResult != null && tikaResult.getConfidence() >= MIN_CHARSETDETECT_MATCH_CONFIDENCE) { try { - return Charset.forName(tikaResult.getName()); - } catch (UnsupportedCharsetException ignored) { + encoding = Charset.forName(tikaResult.getName()); + return encoding; + } catch (UnsupportedCharsetException ex) { + logger.log(Level.WARNING, String.format("Error converting CharsetDetector result for %s (objID=%d)", file.getName(), file.getId()), ex); } } + } catch (IOException ex) { + logger.log(Level.WARNING, String.format("Error setting CharsetDetector stream for %s (objID=%d)", file.getName(), file.getId()), ex); + } - // Decodetect if Tika fails or falls below confidence threshold + // If that did not work, then use DecoDetect, which is stastical + // We needed this for some Japanese text files that were incorrectly detected by CharsetDetector (with low confidence) + // This will not always work with messy data that combines some binary and some ASCII. + try { int maxBytes = 100000; - int numBytes = Math.min(stream.available(), maxBytes); + int numBytes = maxBytes; + if (file.getSize() < maxBytes) { + numBytes = (int) file.getSize(); + } + byte[] targetArray = new byte[numBytes]; - stream.read(targetArray); + file.read(targetArray, 0, numBytes); List results = Decodetect.DECODETECT.getResults(targetArray); if (!results.isEmpty()) { DecodetectResult topResult = results.get(0); if (topResult.getConfidence() >= MIN_DECODETECT_MATCH_CONFIDENCE) { - return topResult.getEncoding(); + encoding = topResult.getEncoding(); + return encoding; } } - } catch (IOException ignored) { + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Error reading content from %s (objID=%d)", file.getName(), file.getId()), ex); } - return UNKNOWN_CHARSET; + + encoding = UNKNOWN_CHARSET; + return encoding; } } diff --git a/Experimental/nbproject/project.xml b/Experimental/nbproject/project.xml index c6e222b422..90fbce3d76 100644 --- a/Experimental/nbproject/project.xml +++ b/Experimental/nbproject/project.xml @@ -135,7 +135,7 @@ 10 - 10.17 + 10.18 diff --git a/ImageGallery/nbproject/project.xml b/ImageGallery/nbproject/project.xml index 634ed81534..a7157df475 100644 --- a/ImageGallery/nbproject/project.xml +++ b/ImageGallery/nbproject/project.xml @@ -127,7 +127,7 @@ 10 - 10.17 + 10.18 diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index bae9894de9..f634e8387d 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -2150,10 +2150,15 @@ public final class DrawableDB { public void deleteDataSource(long dataSourceID) throws SQLException, TskCoreException { dbWriteLock(); DrawableTransaction trans = null; + String whereClause = "WHERE data_source_obj_id = " + dataSourceID; + String tableName = "image_gallery_groups"; try { trans = beginTransaction(); deleteDataSourceStmt.setLong(1, dataSourceID); deleteDataSourceStmt.executeUpdate(); + if (caseDb.getCaseDbAccessManager().tableExists(tableName)) { + caseDb.getCaseDbAccessManager().delete(tableName, whereClause); + } commitTransaction(trans, true); } catch (SQLException | TskCoreException ex) { if (null != trans) { diff --git a/InternalPythonModules/android/textmessage.py b/InternalPythonModules/android/textmessage.py index 7f72a0b570..cf5460da21 100644 --- a/InternalPythonModules/android/textmessage.py +++ b/InternalPythonModules/android/textmessage.py @@ -126,11 +126,12 @@ class TextMessageAnalyzer(general.AndroidComponentAnalyzer): artifact.addAttributes(attributes) - # Create an account - msgAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, address, general.MODULE_NAME, abstractFile); + if address is not None: + # Create an account + msgAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, address, general.MODULE_NAME, abstractFile); - # create relationship between accounts - Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [msgAccountInstance], artifact,Relationship.Type.MESSAGE, date); + # create relationship between accounts + Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [msgAccountInstance], artifact,Relationship.Type.MESSAGE, date); bbartifacts.append(artifact) diff --git a/KeywordSearch/nbproject/project.xml b/KeywordSearch/nbproject/project.xml index 78b0b2626a..25e05e30fa 100644 --- a/KeywordSearch/nbproject/project.xml +++ b/KeywordSearch/nbproject/project.xml @@ -119,7 +119,7 @@ 10 - 10.17 + 10.18 diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index af7ce2e771..ec14b42749 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -680,7 +680,6 @@ public final class KeywordSearchIngestModule implements FileIngestModule { * Returns true if indexing was successful and false otherwise. * * @param aFile Text file to analyze - * @param detectedCharset the encoding of the file */ private boolean indexTextFile(AbstractFile aFile) { try { diff --git a/NEWS.txt b/NEWS.txt index 7a4487417d..ee294d7fea 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -1,3 +1,36 @@ +---------------- VERSION 4.14.0 -------------- +Specialized UIs: +- New File Discovery UI that allows you to search and filter for certain types of files. +- New Map viewer that uses either Bing (when online) or offline map tiles. +- Communications UI shows country names for phone numbers and fixed bug in summary panel. +- Fixed bugs in timeline filtering. +- Refactored backend timeline filtering code based on The Sleuth Kit datamodel changes to remove JavaFX dependency. + +Data Sources: +- Added limited support for APFS disk images. Does not include encrypted volumes or ones that span multiple disks. Uses contribution to The Sleuth Kit from Blackbag Technologies. +- New data source processor that parses “XRY File Exports”. + +Content Viewers: +- Added a new “Context” viewer to show where a file came from. Currently shows what message a file was attached to or what URL a file was downloaded from. +- Added support to seek and change playback speed for videos in “Application” viewer. +- Improved support for Unicode HTML files in “Application” viewer. +- Added support for webp image files in “Application” viewer. + +Ingest Modules: +- Keyword Search module uses Decodetect statistical encoding detection for plain text files. Fixes issues with incorrect detection of Japanese files. +- Embedded File Extractor module uses statistical analysis to determine encoding of file names in ZIP files. Fixes issues with ZIP files created on Windows Japanese computers. +- Solr (Keyword Search module) now uses Japanese-specific tokenization using Kuromoji. +- Fixed Shellbags module in RegRipper (used by Autopsy Recent Activity module) to fix parsing errors. +- Plaso module no longer generates an error if enabled for non-disk image data sources. +- Added support for message attachments that are stored as an external file system file. Expanded Email and Android modules to use this technique. + +General: +- Fixed crashes by gstreamer when a video is selected. +- Added initial capability to delete a data source from a case (excludes data in the CR). +- Changed behavior of portable case menu item to automatically open the case and warn if it was already unpacked. +- Fixed bug that caused issues when case metadata had Unicode values. +- Added new Attachment APIs to the CommunicationsArtifactHelper class to support attachments stored as external file system files. + ---------------- VERSION 4.13.0 -------------- General: - Switch from Oracle JDK to OpenJDK. diff --git a/RecentActivity/nbproject/project.xml b/RecentActivity/nbproject/project.xml index 29b5d1362a..89394f1b1f 100644 --- a/RecentActivity/nbproject/project.xml +++ b/RecentActivity/nbproject/project.xml @@ -60,7 +60,7 @@ 10 - 10.17 + 10.18 diff --git a/TSKVersion.xml b/TSKVersion.xml index 4bbaf0c0d0..561c00581d 100644 --- a/TSKVersion.xml +++ b/TSKVersion.xml @@ -1,3 +1,3 @@ - + diff --git a/Testing/nbproject/project.xml b/Testing/nbproject/project.xml index 41dc8b253e..def60a06ac 100644 --- a/Testing/nbproject/project.xml +++ b/Testing/nbproject/project.xml @@ -47,7 +47,7 @@ 10 - 10.17 + 10.18 diff --git a/docs/doxygen-dev/footer.html b/docs/doxygen-dev/footer.html index 5ab86c6e86..b874c74742 100755 --- a/docs/doxygen-dev/footer.html +++ b/docs/doxygen-dev/footer.html @@ -1,5 +1,5 @@
-

Copyright © 2012-2019 Basis Technology. Generated on $date
+

Copyright © 2012-2020 Basis Technology. Generated on $date
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.

diff --git a/docs/doxygen-user/Doxyfile b/docs/doxygen-user/Doxyfile index 1bb3773a70..8295d9df5a 100644 --- a/docs/doxygen-user/Doxyfile +++ b/docs/doxygen-user/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy User Documentation" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.13.0 +PROJECT_NUMBER = 4.14.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -1025,7 +1025,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = 4.13.0 +HTML_OUTPUT = 4.14.0 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/docs/doxygen-user/android_analyzer.dox b/docs/doxygen-user/android_analyzer.dox index a849e089ff..eee20b85a0 100644 --- a/docs/doxygen-user/android_analyzer.dox +++ b/docs/doxygen-user/android_analyzer.dox @@ -52,14 +52,10 @@ There are no runtime ingest settings required. Seeing Results ------ -The results show up in the tree under "Results", "Extracted Content". +The results show up in the tree under "Results", "Extracted Content". The exact data extracted will vary but can include contacts, call logs, messages, and GPS entries. \image html android_analyzer_output.PNG -Messages can also be seen by browsing to the source file in the Data Sources tree, which will display the messages in the Results Viewer to the right. Any messages with attachments will be shown under the source file in the tree, and the attachments can be seen in the Result Viewer. - -\image html messages_datasource_tree.png - */ diff --git a/docs/doxygen-user/content_viewer.dox b/docs/doxygen-user/content_viewer.dox index d7f3420fbc..66ec679565 100644 --- a/docs/doxygen-user/content_viewer.dox +++ b/docs/doxygen-user/content_viewer.dox @@ -54,6 +54,10 @@ It will display most image types, which can be scaled and rotated: \image html content_viewer_app_image.png +It displays video files, allowing you to move play/pause, move forward or backward 30 seconds, adjust the volume, and change the playback speed. + +\image html content_viewer_video.png + It also allows you to browse SQLite tables and export their contents as CSV: \image html content_viewer_app_sqlite.png @@ -82,6 +86,12 @@ The File Metadata tab displays basic information about the file, such as type, s \image html content_viewer_metadata.png +\section cv_context Context + +The Context tab shows the the source of attached files and allows you to view the original result. In the image below you can see the context for an image that was sent as an email attachment. + +\image html content_viewer_context.png + \section cv_results Results The Results tab is active when selecting items with associated results such as keyword hits, call logs, and messages. The exact fields displayed depend on the type of result. The two images below show the Results tab for a call log and a web bookmark. diff --git a/docs/doxygen-user/data_sources.dox b/docs/doxygen-user/data_sources.dox index cd48474f41..6aeed0bbb1 100644 --- a/docs/doxygen-user/data_sources.dox +++ b/docs/doxygen-user/data_sources.dox @@ -3,12 +3,13 @@ A data source is the thing you want to analyze. It can be a disk image, some logical files, a local disk, etc. You must open a case prior to adding a data source to Autopsy. -Autopsy supports four types of data sources: +Autopsy supports multiple types of data sources: - Disk Image or VM File: A file (or set of files) that is a byte-for-byte copy of a hard drive or media card, or a virtual machine image. (see \ref ds_img) - Local Disk: Local storage device (local drive, USB-attached drive, etc.). (see \ref ds_local) - Logical Files: Local files or folders. (see \ref ds_log) - Unallocated Space Image Files: Any type of file that does not contain a file system but you want to run through ingest (see \ref ds_unalloc) - Autopsy Logical Imager Results: The results from running the logical imager. (see \ref ds_logical_imager) +- XRY Text Export: The results from exporting text files from XRY. (see \ref ds_xry) \section ds_add Adding a Data Source @@ -41,7 +42,7 @@ NOTE: If you are adding a data source to a multi-user case, ensure that all Auto 6) After the ingest modules have been configured and the basic examination of the data source is complete, the ingest modules will begin to analyze the file contents. -You cannot remove a data source from a case. +Data sources can be removed from cases created with Autopsy 4.14.0 and later. See the section \ref data_source_deletion "below". \section ds_img Adding a Disk Image @@ -116,4 +117,23 @@ To add unallocated space image files: This option allows you to add the results of a logical imager collection. See the \ref logical_imager_page page for details. +\section ds_xry Adding XRY Text Export Data +An XRY text export folder is expected to look similar to this: + +\image html xry_folder.png + +To add exported text files: +-# Choose "XRY Text Export" from the data source types. +-# Browse to the folder containing the text files. + +\image html xry_dsp.png + +\section data_source_deletion Deleting Data Sources + +As of Autopsy 4.14.0, data sources can be removed from cases. Removing a data source will delete all files associate with the data source, as well as all results from running ingest modules, tags, and timeline data. \ref reporting_page "Reports" will not be deleted, as most are not associated with a specific data source. If a new data source was created while processing another (from the \ref vm_extractor_page for example), this new data source will also be deleted if its parent is deleted. + +To delete a data source, right click it in either the \ref tree_viewer_page or the \ref result_viewer_page and select "Remove Data Source". If the case was originally created with a version of Autopsy earlier than 4.14.0 then this option will be disabled. After a confirmation dialog, the case will close and then reopen after the data source has been removed. + +\image html data_source_delete.png + */ \ No newline at end of file diff --git a/docs/doxygen-user/email_parser.dox b/docs/doxygen-user/email_parser.dox index 517ccb069d..e69621d2ed 100644 --- a/docs/doxygen-user/email_parser.dox +++ b/docs/doxygen-user/email_parser.dox @@ -23,12 +23,14 @@ There are no runtime ingest settings required. Seeing Results ------ -The results of this show up in the "Results", "E-Mail Messages" portion of the tree. +The results of this show up in the "Results", "E-Mail Messages" portion of the \ref tree_viewer_page. \image html email_results.PNG -The results can also be seen by browsing to the source file in the Data Sources tree, which will display the messages in the Results Viewer to the right. Any messages with attachments will be shown under the source file, and the attachments can be seen in the Result Viewer by selecting the message. +If an e-email has an attachment, the "Attachments" tab in the \ref content_viewer_page will be active. -\image html email_datasource_tree.png +\image html email_attachments.png + +You can right click and select "View File in Directory" to navigate to the attached file. You can also switch to the "Thumbnails" tab to see a preview of any image attachments. */ diff --git a/docs/doxygen-user/extension_mismatch.dox b/docs/doxygen-user/extension_mismatch.dox index 2866ecedc0..25bac5e3fe 100644 --- a/docs/doxygen-user/extension_mismatch.dox +++ b/docs/doxygen-user/extension_mismatch.dox @@ -14,6 +14,10 @@ One can add and remove MIME types in the "Tools", "Options", "File Extension Mis \image html extension-mismatch-detected-configuration.PNG
+If you'd like to contribute your changes back to the community, then you'll need to upload your updated %APPDATA%\autopsy\dev\config\mismatch_config.xml file by either: +- Make a fork of the Github Autopsy repository, copy the new file into the src\org\sleuthkit\autopsy\fileextmismatch folder and submit a pull request +- Attach the entire mismatch_config.xml file to a github issue. + Using the Module ====== Note that you can get a lot of false positives with this module. You can add your own rules to Autopsy to reduce unwanted hits. diff --git a/docs/doxygen-user/file_discovery.dox b/docs/doxygen-user/file_discovery.dox new file mode 100644 index 0000000000..a92b41dc43 --- /dev/null +++ b/docs/doxygen-user/file_discovery.dox @@ -0,0 +1,189 @@ +/*! \page file_discovery_page File Discovery + +\section file_disc_overview Overview + +The file discovery tool shows images or videos that match a set of filters configured by the user. You can choose how to group and order your results in order to see the most relevant data first. + +\section file_disc_prereq Prerequisites + +We suggest running all \ref ingest_page "ingest modules" before launching file discovery, but if time is a factor the following are the modules that are the most important. You will see a warning if you open file discovery without running the \ref file_type_identification_page and \ref EXIF_parser_page. + +Required ingest modules: +
    +
  • \ref file_type_identification_page +
+ +Optional ingest modules: +
    +
  • \ref cr_ingest_module - Needed to use the \ref file_disc_occur_filter +
  • \ref EXIF_parser_page - Needed to use the \ref file_disc_user_filter +
  • \ref hash_db_page - Needed to use the \ref file_disc_hash_filter and to de-duplicate files +
  • \ref interesting_files_identifier_page - Needed to use the \ref file_disc_int_filter +
  • \ref object_detection_page - Needed to use the \ref file_disc_obj_filter +
+ +\section file_disc_run Running File Discovery + +To launch file discovery, either click the "File Discovery" icon near the top of the Autopsy UI or go to "Tools", "File Discovery". There are three steps when setting up file discovery, which flow from the top of the panel to the bottom: +
    +
  1. \ref file_disc_type "Choose the file type" +
  2. \ref file_disc_filtering "Set up filters" +
  3. \ref file_disc_grouping "Choose how to group and sort the results +
+ +Once everything is set up, use the "Show" button at the bottom of the left panel to display your results. If you want to cancel a search in progress you can use the "Cancel" button. + +\image html FileDiscovery/fd_main.png + +\subsection file_disc_type File Type + +The first step is choosing whether you want to display images or videos. The file type is determined by the MIME type of the file, which is why the file_type_identification_page must be run to see any results. Switching between the file types will clear any results being displayed and reset the filters. + +\image html FileDiscovery/fd_fileType.png + +\subsection file_disc_filtering Filtering + +The second step is to select and configure your filters. For most filters, you enable them using the checkbox on the left and then select your options. Multiple options can be selected by using CTRL + left click. Files must pass all enabled filters to be displayed. + +\subsubsection file_disc_size_filter File Size Filter + +The file size filter lets you restrict the size of your results. The options are different for images and videos - an extra small image might be under 16 KB while an extra small video is anything under 500 KB. + +\image html FileDiscovery/fd_fileSizeFilter.png + +\subsubsection file_disc_ds_filter Data Source Filter + +The data source filter lets you restrict which data sources in your case to include in the results. + +\image html FileDiscovery/fd_dataSourceFilter.png + +\subsubsection file_disc_occur_filter Past Occurrences Filter + +The past occurrences filter uses the \ref central_repo_page "central repository" and \ref hash_db_page "known hash sets" to restrict how commom/rare a file must be to be included in the results. By default, the "Known Files" option is disabled, meaning that any file matching the NSRL or other white-listed hash set will not be displayed. + +\image html FileDiscovery/fd_pastOccur.png + +The counts for the rest of the options are based on how many data sources in your central repository contain a copy of this file (based on hash). If a file only appears in one data source in the current case, then it will match "Unique(1)". If it has only been seen in a few other data source, it will match "Rare(2-10)". Note that it doesn't matter how many times a file appears in each data source - a file could have twenty copies in one data source and still be "unique". + +\subsubsection file_disc_user_filter Possibly User Created + +The possibly user created filter restricts the results to files that suspected to be raw images or videos. + +\image html FileDiscovery/fd_userCreatedFilter.png + +This means the image or video must have a "User Content Suspected" result associated with it. These primarily come from the \ref EXIF_parser_page "Exif parser module". + +\image html FileDiscovery/fd_userContentArtifact.png + +\subsubsection file_disc_hash_filter Hash Set Filter + +The hash set filter restricts the results to files found in the selected hash sets. Only notable hash sets that have hits in the current case are listed (though those hits may not be images or videos). See the \ref hash_db_page page for more information on creating and using hash sets. + +\image html FileDiscovery/fd_hashSetFilter.png + +\subsubsection file_disc_int_filter Interesting Item Filter + +The interesting item filter restricts the results to files found in the selected interesting item rule sets. Only interesting file rule sets that have results in the current case are listed (though those matches may not be images or videos). See the \ref interesting_files_identifier_page page for more information on creating and using interesting item rule sets. + +\image html FileDiscovery/fd_interestingItemsFilter.png + +\subsubsection file_disc_obj_filter Object Detected Filter + +The object detected filter restricts the results to files that matched the selected classifiers. Only classifiers that have results in the current case are listed. Note that currently the built-in \ref object_detection_page ingest module only works on images, so you should generally not use this filter with videos. See the \ref object_detection_page page for more information on setting up classifiers. + +\image html FileDiscovery/fd_objectFilter.png + +\subsubsection file_disc_parent_filter Parent Folder Filter + +The parent folder filter either restricts the path the files can be on. This filter works differently than the others in that the individual options do not have to be selected - every rule that has been entered will be applied. + +\image html FileDiscovery/fd_parentFilter.png + +You can enter paths that should be included and paths that should be ignored. For both you then specify whether the path string you entered is a full path or a substring. For full path matches you'll need to include the leading and trailing slashes. Full path matches are also case-sensitive. + +The default options, shown above, will exclude any file that has a "Windows" folder or a "Program Files" folder in its path. It would exclude files like "/Windows/System32/image1.jpg" but would not exclude "/My Pictures/Bay Windows/image2.jpg" because the slashes around "Windows" force it to match the exact folder name. + +Here is another example. This rule was created with "Full" and "Include" selected. + +\image html FileDiscovery/fd_parentEx2.png + +This matches the file "/LogicalFileSet2/File Discovery/bird1.tif" but not any images in subfolders under "File Discovery". + +When there are multiple path options in the filter, they will be applied as follows: +
    +
  • The file path must match every "exclude" rule to pass +
  • If any "include" rules exist, the file path must match at least one "include" rule to pass +
+ +This allows you to, for example, make rules to include both the "My Documents" and the "My Pictures" folders. + +\subsection file_disc_grouping Grouping and Sorting + +The final options are for how you want to group and sort your results. + +\image html FileDiscovery/fd_grouping.png + +The first option lets you choose the top level grouping for your results and the second option lets you choose how to sort them. The groups appear in the middle column of the file discovery panel. Note that some of the grouping options may not always appear - for example, grouping by past occurrences will only be present if the \ref central_repo_page is enabled, and grouping by hash set will only be present if there are hash set hits in your current case. The example below shows the groups created using the default options (group by file size, order groups by group name): + +\image html FileDiscovery/fd_groupingSize.png + +In the case of file size and past occurrences, ordering by group name is based on the natural ordering of the group (largest to smallest or most rare to most common). For the other groups it will be alphabetical. Ordering groups by size will sort them based on how many files each group contains, going largest to smallest. For example, here we've grouped by interesting item set and ordered the groups by their size. + +\image html FileDiscovery/fd_groupingInt.png + +The interesting items filter was not enabled so most images ended up in the "None" group, meaning they have no interesting file result associated with them. The final group in the list contains a file that matched both interesting item rule sets. + +The last grouping and sorting option is choosing how to sort the results within a group. This is the order of the results in the top right panel after selecting a group from the middle column. Note that due to the merging of results with the same hash in that panel, ordering by file name, path, or data source can vary. See the \ref file_disc_dedupe section below for more information. + +\section file_disc_results Viewing Results + +\subsection file_disc_results_overview Overview + +Once you select your options and click "Show", you'll see a list of groups in the middle panel. Selecting one of these groups will display the results from that group in the right panel. If your results are images, you'll see thumbnails for each image in the top area of the right panel. + +\image html FileDiscovery/fd_resultGroups.png + +If your results are videos, each result will display four thumbnails from the video. + +\image html FileDiscovery/fd_videos.png + +When you select a result from the top of the right panel, you'll see the path to the corresponding file(s) in the "Instances" panel below the thumbnails. There may be more than one file instance associated with a result - see the \ref file_disc_dedupe section below. You can right-click on files in the instances panel to use most of options available in the normal \ref result_viewer_page. + +\image html FileDiscovery/fd_instanceContext.png + +The bottom section of the panel is identical to the standard \ref content_viewer_page and displays data corresponding to the file instance selected in the middle of the panel. + +\subsection file_disc_dedupe De-duplication + +Assuming the \ref hash_db_page module has been run, all files in a result group with the same hash will be merged together under a single instance. You can see the number of instances of each file under the thumbnail, and each file instance will be displayed in the middle section of the panel. + + + +\image html FileDiscovery/fd_dupeEx.png + +Clicking on a particular instance will load data for that file in the content viewer area at the bottom. + +Note that files in different groups will not be merged together or appear under the instances list of each other. For example, if you choose to group by parent folder and have two instances of a file with the same hash but in different folders, each will appear once under its parent folder. Grouping by file size (the default) will always merge every instance of the same file. + +\subsection file_disc_icons Status Icons + +A number of icons may be displayed in the bottom right of the thumbnails to help point out notable results. Hovering over the icon will display a message explaining why the icon is present. In the image below, the yellow icon is present because the file is associated with an interesting item set. + +\image html FileDiscovery/fd_icon.png + +Most of the icons match what would be displayed in the "S" column of the normal \ref result_viewer_page. + +| Icon | Usage | +|-------|------| +\image html FileDiscovery/yellow-circle-yield.png "" | \ref interesting_files_identifier_page "Interesting file set match" or normal \ref tagging_page "file tag" +\image html FileDiscovery/red-circle-exclamation.png "" | Notable \ref hash_db_page "hash set hit" or notable \ref tagging_page "file tag" +\image html FileDiscovery/file-icon-deleted.png "" | Deleted file (every instance is deleted) + + +\subsection file_disc_paging Paging + +If the group you select has many results, the results will be split up into pages. You can use the left and right arrows to move between pages or type in the page number you wish to go to. You can adjust the number of results per page using the drop down box in the upper right. + +\image html FileDiscovery/fd_paging.png + +*/ \ No newline at end of file diff --git a/docs/doxygen-user/footer.html b/docs/doxygen-user/footer.html index 5ab86c6e86..b874c74742 100644 --- a/docs/doxygen-user/footer.html +++ b/docs/doxygen-user/footer.html @@ -1,5 +1,5 @@
-

Copyright © 2012-2019 Basis Technology. Generated on $date
+

Copyright © 2012-2020 Basis Technology. Generated on $date
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.

diff --git a/docs/doxygen-user/geolocation.dox b/docs/doxygen-user/geolocation.dox new file mode 100644 index 0000000000..a4c78351cf --- /dev/null +++ b/docs/doxygen-user/geolocation.dox @@ -0,0 +1,137 @@ +/*! \page geolocation_page Geolocation + +\section geo_overview Overview + +The Geolocation window shows artifacts that have longitude and latitude attributes as waypoints on a map. In the field, when access to online map tile servers may not be available, the Geolocation window provides support for offline map tile data sources. + +\image html geo_main.png + +\section geo_usage Usage + +To open the Geolocation window, go to "Tools" and then select "Geolocation". + +\subsection geo_navigation General Usage + +You can move the map by clicking and dragging, and zoom using either the mouse wheel or the slider in the bottom left of the map. If a map tile is not available the tile will appear grey but the waypoints will still be displayed. This is more likely to happen when changing the default \ref geo_map_options. + +You can left click on a waypoint to highlight that waypoint and show a details pop-up in the upper right corner of the map. The details pop-up will be updated as you click on different waypoints. The data displayed will vary depending on the type of waypoint. For example, this is the endpoint of a GPS Route: + +\image html geo_details_route.png + +While this is an image with GPS coordinates found by the \ref EXIF_parser_page : + +\image html geo_details.png + +You can also right click on a waypoint to bring up a similar menu to what you'd see in the \ref result_viewer_page. + +\image html geo_context_menu.png + +\subsection geo_filter Filtering + +The filters are displayed on the left side of the screen. The top filter lets you filter the waypoints based on timestamp. If enabled, you will only see waypoints with a timestamp within N days of the most recent waypoint (not the current date). When using this filter you can also choose whether you want to see waypoints with no timestamp. The second filter allows you to show waypoints only for the selected data sources. + +\image html geo_filter_panel.png + +If desired, the filter panel can be hidden by clicking on the vertical "Filters" tab on the top right edge of the filter panel. Clicking on that tab a second time will restore the filters panel. + +\subsection geo_report Generating a Report + +You can generate a KML report using the "KML Report" button in the bottom right corner of the window. The report will include only the currently visible waypoints and can be found in the "Reports" folder of your case. + +\image html geo_report.png + +As with other \ref reporting_page "report modules", the generated report will appear under "Reports" in the \ref tree_viewer_page. Note that you can also use the \ref report_kml report module to generate a report containing all geolocation data in the case. + +\section geo_map_options Map Tile Options + +

The Autopsy Geolocation window supports several map tile data source options. The map tile data source can be changed +on the Geolocation panel in the Options dialog. There are four options for geolocation tile data, two of which can be used offline. +

    +
  • Default online tile server +
      +
    • The default Geolocation window tile data source is the Microsoft Virtual Earth server https://www.bing.com/maps. +
    +
  • OpenStreetMap server +
      +
    • You can specify the address of a OSM tile server. A list of online tile servers can be found here: https://wiki.openstreetmap.org/wiki/Tile_servers. +Tile servers may have restrictions on them that prevent Autopsy from accessing their tiles. If the tiles URL is something of the form "http://tiles.example.org/${z}/${x}/${y}.png", +then you'll need to enter "http://tiles.example.org" in the options panel. + +\image html geo_openstreetmap.png +
    +
  • OpenStreetMap zip file +
      +
    • Allows offline use of zip file of OSM tile images +
    • Details on how to generate tile zip files are \ref geo_generate_zip "below". +
    +
  • MBTiles file +
      +
    • Allows offline use of MBTiles file containing raster tiles. +
    • MBTiles raster tiles files can be downloaded from OpenMapTiles. +
    • OpenMapTiles provides downloads of MBTile files for areas as large as the whole planet to as small as regions of countries. +
    • For each of these regions there are at least four MBTiles available for download, please be sure to download one of the "Raster Tile" files, +not the "Vector Tiles". +
    +
+ +\subsection geo_generate_zip Using Maperative to Generate Tile Image Zip Files + +Maperative is a tool for drawing maps, however it can also be used to create tile images. Maperative download and documentations can be found at http://maperitive.net/ . + +By default Maperative uses an online tile server to generate the map. For offline use, users can supply an OpenStreetMap raw data extract. + + +\subsubsection geo_generate_tile_image Generating tile image zip files using any map data source: +
    +
  1. Download and run Maperative. +
  2. Center and zoom in on area of interest. The larger the area, the more tiles that will be generated. Tiles will be generated for the area visible in the map panel. +
  3. Choose whether you want to use the default zoom levels or custom ones. Zoom levels in Mapertive start at 1. As the zoom level increases so will the quantity of tiles generated as well as the detail of each tile. Generating tiles, especially for heavily populated areas, may take time, please be patient with either method. +
      +
    • To generate tiles using the default zoom levels, select Tools->Generate Tiles + +\image html geo_gen_tiles.png + +Maperative will generate tiles for zoom levels depending on the area of interest and the zoom level. For example, if you start all the way zoomed out, you will likely see levels 1 through 10 generated. If you start zoomed in, you might see levels 10 through 14. + +
    • Maperative provides a command interface which allows you to generate tiles for specific zoom levels. Commands can be run in the Command prompt text field at the bottom of the Maperative window. For a full list of commands see the Maperative documentation or http://maperitive.net/docs/. The generate-tiles command can be used to generate tiles for the area visible in the map panel area. For full details on generate-tiles see the documentation included with Maperative or http://maperitive.net/docs/Commands/GenerateTiles.html. The following is a sample command to generate tiles for zoom level 2 to 3 into the folder Tiles: +\verbatim generate-tiles minzoom=2 maxzoom=3 tilesdir=C:\Tiles \endverbatim + +\image html geo_command_line.png + +
    +
  4. For use in autopsy, the generated tile images need to be in a zip file. To create a zip of tiles for use in Autopsy, zip up all of the folders in the tile file output directory. Do not include the parent directory, just the numbered folders contained within. If you use the menu bar option or did not specify a folder in your command the generated tiles will be located in <Maperative Install Location>\\Tiles. + +\image html geo_tile_folder.png + +Be sure to zip only the contents of the folder, not the top level folder. +
+ + +\subsubsection geo_add_ds Adding a data source to Maperative + +Maperative can be used to generate tiles using raw data extracts from OpenStreetMaps. Data extracts (*.osm or *.osm.pbf) files can be downloaded from various locations. See https://wiki.openstreetmap.org/wiki/Planet.osm for a list of locations. Geofabrik's free download server has open OpenStreetMap data extracts for many regions. When using OSM raw data extracts in Maperative, the recommendation is to use smaller (.osm) files. + +To add a data source to Maperative: +
    +
  1. Select from the menu bar File->Open Map Source... + +\image html geo_add_ds.png + +
  2. The new data source will appear in the bottom right corner of the window in the "Map Sources" list. +
  3. To disable a Map Source, select the Map Source from the list and click the X button. +
+ +\subsubsection geo_merge_osm Merging OSM raw data extracts + +For ease of use, users may want to merge OSM raw data extracts. OSMConvert is a tool that can be used to merge OSM raw data extracts. + +To merge two OSM raw data extracts country1.osm.pbf and country2.osm.pbf use the following commands. Note that this assumes that osmcovert and the files are in the same directory; if they are not be sure to use full paths. +\verbatim +osmconvert country1.osm.pbf -o=country1.o5m +osmconvert country2.osm.pbf -o=country2.o5m +osmconvert country1.o5m country2.o5m -o=together.o5m +osmconvert together.o5m -o=together.osm.pbf +\endverbatim + + +*/ \ No newline at end of file diff --git a/docs/doxygen-user/images/FileDiscovery/fd_dataSourceFilter.png b/docs/doxygen-user/images/FileDiscovery/fd_dataSourceFilter.png new file mode 100644 index 0000000000..9c42198839 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_dataSourceFilter.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_dupeEx.png b/docs/doxygen-user/images/FileDiscovery/fd_dupeEx.png new file mode 100644 index 0000000000..e711bc95cc Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_dupeEx.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_fileSizeFilter.png b/docs/doxygen-user/images/FileDiscovery/fd_fileSizeFilter.png new file mode 100644 index 0000000000..1e2bdb7f84 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_fileSizeFilter.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_fileType.png b/docs/doxygen-user/images/FileDiscovery/fd_fileType.png new file mode 100644 index 0000000000..e7ae704fe5 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_fileType.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_grouping.png b/docs/doxygen-user/images/FileDiscovery/fd_grouping.png new file mode 100644 index 0000000000..7d1441bee1 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_grouping.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_groupingInt.png b/docs/doxygen-user/images/FileDiscovery/fd_groupingInt.png new file mode 100644 index 0000000000..1de9099297 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_groupingInt.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_groupingSize.png b/docs/doxygen-user/images/FileDiscovery/fd_groupingSize.png new file mode 100644 index 0000000000..e21707eb44 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_groupingSize.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_hashSetFilter.png b/docs/doxygen-user/images/FileDiscovery/fd_hashSetFilter.png new file mode 100644 index 0000000000..8c05e72524 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_hashSetFilter.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_icon.png b/docs/doxygen-user/images/FileDiscovery/fd_icon.png new file mode 100644 index 0000000000..575395bad7 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_icon.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_instanceContext.png b/docs/doxygen-user/images/FileDiscovery/fd_instanceContext.png new file mode 100644 index 0000000000..e8d7961da2 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_instanceContext.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_interestingItemsFilter.png b/docs/doxygen-user/images/FileDiscovery/fd_interestingItemsFilter.png new file mode 100644 index 0000000000..4362a904d9 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_interestingItemsFilter.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_loadSettings.png b/docs/doxygen-user/images/FileDiscovery/fd_loadSettings.png new file mode 100644 index 0000000000..0ed2169853 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_loadSettings.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_main.png b/docs/doxygen-user/images/FileDiscovery/fd_main.png new file mode 100644 index 0000000000..1412f43892 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_main.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_objectFilter.png b/docs/doxygen-user/images/FileDiscovery/fd_objectFilter.png new file mode 100644 index 0000000000..42efcee8fa Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_objectFilter.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_paging.png b/docs/doxygen-user/images/FileDiscovery/fd_paging.png new file mode 100644 index 0000000000..7a18889e5e Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_paging.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_parentEx2.png b/docs/doxygen-user/images/FileDiscovery/fd_parentEx2.png new file mode 100644 index 0000000000..102f39f92b Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_parentEx2.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_parentFilter.png b/docs/doxygen-user/images/FileDiscovery/fd_parentFilter.png new file mode 100644 index 0000000000..5cf24a7fda Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_parentFilter.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_pastOccur.png b/docs/doxygen-user/images/FileDiscovery/fd_pastOccur.png new file mode 100644 index 0000000000..9b04f882c9 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_pastOccur.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_resultGroups.png b/docs/doxygen-user/images/FileDiscovery/fd_resultGroups.png new file mode 100644 index 0000000000..02e644a605 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_resultGroups.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_userContentArtifact.png b/docs/doxygen-user/images/FileDiscovery/fd_userContentArtifact.png new file mode 100644 index 0000000000..f4af8f8fb6 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_userContentArtifact.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_userCreatedFilter.png b/docs/doxygen-user/images/FileDiscovery/fd_userCreatedFilter.png new file mode 100644 index 0000000000..510624fcd0 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_userCreatedFilter.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/fd_videos.png b/docs/doxygen-user/images/FileDiscovery/fd_videos.png new file mode 100644 index 0000000000..eb36a81f41 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/fd_videos.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/file-icon-deleted.png b/docs/doxygen-user/images/FileDiscovery/file-icon-deleted.png new file mode 100644 index 0000000000..ef172e501b Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/file-icon-deleted.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/red-circle-exclamation.png b/docs/doxygen-user/images/FileDiscovery/red-circle-exclamation.png new file mode 100644 index 0000000000..26b61e6f06 Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/red-circle-exclamation.png differ diff --git a/docs/doxygen-user/images/FileDiscovery/yellow-circle-yield.png b/docs/doxygen-user/images/FileDiscovery/yellow-circle-yield.png new file mode 100644 index 0000000000..85c873f33f Binary files /dev/null and b/docs/doxygen-user/images/FileDiscovery/yellow-circle-yield.png differ diff --git a/docs/doxygen-user/images/content_viewer_annotations.png b/docs/doxygen-user/images/content_viewer_annotations.png index 671a842099..aa37590379 100644 Binary files a/docs/doxygen-user/images/content_viewer_annotations.png and b/docs/doxygen-user/images/content_viewer_annotations.png differ diff --git a/docs/doxygen-user/images/content_viewer_app_image.png b/docs/doxygen-user/images/content_viewer_app_image.png index 42b57d27c6..c3ba7a0052 100644 Binary files a/docs/doxygen-user/images/content_viewer_app_image.png and b/docs/doxygen-user/images/content_viewer_app_image.png differ diff --git a/docs/doxygen-user/images/content_viewer_app_plist.png b/docs/doxygen-user/images/content_viewer_app_plist.png index 29da91b7ac..815b5d2ed9 100644 Binary files a/docs/doxygen-user/images/content_viewer_app_plist.png and b/docs/doxygen-user/images/content_viewer_app_plist.png differ diff --git a/docs/doxygen-user/images/content_viewer_app_sqlite.png b/docs/doxygen-user/images/content_viewer_app_sqlite.png index 0166cb32e3..9bc708593f 100644 Binary files a/docs/doxygen-user/images/content_viewer_app_sqlite.png and b/docs/doxygen-user/images/content_viewer_app_sqlite.png differ diff --git a/docs/doxygen-user/images/content_viewer_context.png b/docs/doxygen-user/images/content_viewer_context.png new file mode 100644 index 0000000000..c808e6c0b2 Binary files /dev/null and b/docs/doxygen-user/images/content_viewer_context.png differ diff --git a/docs/doxygen-user/images/content_viewer_hex.png b/docs/doxygen-user/images/content_viewer_hex.png index 330c0468a7..0843548450 100644 Binary files a/docs/doxygen-user/images/content_viewer_hex.png and b/docs/doxygen-user/images/content_viewer_hex.png differ diff --git a/docs/doxygen-user/images/content_viewer_html.png b/docs/doxygen-user/images/content_viewer_html.png index 29f2f86ea4..4a24941951 100644 Binary files a/docs/doxygen-user/images/content_viewer_html.png and b/docs/doxygen-user/images/content_viewer_html.png differ diff --git a/docs/doxygen-user/images/content_viewer_indexed_text.png b/docs/doxygen-user/images/content_viewer_indexed_text.png index 83c0076eda..e25cbee5c5 100644 Binary files a/docs/doxygen-user/images/content_viewer_indexed_text.png and b/docs/doxygen-user/images/content_viewer_indexed_text.png differ diff --git a/docs/doxygen-user/images/content_viewer_message.png b/docs/doxygen-user/images/content_viewer_message.png index f927289531..7f3e109396 100644 Binary files a/docs/doxygen-user/images/content_viewer_message.png and b/docs/doxygen-user/images/content_viewer_message.png differ diff --git a/docs/doxygen-user/images/content_viewer_metadata.png b/docs/doxygen-user/images/content_viewer_metadata.png index 1c25115398..fd38248776 100644 Binary files a/docs/doxygen-user/images/content_viewer_metadata.png and b/docs/doxygen-user/images/content_viewer_metadata.png differ diff --git a/docs/doxygen-user/images/content_viewer_other_occurrences.png b/docs/doxygen-user/images/content_viewer_other_occurrences.png index 3bdac5b67b..c8a69e15d2 100644 Binary files a/docs/doxygen-user/images/content_viewer_other_occurrences.png and b/docs/doxygen-user/images/content_viewer_other_occurrences.png differ diff --git a/docs/doxygen-user/images/content_viewer_registry.png b/docs/doxygen-user/images/content_viewer_registry.png index 43149fcb7f..ee45181c85 100644 Binary files a/docs/doxygen-user/images/content_viewer_registry.png and b/docs/doxygen-user/images/content_viewer_registry.png differ diff --git a/docs/doxygen-user/images/content_viewer_results_bookmark.png b/docs/doxygen-user/images/content_viewer_results_bookmark.png index 6baa7f7383..7dbc61bc7a 100644 Binary files a/docs/doxygen-user/images/content_viewer_results_bookmark.png and b/docs/doxygen-user/images/content_viewer_results_bookmark.png differ diff --git a/docs/doxygen-user/images/content_viewer_results_call.png b/docs/doxygen-user/images/content_viewer_results_call.png index 03b79bf1f0..fa554e873a 100644 Binary files a/docs/doxygen-user/images/content_viewer_results_call.png and b/docs/doxygen-user/images/content_viewer_results_call.png differ diff --git a/docs/doxygen-user/images/content_viewer_strings_cyrillic.png b/docs/doxygen-user/images/content_viewer_strings_cyrillic.png index f70ba0c130..0318a383d6 100644 Binary files a/docs/doxygen-user/images/content_viewer_strings_cyrillic.png and b/docs/doxygen-user/images/content_viewer_strings_cyrillic.png differ diff --git a/docs/doxygen-user/images/content_viewer_strings_latin.png b/docs/doxygen-user/images/content_viewer_strings_latin.png index 6968326d65..ba4403e518 100644 Binary files a/docs/doxygen-user/images/content_viewer_strings_latin.png and b/docs/doxygen-user/images/content_viewer_strings_latin.png differ diff --git a/docs/doxygen-user/images/content_viewer_video.png b/docs/doxygen-user/images/content_viewer_video.png new file mode 100644 index 0000000000..f3a26ef8f6 Binary files /dev/null and b/docs/doxygen-user/images/content_viewer_video.png differ diff --git a/docs/doxygen-user/images/cvt_contacts.png b/docs/doxygen-user/images/cvt_contacts.png index 9d86b82ccc..4f674e6579 100644 Binary files a/docs/doxygen-user/images/cvt_contacts.png and b/docs/doxygen-user/images/cvt_contacts.png differ diff --git a/docs/doxygen-user/images/data_source_delete.png b/docs/doxygen-user/images/data_source_delete.png new file mode 100644 index 0000000000..d33e42e107 Binary files /dev/null and b/docs/doxygen-user/images/data_source_delete.png differ diff --git a/docs/doxygen-user/images/email_attachments.png b/docs/doxygen-user/images/email_attachments.png new file mode 100644 index 0000000000..6eab4cc45f Binary files /dev/null and b/docs/doxygen-user/images/email_attachments.png differ diff --git a/docs/doxygen-user/images/email_datasource_tree.png b/docs/doxygen-user/images/email_datasource_tree.png deleted file mode 100644 index d78039867e..0000000000 Binary files a/docs/doxygen-user/images/email_datasource_tree.png and /dev/null differ diff --git a/docs/doxygen-user/images/email_results.PNG b/docs/doxygen-user/images/email_results.PNG index 1e36b83c71..49ccbc6439 100644 Binary files a/docs/doxygen-user/images/email_results.PNG and b/docs/doxygen-user/images/email_results.PNG differ diff --git a/docs/doxygen-user/images/geo_add_ds.png b/docs/doxygen-user/images/geo_add_ds.png new file mode 100644 index 0000000000..60ac3f5684 Binary files /dev/null and b/docs/doxygen-user/images/geo_add_ds.png differ diff --git a/docs/doxygen-user/images/geo_command_line.png b/docs/doxygen-user/images/geo_command_line.png new file mode 100644 index 0000000000..de69fdfd15 Binary files /dev/null and b/docs/doxygen-user/images/geo_command_line.png differ diff --git a/docs/doxygen-user/images/geo_context_menu.png b/docs/doxygen-user/images/geo_context_menu.png new file mode 100644 index 0000000000..e6da8484f9 Binary files /dev/null and b/docs/doxygen-user/images/geo_context_menu.png differ diff --git a/docs/doxygen-user/images/geo_details.png b/docs/doxygen-user/images/geo_details.png new file mode 100644 index 0000000000..8932479524 Binary files /dev/null and b/docs/doxygen-user/images/geo_details.png differ diff --git a/docs/doxygen-user/images/geo_details_route.png b/docs/doxygen-user/images/geo_details_route.png new file mode 100644 index 0000000000..0f47ba0654 Binary files /dev/null and b/docs/doxygen-user/images/geo_details_route.png differ diff --git a/docs/doxygen-user/images/geo_filter_panel.png b/docs/doxygen-user/images/geo_filter_panel.png new file mode 100644 index 0000000000..bac9ee702e Binary files /dev/null and b/docs/doxygen-user/images/geo_filter_panel.png differ diff --git a/docs/doxygen-user/images/geo_gen_tiles.png b/docs/doxygen-user/images/geo_gen_tiles.png new file mode 100644 index 0000000000..0f1547779c Binary files /dev/null and b/docs/doxygen-user/images/geo_gen_tiles.png differ diff --git a/docs/doxygen-user/images/geo_main.png b/docs/doxygen-user/images/geo_main.png new file mode 100644 index 0000000000..cf7f49ca68 Binary files /dev/null and b/docs/doxygen-user/images/geo_main.png differ diff --git a/docs/doxygen-user/images/geo_openstreetmap.png b/docs/doxygen-user/images/geo_openstreetmap.png new file mode 100644 index 0000000000..a35d4234be Binary files /dev/null and b/docs/doxygen-user/images/geo_openstreetmap.png differ diff --git a/docs/doxygen-user/images/geo_report.png b/docs/doxygen-user/images/geo_report.png new file mode 100644 index 0000000000..4b710b3cbc Binary files /dev/null and b/docs/doxygen-user/images/geo_report.png differ diff --git a/docs/doxygen-user/images/geo_tile_folder.png b/docs/doxygen-user/images/geo_tile_folder.png new file mode 100644 index 0000000000..2ae5c347eb Binary files /dev/null and b/docs/doxygen-user/images/geo_tile_folder.png differ diff --git a/docs/doxygen-user/images/messages_datasource_tree.png b/docs/doxygen-user/images/messages_datasource_tree.png deleted file mode 100644 index b8ff011502..0000000000 Binary files a/docs/doxygen-user/images/messages_datasource_tree.png and /dev/null differ diff --git a/docs/doxygen-user/images/plaso_config.png b/docs/doxygen-user/images/plaso_config.png new file mode 100644 index 0000000000..e31aea1a23 Binary files /dev/null and b/docs/doxygen-user/images/plaso_config.png differ diff --git a/docs/doxygen-user/images/plaso_timeline.png b/docs/doxygen-user/images/plaso_timeline.png new file mode 100644 index 0000000000..af112b2732 Binary files /dev/null and b/docs/doxygen-user/images/plaso_timeline.png differ diff --git a/docs/doxygen-user/images/select-data-source-type.PNG b/docs/doxygen-user/images/select-data-source-type.PNG index b997bf9f0f..e772e0114e 100644 Binary files a/docs/doxygen-user/images/select-data-source-type.PNG and b/docs/doxygen-user/images/select-data-source-type.PNG differ diff --git a/docs/doxygen-user/images/ui-layout-1.PNG b/docs/doxygen-user/images/ui-layout-1.PNG index 7f9485888d..8e54967524 100644 Binary files a/docs/doxygen-user/images/ui-layout-1.PNG and b/docs/doxygen-user/images/ui-layout-1.PNG differ diff --git a/docs/doxygen-user/images/xry_dsp.png b/docs/doxygen-user/images/xry_dsp.png new file mode 100644 index 0000000000..46e2659c19 Binary files /dev/null and b/docs/doxygen-user/images/xry_dsp.png differ diff --git a/docs/doxygen-user/images/xry_folder.png b/docs/doxygen-user/images/xry_folder.png new file mode 100644 index 0000000000..1f2e734a98 Binary files /dev/null and b/docs/doxygen-user/images/xry_folder.png differ diff --git a/docs/doxygen-user/main.dox b/docs/doxygen-user/main.dox index 61d555ace4..1322042b91 100644 --- a/docs/doxygen-user/main.dox +++ b/docs/doxygen-user/main.dox @@ -66,7 +66,8 @@ The following topics are available here: - \subpage image_gallery_page - \subpage timeline_page - \subpage communications_page - + - \subpage geolocation_page + - \subpage file_discovery_page - Reporting - \subpage tagging_page diff --git a/docs/doxygen-user/plaso.dox b/docs/doxygen-user/plaso.dox new file mode 100644 index 0000000000..9b013c97c1 --- /dev/null +++ b/docs/doxygen-user/plaso.dox @@ -0,0 +1,19 @@ +/*! \page plaso_page Plaso + +Plaso is a framework for running modules to extract timestamps for various types of files. The Plaso ingest module runs Plaso to generate events that are displayed in the Autopsy \ref timeline_page. For more information on Plaso, see the documentation. + +\section plaso_config Running the Module + +The Plaso ingest module runs dozens of individual parsers and can take a long time to run. In testing, the slowest parsers by far were winreg, pe, and chrome_cache. chrome_cache is always disabled as it duplicates events created by the \ref recent_activity_page. You can choose to enable the winreg and pe modules on the ingest module configuration panel. + +\image html plaso_config.png + +Plaso will only run on \ref ds_img "disk image data sources". + +\section plaso_results Viewing Results + +The Plaso events will be shown in the \ref timeline_page Timeline. Note that events created by Plaso are not displayed in the \ref tree_viewer_page. + +\image html plaso_timeline.png + +*/ \ No newline at end of file diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index d73868792a..dd78bc69ea 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.13.0 +PROJECT_NUMBER = 4.14.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears a the top of each page and should give viewer a @@ -1066,7 +1066,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = api-docs/4.13.0/ +HTML_OUTPUT = api-docs/4.14.0/ # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/docs/doxygen/footer.html b/docs/doxygen/footer.html index 8bf1577a45..f703eb2a5e 100644 --- a/docs/doxygen/footer.html +++ b/docs/doxygen/footer.html @@ -1,5 +1,5 @@
-

Copyright © 2012-2019 Basis Technology. Generated on: $date
+

Copyright © 2012-2020 Basis Technology. Generated on: $date
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.

diff --git a/nbproject/project.properties b/nbproject/project.properties index 8132096d48..1f61a9c967 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -4,7 +4,7 @@ app.title=Autopsy ### lowercase version of above app.name=${branding.token} ### if left unset, version will default to today's date -app.version=4.13.0 +app.version=4.14.0 ### build.type must be one of: DEVELOPMENT, RELEASE #build.type=RELEASE build.type=DEVELOPMENT diff --git a/thirdparty/rr-full/Parse/Win32Registry/Base.pm b/thirdparty/rr-full/Parse/Win32Registry/Base.pm index 6598f37b11..0b206e7bb5 100644 --- a/thirdparty/rr-full/Parse/Win32Registry/Base.pm +++ b/thirdparty/rr-full/Parse/Win32Registry/Base.pm @@ -161,14 +161,26 @@ sub unpack_windows_time { # The equation can be found in several places on the Net. # My thanks go to Dan Sully for Audio::WMA's _fileTimeToUnixTime # which shows a perl implementation of it. - my ($low, $high) = unpack("VV", $data); - my $filetime = $high * 2 ** 32 + $low; - my $epoch_time = int(($filetime - 116444736000000000) / 10000000); + my ($lo, $hi) = unpack("VV", $data); +# my $filetime = $high * 2 ** 32 + $low; +# my $epoch_time = int(($filetime - 116444736000000000) / 10000000); + + my $epoch_time; + if ($lo == 0 && $hi == 0) { + $epoch_time = 0; + } else { + $lo -= 0xd53e8000; + $hi -= 0x019db1de; + $epoch_time = int($hi*429.4967296 + $lo/1e7); + }; + $epoch_time = 0 if ($epoch_time < 0); + + # adjust the UNIX epoch time to the local OS's epoch time # (see perlport's Time and Date section) - my $epoch_offset = timegm(0, 0, 0, 1, 0, 70); - $epoch_time += $epoch_offset; + # my $epoch_offset = timegm(0, 0, 0, 1, 0, 70); + # $epoch_time += $epoch_offset; if ($epoch_time < 0 || $epoch_time > 0x7fffffff) { $epoch_time = undef; diff --git a/thirdparty/rr-full/rip.exe b/thirdparty/rr-full/rip.exe index 2becc8de6b..9a024202f3 100755 Binary files a/thirdparty/rr-full/rip.exe and b/thirdparty/rr-full/rip.exe differ diff --git a/thirdparty/rr-full/rr.exe b/thirdparty/rr-full/rr.exe index a96bba5daa..889b971889 100755 Binary files a/thirdparty/rr-full/rr.exe and b/thirdparty/rr-full/rr.exe differ diff --git a/thirdparty/rr/Parse/Win32Registry/Base.pm b/thirdparty/rr/Parse/Win32Registry/Base.pm index 6598f37b11..0b206e7bb5 100644 --- a/thirdparty/rr/Parse/Win32Registry/Base.pm +++ b/thirdparty/rr/Parse/Win32Registry/Base.pm @@ -161,14 +161,26 @@ sub unpack_windows_time { # The equation can be found in several places on the Net. # My thanks go to Dan Sully for Audio::WMA's _fileTimeToUnixTime # which shows a perl implementation of it. - my ($low, $high) = unpack("VV", $data); - my $filetime = $high * 2 ** 32 + $low; - my $epoch_time = int(($filetime - 116444736000000000) / 10000000); + my ($lo, $hi) = unpack("VV", $data); +# my $filetime = $high * 2 ** 32 + $low; +# my $epoch_time = int(($filetime - 116444736000000000) / 10000000); + + my $epoch_time; + if ($lo == 0 && $hi == 0) { + $epoch_time = 0; + } else { + $lo -= 0xd53e8000; + $hi -= 0x019db1de; + $epoch_time = int($hi*429.4967296 + $lo/1e7); + }; + $epoch_time = 0 if ($epoch_time < 0); + + # adjust the UNIX epoch time to the local OS's epoch time # (see perlport's Time and Date section) - my $epoch_offset = timegm(0, 0, 0, 1, 0, 70); - $epoch_time += $epoch_offset; + # my $epoch_offset = timegm(0, 0, 0, 1, 0, 70); + # $epoch_time += $epoch_offset; if ($epoch_time < 0 || $epoch_time > 0x7fffffff) { $epoch_time = undef; diff --git a/thirdparty/rr/plugins/autopsyprocarchitecture.pl b/thirdparty/rr/plugins/autopsyprocarchitecture.pl index a03a53f470..d5c0e63f6e 100644 --- a/thirdparty/rr/plugins/autopsyprocarchitecture.pl +++ b/thirdparty/rr/plugins/autopsyprocarchitecture.pl @@ -47,15 +47,14 @@ sub pluginmain { my $arch = $env->get_value("PROCESSOR_ARCHITECTURE")->get_data(); ::rptMsg("" . $arch . ""); }; - ::rptMsg($@) if ($@); - + ::logMsg($@) if ($@); } else { - ::rptMsg($env_path." not found."); + ::logMsg($env_path." not found."); } } else { - ::rptMsg($key_path." not found."); + ::logMsg($key_path." not found."); #::logMsg($key_path." not found."); } } diff --git a/thirdparty/rr/rip.exe b/thirdparty/rr/rip.exe index 2becc8de6b..9a024202f3 100755 Binary files a/thirdparty/rr/rip.exe and b/thirdparty/rr/rip.exe differ diff --git a/thirdparty/rr/rr.exe b/thirdparty/rr/rr.exe index a96bba5daa..889b971889 100755 Binary files a/thirdparty/rr/rr.exe and b/thirdparty/rr/rr.exe differ diff --git a/thunderbirdparser/nbproject/project.xml b/thunderbirdparser/nbproject/project.xml index 52e915e2f3..4eebb3d2f4 100644 --- a/thunderbirdparser/nbproject/project.xml +++ b/thunderbirdparser/nbproject/project.xml @@ -36,7 +36,7 @@ 10 - 10.17 + 10.18
diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/Bundle.properties-MERGED b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/Bundle.properties-MERGED index cdfd241886..90ce00170c 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/Bundle.properties-MERGED +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/Bundle.properties-MERGED @@ -15,6 +15,7 @@ ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index e # {0} - file name # {1} - file ID ThunderbirdMboxFileIngestModule.errorMessage.outOfDiskSpace=Out of disk space. Cannot copy '{0}' (id={1}) to parse. +ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg=Failed to add attachments to email message. ThunderbirdMboxFileIngestModule.moduleName=Email Parser ThunderbirdMboxFileIngestModule.noOpenCase.errMsg=Exception while getting open case. ThunderbirdMboxFileIngestModule.processPst.errMsg.outOfDiskSpace=Out of disk space. Cannot copy {0} to parse. diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java index f1260f269f..d69f2f6988 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java @@ -22,6 +22,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -58,6 +59,9 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.TskException; +import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper; +import org.sleuthkit.datamodel.blackboardutils.FileAttachment; +import org.sleuthkit.datamodel.blackboardutils.MessageAttachments; /** * File-level ingest module that detects MBOX, PST, and vCard files based on @@ -70,6 +74,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { private FileManager fileManager; private IngestJobContext context; private Blackboard blackboard; + private CommunicationArtifactsHelper communicationArtifactsHelper; private Case currentCase; @@ -128,7 +133,20 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { } catch (TskException ex) { logger.log(Level.WARNING, null, ex); } - + + boolean isPstFile = PstParser.isPstFile(abstractFile); + boolean isVcardFile = VcardParser.isVcardFile(abstractFile); + + if (isMbox || isEMLFile || isPstFile || isVcardFile ) { + try { + communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(), + EmailParserModuleFactory.getModuleName(), abstractFile, Account.Type.EMAIL); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Failed to create CommunicationArtifactsHelper for file with object id = %d", abstractFile.getId()), ex); + return ProcessResult.ERROR; + } + } + if (isMbox) { return processMBox(abstractFile); } @@ -137,11 +155,11 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { return processEMLFile(abstractFile); } - if (PstParser.isPstFile(abstractFile)) { + if (isPstFile) { return processPst(abstractFile); } - if (VcardParser.isVcardFile(abstractFile)) { + if (isVcardFile) { return processVcard(abstractFile); } @@ -267,7 +285,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { } else if (mboxParentDir.contains("/ImapMail/")) { //NON-NLS emailFolder = mboxParentDir.substring(mboxParentDir.indexOf("/ImapMail/") + 9); //NON-NLS } - emailFolder = emailFolder + mboxFileName; + emailFolder += mboxFileName; emailFolder = emailFolder.replaceAll(".sbd", ""); //NON-NLS String fileName; @@ -487,8 +505,12 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { * * @return List of attachments */ + @NbBundle.Messages({ + "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg=Failed to add attachments to email message." +}) private List handleAttachments(List attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) { List files = new ArrayList<>(); + List fileAttachments = new ArrayList<>(); for (EmailMessage.Attachment attach : attachments) { String filename = attach.getName(); long crTime = attach.getCrTime(); @@ -501,12 +523,14 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { try { DerivedFile df = fileManager.addDerivedFile(filename, relPath, - size, cTime, crTime, aTime, mTime, true, messageArtifact, "", + size, cTime, crTime, aTime, mTime, true, abstractFile, "", EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType); associateAttachmentWithMesssge(messageArtifact, df); files.add(df); + + fileAttachments.add(new FileAttachment(df)); } catch (TskCoreException ex) { postErrorMessage( NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.errMsg", @@ -516,6 +540,17 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { logger.log(Level.INFO, "", ex); } } + + + try { + communicationArtifactsHelper.addAttachments(messageArtifact, new MessageAttachments(fileAttachments, Collections.emptyList())); + } catch (TskCoreException ex) { + postErrorMessage( + NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.addAttachmentsErrorMsg"), + ""); + logger.log(Level.INFO, "Failed to add attachments to email message.", ex); + } + return files; } diff --git a/unix_setup.sh b/unix_setup.sh index e055c2797b..badf2a1505 100644 --- a/unix_setup.sh +++ b/unix_setup.sh @@ -5,7 +5,7 @@ # NOTE: update_sleuthkit_version.pl updates this value and relies # on it keeping the same name and whitespace. Don't change it. -TSK_VERSION=4.7.0 +TSK_VERSION=4.8.0 # In the beginning...