Merge remote-tracking branch 'upstream/develop' into 5718-invert-control-of-case-uco
@ -2,7 +2,7 @@ Manifest-Version: 1.0
|
|||||||
OpenIDE-Module: org.sleuthkit.autopsy.core/10
|
OpenIDE-Module: org.sleuthkit.autopsy.core/10
|
||||||
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties
|
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties
|
||||||
OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml
|
OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml
|
||||||
OpenIDE-Module-Implementation-Version: 29
|
OpenIDE-Module-Implementation-Version: 30
|
||||||
OpenIDE-Module-Requires: org.openide.windows.WindowManager
|
OpenIDE-Module-Requires: org.openide.windows.WindowManager
|
||||||
AutoUpdate-Show-In-Client: true
|
AutoUpdate-Show-In-Client: true
|
||||||
AutoUpdate-Essential-Module: true
|
AutoUpdate-Essential-Module: true
|
||||||
|
@ -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-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-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.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-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-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
|
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.module.author=Brian Carrier
|
||||||
nbm.needs.restart=true
|
nbm.needs.restart=true
|
||||||
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
|
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
|
||||||
spec.version.base=10.17
|
spec.version.base=10.18
|
||||||
|
|
||||||
|
@ -345,6 +345,7 @@
|
|||||||
<package>org.sleuthkit.autopsy.textextractors.configs</package>
|
<package>org.sleuthkit.autopsy.textextractors.configs</package>
|
||||||
<package>org.sleuthkit.autopsy.texttranslation</package>
|
<package>org.sleuthkit.autopsy.texttranslation</package>
|
||||||
<package>org.sleuthkit.datamodel</package>
|
<package>org.sleuthkit.datamodel</package>
|
||||||
|
<package>org.sleuthkit.datamodel.blackboardutils</package>
|
||||||
</public-packages>
|
</public-packages>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/commons-lang3-3.8.1.jar</runtime-relative-path>
|
<runtime-relative-path>ext/commons-lang3-3.8.1.jar</runtime-relative-path>
|
||||||
@ -527,8 +528,8 @@
|
|||||||
<binary-origin>release/modules/ext/google-http-client-1.29.0.jar</binary-origin>
|
<binary-origin>release/modules/ext/google-http-client-1.29.0.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/sleuthkit-postgresql-4.7.0.jar</runtime-relative-path>
|
<runtime-relative-path>ext/sleuthkit-postgresql-4.8.0.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.7.0.jar</binary-origin>
|
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.8.0.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/bcpkix-jdk15on-1.60.jar</runtime-relative-path>
|
<runtime-relative-path>ext/bcpkix-jdk15on-1.60.jar</runtime-relative-path>
|
||||||
|
@ -354,7 +354,7 @@ UnpackagePortableCaseProgressDialog.title.text=Unpackage Portable Case Progress
|
|||||||
UnpackageWorker.doInBackground.canceled=Unpackaging canceled by user
|
UnpackageWorker.doInBackground.canceled=Unpackaging canceled by user
|
||||||
UnpackageWorker.doInBackground.errorCompressingCase=Error unpackaging case
|
UnpackageWorker.doInBackground.errorCompressingCase=Error unpackaging case
|
||||||
UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executable
|
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.clearRecentCases.text=Clear Recent Cases
|
||||||
UpdateRecentCases.menuItem.empty=-Empty-
|
UpdateRecentCases.menuItem.empty=-Empty-
|
||||||
AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel
|
AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel
|
||||||
|
@ -761,7 +761,7 @@ public class Case {
|
|||||||
@Messages({
|
@Messages({
|
||||||
"Case.progressIndicatorTitle.deletingDataSource=Removing Data Source"
|
"Case.progressIndicatorTitle.deletingDataSource=Removing Data Source"
|
||||||
})
|
})
|
||||||
public static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException {
|
static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException {
|
||||||
synchronized (caseActionSerializationLock) {
|
synchronized (caseActionSerializationLock) {
|
||||||
if (null == currentCase) {
|
if (null == currentCase) {
|
||||||
return;
|
return;
|
||||||
|
@ -150,14 +150,14 @@ class UnpackagePortableCaseProgressDialog extends javax.swing.JDialog implements
|
|||||||
"UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executable",
|
"UnpackageWorker.doInBackground.errorFinding7zip=Could not locate 7-Zip executable",
|
||||||
"UnpackageWorker.doInBackground.errorCompressingCase=Error unpackaging case",
|
"UnpackageWorker.doInBackground.errorCompressingCase=Error unpackaging case",
|
||||||
"UnpackageWorker.doInBackground.canceled=Unpackaging canceled by user",
|
"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
|
@Override
|
||||||
protected Void doInBackground() throws Exception {
|
protected Void doInBackground() throws Exception {
|
||||||
|
|
||||||
// Check to see if this case has been already opened before
|
// Check to see if this case has been already opened before
|
||||||
String caseUnpackedBefore = getCaseIfUnpackedBefore(packagedCase);
|
String caseUnpackedBefore = getCaseIfUnpackedBefore(packagedCase);
|
||||||
if ((!caseUnpackedBefore.isEmpty())
|
if ((!caseUnpackedBefore.isEmpty())
|
||||||
&& (MessageNotifyUtil.Message.confirm(Bundle.UnpackageWorker_doInBackground_previousSeenCase(packagedCase)))) {
|
&& (MessageNotifyUtil.Message.confirm(Bundle.UnpackageWorker_doInBackground_previouslySeenCase()))) {
|
||||||
try {
|
try {
|
||||||
Case.openAsCurrentCase(caseUnpackedBefore);
|
Case.openAsCurrentCase(caseUnpackedBefore);
|
||||||
success.set(true);
|
success.set(true);
|
||||||
|
@ -18,16 +18,21 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.communications.relationships;
|
package org.sleuthkit.autopsy.communications.relationships;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.Account;
|
import org.sleuthkit.datamodel.Account;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.FileAttachment;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.MessageAttachments;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -121,10 +126,29 @@ class AccountSummary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
attachmentCnt += artifact.getChildrenCount();
|
// count the attachments from the TSK_ATTACHMENTS attribute.
|
||||||
for (Content childContent : artifact.getChildren()) {
|
BlackboardAttribute attachmentsAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS));
|
||||||
if (ImageUtils.thumbnailSupported(childContent)) {
|
if (attachmentsAttr != null) {
|
||||||
mediaCnt++;
|
String jsonVal = attachmentsAttr.getValueString();
|
||||||
|
MessageAttachments msgAttachments = new Gson().fromJson(jsonVal, MessageAttachments.class);
|
||||||
|
|
||||||
|
Collection<FileAttachment> 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) {
|
} catch (TskCoreException ex) {
|
||||||
|
@ -36,7 +36,6 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBU
|
|||||||
import org.sleuthkit.datamodel.TimeUtilities;
|
import org.sleuthkit.datamodel.TimeUtilities;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.autopsy.communications.Utils;
|
import org.sleuthkit.autopsy.communications.Utils;
|
||||||
import org.sleuthkit.autopsy.coreutils.PhoneNumUtil;
|
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
|
@ -16,12 +16,13 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.coreutils;
|
package org.sleuthkit.autopsy.communications.relationships;
|
||||||
|
|
||||||
import com.google.i18n.phonenumbers.NumberParseException;
|
import com.google.i18n.phonenumbers.NumberParseException;
|
||||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||||
import com.google.i18n.phonenumbers.Phonenumber;
|
import com.google.i18n.phonenumbers.Phonenumber;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
@ -29,7 +29,6 @@ import org.openide.util.Lookup;
|
|||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||||
import org.sleuthkit.datamodel.Account;
|
import org.sleuthkit.datamodel.Account;
|
||||||
import org.sleuthkit.autopsy.coreutils.PhoneNumUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Account Summary View Panel. This panel shows a list of various counts related
|
* Account Summary View Panel. This panel shows a list of various counts related
|
||||||
|
@ -87,13 +87,9 @@ HtmlPanel.showImagesToggleButton.text=Download Images
|
|||||||
MediaViewImagePanel.tagsMenu.text_1=Tags Menu
|
MediaViewImagePanel.tagsMenu.text_1=Tags Menu
|
||||||
MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00
|
MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00
|
||||||
MediaPlayerPanel.audioSlider.toolTipText=
|
MediaPlayerPanel.audioSlider.toolTipText=
|
||||||
MediaPlayerPanel.rewindButton.text=\u2bc7\u2bc7
|
MediaPlayerPanel.rewindButton.text=
|
||||||
MediaPlayerPanel.fastForwardButton.text=\u2bc8\u2bc8
|
MediaPlayerPanel.fastForwardButton.text=
|
||||||
MediaPlayerPanel.playButton.text=\u25ba
|
MediaPlayerPanel.playButton.text=
|
||||||
MediaPlayerPanel.infoLabel.text=No Errors
|
MediaPlayerPanel.infoLabel.text=No Errors
|
||||||
MediaPlayerPanel.VolumeIcon.text=Volume
|
MediaPlayerPanel.VolumeIcon.text=Volume
|
||||||
MediaPlayerPanel.playBackSpeedLabel.text=Speed:
|
MediaPlayerPanel.playBackSpeedLabel.text=Speed:
|
||||||
ContextViewer.jSourceGoToResultButton.text=Go to Result
|
|
||||||
ContextViewer.jSourceNameLabel.text=jSourceNameLabel
|
|
||||||
ContextViewer.jSourceTextLabel.text=jLabel2
|
|
||||||
ContextViewer.jSourceLabel.text=Source
|
|
||||||
|
@ -10,17 +10,6 @@ AnnotationsContentViewer.title=Annotations
|
|||||||
AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content.
|
AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content.
|
||||||
ApplicationContentViewer.title=Application
|
ApplicationContentViewer.title=Application
|
||||||
ApplicationContentViewer.toolTip=Displays file contents.
|
ApplicationContentViewer.toolTip=Displays file contents.
|
||||||
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.pauseButton.infoLabel.playbackErr=Unable to play video.
|
||||||
FXVideoPanel.progress.bufferingCancelled=media buffering was canceled
|
FXVideoPanel.progress.bufferingCancelled=media buffering was canceled
|
||||||
FXVideoPanel.progress.bufferingInterrupted=media buffering was interrupted
|
FXVideoPanel.progress.bufferingInterrupted=media buffering was interrupted
|
||||||
@ -169,16 +158,12 @@ HtmlPanel.showImagesToggleButton.text=Download Images
|
|||||||
MediaViewImagePanel.tagsMenu.text_1=Tags Menu
|
MediaViewImagePanel.tagsMenu.text_1=Tags Menu
|
||||||
MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00
|
MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00
|
||||||
MediaPlayerPanel.audioSlider.toolTipText=
|
MediaPlayerPanel.audioSlider.toolTipText=
|
||||||
MediaPlayerPanel.rewindButton.text=\u2bc7\u2bc7
|
MediaPlayerPanel.rewindButton.text=
|
||||||
MediaPlayerPanel.fastForwardButton.text=\u2bc8\u2bc8
|
MediaPlayerPanel.fastForwardButton.text=
|
||||||
MediaPlayerPanel.playButton.text=\u25ba
|
MediaPlayerPanel.playButton.text=
|
||||||
MediaPlayerPanel.infoLabel.text=No Errors
|
MediaPlayerPanel.infoLabel.text=No Errors
|
||||||
MediaPlayerPanel.VolumeIcon.text=Volume
|
MediaPlayerPanel.VolumeIcon.text=Volume
|
||||||
MediaPlayerPanel.playBackSpeedLabel.text=Speed:
|
MediaPlayerPanel.playBackSpeedLabel.text=Speed:
|
||||||
ContextViewer.jSourceGoToResultButton.text=Go to Result
|
|
||||||
ContextViewer.jSourceNameLabel.text=jSourceNameLabel
|
|
||||||
ContextViewer.jSourceTextLabel.text=jLabel2
|
|
||||||
ContextViewer.jSourceLabel.text=Source
|
|
||||||
# {0} - tableName
|
# {0} - tableName
|
||||||
SQLiteViewer.readTable.errorText=Error getting rows for table: {0}
|
SQLiteViewer.readTable.errorText=Error getting rows for table: {0}
|
||||||
# {0} - tableName
|
# {0} - tableName
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
<DimensionLayout dim="1">
|
<DimensionLayout dim="1">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<EmptySpace min="0" pref="131" max="32767" attributes="0"/>
|
<EmptySpace min="0" pref="117" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
</Layout>
|
</Layout>
|
||||||
@ -83,7 +83,7 @@
|
|||||||
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
|
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
|
||||||
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
||||||
<Component id="buttonPanel" max="32767" attributes="0"/>
|
<Component id="buttonPanel" max="32767" attributes="0"/>
|
||||||
<Component id="playBackPanel" pref="0" max="32767" attributes="0"/>
|
<Component id="playBackPanel" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
|
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
|
||||||
<Component id="infoLabel" min="-2" max="-2" attributes="0"/>
|
<Component id="infoLabel" min="-2" max="-2" attributes="0"/>
|
||||||
@ -107,7 +107,7 @@
|
|||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
<AuxValues>
|
<AuxValues>
|
||||||
<AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="progressSlider.setUI(new CircularJSliderUI(progressSlider, new CircularJSliderConfiguration(new Dimension(18,18))));"/>
|
<AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="progressSlider.setUI(new CircularJSliderUI(progressSlider, new Dimension(18,18)));"/>
|
||||||
</AuxValues>
|
</AuxValues>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JLabel" name="progressLabel">
|
<Component class="javax.swing.JLabel" name="progressLabel">
|
||||||
@ -123,9 +123,21 @@
|
|||||||
<SubComponents>
|
<SubComponents>
|
||||||
<Component class="javax.swing.JButton" name="playButton">
|
<Component class="javax.swing.JButton" name="playButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
|
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/contentviewers/images/Play-arrow-01.png"/>
|
||||||
|
</Property>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.playButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.playButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[53, 29]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[53, 29]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[49, 29]"/>
|
||||||
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="playButtonActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="playButtonActionPerformed"/>
|
||||||
@ -138,6 +150,9 @@
|
|||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JButton" name="fastForwardButton">
|
<Component class="javax.swing.JButton" name="fastForwardButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
|
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/contentviewers/images/Fast-forward-01.png"/>
|
||||||
|
</Property>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.fastForwardButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.fastForwardButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
@ -153,6 +168,9 @@
|
|||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JButton" name="rewindButton">
|
<Component class="javax.swing.JButton" name="rewindButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
|
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/contentviewers/images/Fast-rewind-01.png"/>
|
||||||
|
</Property>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.rewindButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.rewindButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
@ -172,6 +190,15 @@
|
|||||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.VolumeIcon.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.VolumeIcon.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="horizontalTextPosition" type="int" value="2"/>
|
<Property name="horizontalTextPosition" type="int" value="2"/>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[34, 29]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[34, 29]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[34, 19]"/>
|
||||||
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Constraints>
|
<Constraints>
|
||||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||||
@ -188,15 +215,19 @@
|
|||||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.audioSlider.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.audioSlider.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="value" type="int" value="25"/>
|
<Property name="value" type="int" value="25"/>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[32767, 19]"/>
|
||||||
|
</Property>
|
||||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
<Dimension value="[200, 21]"/>
|
<Dimension value="[200, 19]"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
<Dimension value="[200, 21]"/>
|
<Dimension value="[200, 30]"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property name="requestFocusEnabled" type="boolean" value="false"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<AuxValues>
|
<AuxValues>
|
||||||
<AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="audioSlider.setUI(new CircularJSliderUI(audioSlider, new CircularJSliderConfiguration(new Dimension(15,15))));"/>
|
<AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="audioSlider.setUI(new CircularJSliderUI(audioSlider, new Dimension(15,15)));"/>
|
||||||
</AuxValues>
|
</AuxValues>
|
||||||
<Constraints>
|
<Constraints>
|
||||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||||
@ -223,7 +254,7 @@
|
|||||||
<DimensionLayout dim="0">
|
<DimensionLayout dim="0">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<Component id="playBackSpeedLabel" min="-2" max="-2" attributes="0"/>
|
<Component id="playBackSpeedLabel" min="-2" pref="34" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="32767" attributes="0"/>
|
<EmptySpace max="32767" attributes="0"/>
|
||||||
<Component id="playBackSpeedComboBox" min="-2" max="-2" attributes="0"/>
|
<Component id="playBackSpeedComboBox" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace min="-2" pref="13" max="-2" attributes="0"/>
|
<EmptySpace min="-2" pref="13" max="-2" attributes="0"/>
|
||||||
@ -233,12 +264,15 @@
|
|||||||
<DimensionLayout dim="1">
|
<DimensionLayout dim="1">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
|
<EmptySpace min="-2" pref="7" max="-2" attributes="0"/>
|
||||||
<Group type="103" groupAlignment="3" attributes="0">
|
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
||||||
<Component id="playBackSpeedComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Group type="102" attributes="0">
|
||||||
<Component id="playBackSpeedLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
<EmptySpace min="2" pref="2" max="-2" attributes="0"/>
|
||||||
|
<Component id="playBackSpeedLabel" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<Component id="playBackSpeedComboBox" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace max="32767" attributes="0"/>
|
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
@ -260,14 +294,15 @@
|
|||||||
</Property>
|
</Property>
|
||||||
<Property name="selectedIndex" type="int" value="3"/>
|
<Property name="selectedIndex" type="int" value="3"/>
|
||||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
<Dimension value="[53, 23]"/>
|
<Dimension value="[53, 29]"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
<Dimension value="[53, 23]"/>
|
<Dimension value="[53, 29]"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
<Dimension value="[53, 23]"/>
|
<Dimension value="[53, 29]"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property name="requestFocusEnabled" type="boolean" value="false"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="playBackSpeedComboBoxActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="playBackSpeedComboBoxActionPerformed"/>
|
||||||
@ -281,6 +316,15 @@
|
|||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.playBackSpeedLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.playBackSpeedLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[34, 19]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[34, 19]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[34, 19]"/>
|
||||||
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013-2019 Basis Technology Corp.
|
* Copyright 2013-2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -29,6 +29,7 @@ import java.awt.RenderingHints;
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -61,6 +62,7 @@ import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
|||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
import javafx.embed.swing.JFXPanel;
|
import javafx.embed.swing.JFXPanel;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JSlider;
|
import javax.swing.JSlider;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
@ -72,6 +74,7 @@ import org.freedesktop.gstreamer.Format;
|
|||||||
import org.freedesktop.gstreamer.GstException;
|
import org.freedesktop.gstreamer.GstException;
|
||||||
import org.freedesktop.gstreamer.event.SeekFlags;
|
import org.freedesktop.gstreamer.event.SeekFlags;
|
||||||
import org.freedesktop.gstreamer.event.SeekType;
|
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
|
* 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",
|
".wav",
|
||||||
".webm",
|
".webm",
|
||||||
".wma",
|
".wma",
|
||||||
".wmv",}; //NON-NLS
|
".wmv"}; //NON-NLS
|
||||||
private static final List<String> MIME_TYPES = Arrays.asList(
|
private static final List<String> MIME_TYPES = Arrays.asList(
|
||||||
"video/3gpp",
|
"video/3gpp",
|
||||||
"video/3gpp2",
|
"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 PROGRESS_SLIDER_SIZE = 2000;
|
||||||
private static final int SKIP_IN_SECONDS = 30;
|
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;
|
private ExtractMedia extractMediaWorker;
|
||||||
|
|
||||||
//Serialize setting the value of the Video progress slider.
|
//Serialize setting the value of the Video progress slider.
|
||||||
//The slider is a shared resource between the VideoPanelUpdater
|
//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;
|
private final Semaphore sliderLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new form MediaViewVideoPanel
|
* Creates a new MediaPlayerPanel
|
||||||
*/
|
*/
|
||||||
public MediaPlayerPanel() throws GstException, UnsatisfiedLinkError {
|
public MediaPlayerPanel() throws GstException, UnsatisfiedLinkError {
|
||||||
initComponents();
|
initComponents();
|
||||||
@ -216,6 +222,14 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
|||||||
//True for fairness. In other words,
|
//True for fairness. In other words,
|
||||||
//acquire() calls are processed in order of invocation.
|
//acquire() calls are processed in order of invocation.
|
||||||
sliderLock = new Semaphore(1, true);
|
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() {
|
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) -> {
|
audioSlider.addChangeListener((ChangeEvent event) -> {
|
||||||
if (audioSlider.getValueIsAdjusting()) {
|
if (audioSlider.getValueIsAdjusting()) {
|
||||||
double audioPercent = (audioSlider.getValue() * 2.0) / 100.0;
|
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) {
|
public void stateChanged(GstObject go, State oldState, State currentState, State pendingState) {
|
||||||
if (State.PLAYING.equals(currentState)) {
|
if (State.PLAYING.equals(currentState)) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
playButton.setText("||");
|
playButton.setIcon(pauseIcon);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
SwingUtilities.invokeLater(() -> {
|
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.
|
// Initialize Gstreamer. It is safe to call this for every file.
|
||||||
// It was moved here from the constructor because having it happen
|
// It was moved here from the constructor because having it happen
|
||||||
// earlier resulted in conflicts on Linux.
|
// earlier resulted in conflicts on Linux. See JIRA-5888.
|
||||||
Gst.init();
|
if (!PlatformUtil.isWindowsOS()) {
|
||||||
|
Gst.init();
|
||||||
|
}
|
||||||
|
|
||||||
//Video is ready for playback. Create new components
|
//Video is ready for playback. Create new components
|
||||||
gstPlayBin = new PlayBin("VideoPlayer", tempFile.toURI());
|
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.
|
* Custom view for the JSlider.
|
||||||
*/
|
*/
|
||||||
private class CircularJSliderUI extends BasicSliderUI {
|
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
|
* 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
|
* @param config Configuration object. Contains info about thumb
|
||||||
* dimensions and colors.
|
* dimensions and colors.
|
||||||
*/
|
*/
|
||||||
public CircularJSliderUI(JSlider slider, CircularJSliderConfiguration config) {
|
public CircularJSliderUI(JSlider slider, Dimension thumbDimension) {
|
||||||
super(slider);
|
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
|
@Override
|
||||||
protected Dimension getThumbSize() {
|
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
|
@Override
|
||||||
public void paintThumb(Graphics graphic) {
|
public void paintThumb(Graphics graphic) {
|
||||||
@ -662,8 +667,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
|||||||
|
|
||||||
//Change the thumb view from the rectangle
|
//Change the thumb view from the rectangle
|
||||||
//controller to an oval.
|
//controller to an oval.
|
||||||
graphic.setColor(config.getThumbColor());
|
graphic.setColor(thumbColor);
|
||||||
Dimension thumbDimension = config.getThumbDimension();
|
|
||||||
graphic.fillOval(thumb.x, thumb.y, thumbDimension.width, thumbDimension.height);
|
graphic.fillOval(thumb.x, thumb.y, thumbDimension.width, thumbDimension.height);
|
||||||
|
|
||||||
//Preserve the graphics original color
|
//Preserve the graphics original color
|
||||||
@ -686,12 +690,12 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
|||||||
Color original = graphic.getColor();
|
Color original = graphic.getColor();
|
||||||
|
|
||||||
//Paint the seen side
|
//Paint the seen side
|
||||||
graphic.setColor(config.getSeenTrackColor());
|
graphic.setColor(trackSeen);
|
||||||
graphic.drawLine(track.x, track.y + track.height / 2,
|
graphic.drawLine(track.x, track.y + track.height / 2,
|
||||||
thumbX, thumbY + track.height / 2);
|
thumbX, thumbY + track.height / 2);
|
||||||
|
|
||||||
//Paint the unseen side
|
//Paint the unseen side
|
||||||
graphic.setColor(config.getUnseenTrackColor());
|
graphic.setColor(trackUnseen);
|
||||||
graphic.drawLine(thumbX, thumbY + track.height / 2,
|
graphic.drawLine(thumbX, thumbY + track.height / 2,
|
||||||
track.x + track.width, track.y + 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
|
@Override
|
||||||
protected TrackListener createTrackListener(JSlider slider) {
|
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
|
@Override
|
||||||
@ -715,7 +738,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
|||||||
int value = this.valueForXPosition(mousePosition.x);
|
int value = this.valueForXPosition(mousePosition.x);
|
||||||
|
|
||||||
//Lock the slider down, which is a shared resource.
|
//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
|
//slider in sync with the video position, so without
|
||||||
//proper locking our change could be overwritten.
|
//proper locking our change could be overwritten.
|
||||||
sliderLock.acquireUninterruptibly();
|
sliderLock.acquireUninterruptibly();
|
||||||
@ -738,43 +761,6 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
|||||||
|
|
||||||
super.update(graphic, component);
|
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.setVerticalGroup(
|
||||||
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGap(0, 131, Short.MAX_VALUE)
|
.addGap(0, 117, Short.MAX_VALUE)
|
||||||
);
|
);
|
||||||
|
|
||||||
progressSlider.setValue(0);
|
progressSlider.setValue(0);
|
||||||
@ -818,13 +804,17 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
|||||||
progressSlider.setDoubleBuffered(true);
|
progressSlider.setDoubleBuffered(true);
|
||||||
progressSlider.setMinimumSize(new java.awt.Dimension(36, 21));
|
progressSlider.setMinimumSize(new java.awt.Dimension(36, 21));
|
||||||
progressSlider.setPreferredSize(new java.awt.Dimension(200, 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
|
org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.progressLabel.text")); // NOI18N
|
||||||
|
|
||||||
buttonPanel.setLayout(new java.awt.GridBagLayout());
|
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
|
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() {
|
playButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
playButtonActionPerformed(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);
|
gridBagConstraints.insets = new java.awt.Insets(5, 6, 0, 0);
|
||||||
buttonPanel.add(playButton, gridBagConstraints);
|
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
|
org.openide.awt.Mnemonics.setLocalizedText(fastForwardButton, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.fastForwardButton.text")); // NOI18N
|
||||||
fastForwardButton.addActionListener(new java.awt.event.ActionListener() {
|
fastForwardButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
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);
|
gridBagConstraints.insets = new java.awt.Insets(5, 6, 0, 0);
|
||||||
buttonPanel.add(fastForwardButton, gridBagConstraints);
|
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
|
org.openide.awt.Mnemonics.setLocalizedText(rewindButton, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.rewindButton.text")); // NOI18N
|
||||||
rewindButton.addActionListener(new java.awt.event.ActionListener() {
|
rewindButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
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
|
org.openide.awt.Mnemonics.setLocalizedText(VolumeIcon, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.VolumeIcon.text")); // NOI18N
|
||||||
VolumeIcon.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
|
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 = new java.awt.GridBagConstraints();
|
||||||
gridBagConstraints.gridx = 3;
|
gridBagConstraints.gridx = 3;
|
||||||
gridBagConstraints.gridy = 0;
|
gridBagConstraints.gridy = 0;
|
||||||
@ -880,9 +875,11 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
|||||||
audioSlider.setMinorTickSpacing(5);
|
audioSlider.setMinorTickSpacing(5);
|
||||||
audioSlider.setToolTipText(org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.audioSlider.toolTipText")); // NOI18N
|
audioSlider.setToolTipText(org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.audioSlider.toolTipText")); // NOI18N
|
||||||
audioSlider.setValue(25);
|
audioSlider.setValue(25);
|
||||||
audioSlider.setMinimumSize(new java.awt.Dimension(200, 21));
|
audioSlider.setMaximumSize(new java.awt.Dimension(32767, 19));
|
||||||
audioSlider.setPreferredSize(new java.awt.Dimension(200, 21));
|
audioSlider.setMinimumSize(new java.awt.Dimension(200, 19));
|
||||||
audioSlider.setUI(new CircularJSliderUI(audioSlider, new CircularJSliderConfiguration(new Dimension(15,15))));
|
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 = new java.awt.GridBagConstraints();
|
||||||
gridBagConstraints.gridx = 4;
|
gridBagConstraints.gridx = 4;
|
||||||
gridBagConstraints.gridy = 0;
|
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.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.setSelectedIndex(3);
|
||||||
playBackSpeedComboBox.setMaximumSize(new java.awt.Dimension(53, 23));
|
playBackSpeedComboBox.setMaximumSize(new java.awt.Dimension(53, 29));
|
||||||
playBackSpeedComboBox.setMinimumSize(new java.awt.Dimension(53, 23));
|
playBackSpeedComboBox.setMinimumSize(new java.awt.Dimension(53, 29));
|
||||||
playBackSpeedComboBox.setPreferredSize(new java.awt.Dimension(53, 23));
|
playBackSpeedComboBox.setPreferredSize(new java.awt.Dimension(53, 29));
|
||||||
|
playBackSpeedComboBox.setRequestFocusEnabled(false);
|
||||||
playBackSpeedComboBox.addActionListener(new java.awt.event.ActionListener() {
|
playBackSpeedComboBox.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
playBackSpeedComboBoxActionPerformed(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
|
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);
|
javax.swing.GroupLayout playBackPanelLayout = new javax.swing.GroupLayout(playBackPanel);
|
||||||
playBackPanel.setLayout(playBackPanelLayout);
|
playBackPanel.setLayout(playBackPanelLayout);
|
||||||
playBackPanelLayout.setHorizontalGroup(
|
playBackPanelLayout.setHorizontalGroup(
|
||||||
playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(playBackPanelLayout.createSequentialGroup()
|
.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)
|
.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)
|
.addComponent(playBackSpeedComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addGap(13, 13, 13))
|
.addGap(13, 13, 13))
|
||||||
@ -922,11 +923,13 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
|||||||
playBackPanelLayout.setVerticalGroup(
|
playBackPanelLayout.setVerticalGroup(
|
||||||
playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(playBackPanelLayout.createSequentialGroup()
|
.addGroup(playBackPanelLayout.createSequentialGroup()
|
||||||
.addGap(6, 6, 6)
|
.addGap(7, 7, 7)
|
||||||
.addGroup(playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
.addGroup(playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
|
||||||
.addComponent(playBackSpeedComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addGroup(playBackPanelLayout.createSequentialGroup()
|
||||||
.addComponent(playBackSpeedLabel))
|
.addGap(2, 2, 2)
|
||||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
.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);
|
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)
|
.addGap(5, 5, 5)
|
||||||
.addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
|
.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(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)
|
.addGap(14, 14, 14)
|
||||||
.addComponent(infoLabel))
|
.addComponent(infoLabel))
|
||||||
);
|
);
|
||||||
@ -1002,13 +1005,21 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
|||||||
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||||
//Skip 30 seconds.
|
//Skip 30 seconds.
|
||||||
long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
|
long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
|
||||||
|
//Don't allow skipping within 2 seconds of video ending. Skipping right to
|
||||||
//Ignore fast forward requests if there are less than 30 seconds left.
|
//the end causes undefined behavior for some gstreamer plugins.
|
||||||
if (currentTime + fastForwardDelta >= duration) {
|
long twoSecondsInNano = TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);
|
||||||
|
if((duration - currentTime) <= twoSecondsInNano) {
|
||||||
return;
|
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();
|
double playBackRate = getPlayBackRate();
|
||||||
gstPlayBin.seek(playBackRate,
|
gstPlayBin.seek(playBackRate,
|
||||||
Format.TIME,
|
Format.TIME,
|
||||||
|
@ -39,6 +39,8 @@ import org.openide.nodes.Children;
|
|||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||||
import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
|
import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
|
||||||
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
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.autopsy.directorytree.NewWindowViewAction;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
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_EMAIL_MSG;
|
||||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
|
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
|
||||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE;
|
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_SUBJECT;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.blackboardutils.FileAttachment;
|
import org.sleuthkit.datamodel.blackboardutils.FileAttachment;
|
||||||
import org.sleuthkit.datamodel.blackboardutils.MessageAttachments;
|
import org.sleuthkit.datamodel.blackboardutils.MessageAttachments;
|
||||||
@ -370,7 +374,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
artifact = node.getLookup().lookup(BlackboardArtifact.class);
|
artifact = getNodeArtifact(node);
|
||||||
if (artifact == null) {
|
if (artifact == null) {
|
||||||
resetComponent();
|
resetComponent();
|
||||||
return;
|
return;
|
||||||
@ -465,7 +469,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSupported(Node node) {
|
public boolean isSupported(Node node) {
|
||||||
BlackboardArtifact nodeArtifact = node.getLookup().lookup(BlackboardArtifact.class);
|
BlackboardArtifact nodeArtifact = getNodeArtifact(node);
|
||||||
|
|
||||||
if (nodeArtifact == null) {
|
if (nodeArtifact == null) {
|
||||||
return false;
|
return false;
|
||||||
@ -498,10 +502,56 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
|
|||||||
|| artifactTypeID == TSK_MESSAGE.getTypeID();
|
|| 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<BlackboardArtifact> 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
|
@Override
|
||||||
public int isPreferred(Node node) {
|
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)) {
|
if (isSupported(node)) {
|
||||||
return 7;
|
BlackboardArtifact nodeArtifact = node.getLookup().lookup(BlackboardArtifact.class);
|
||||||
|
if (nodeArtifact != null) {
|
||||||
|
return 7;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
ContextViewer.jSourceGoToResultButton.text=Go to Result
|
||||||
|
ContextViewer.jSourceTextLabel.text=jLabel2
|
||||||
|
ContextViewer.jSourceNameLabel.text=jSourceNameLabel
|
||||||
|
ContextViewer.jSourceLabel.text=Source
|
@ -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.
|
@ -65,16 +65,12 @@
|
|||||||
<Component class="javax.swing.JButton" name="jSourceGoToResultButton">
|
<Component class="javax.swing.JButton" name="jSourceGoToResultButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContextViewer.jSourceGoToResultButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jSourceGoToResultButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jSourceGoToResultButtonActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jSourceGoToResultButtonActionPerformed"/>
|
||||||
</Events>
|
</Events>
|
||||||
<AuxValues>
|
|
||||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
|
||||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
|
||||||
</AuxValues>
|
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JLabel" name="jSourceLabel">
|
<Component class="javax.swing.JLabel" name="jSourceLabel">
|
||||||
<Properties>
|
<Properties>
|
||||||
@ -82,25 +78,21 @@
|
|||||||
<Font name="Dialog" size="14" style="1"/>
|
<Font name="Dialog" size="14" style="1"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContextViewer.jSourceLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jSourceLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
<AuxValues>
|
|
||||||
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
|
||||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
|
||||||
</AuxValues>
|
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JLabel" name="jSourceNameLabel">
|
<Component class="javax.swing.JLabel" name="jSourceNameLabel">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContextViewer.jSourceNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jSourceNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JLabel" name="jSourceTextLabel">
|
<Component class="javax.swing.JLabel" name="jSourceTextLabel">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContextViewer.jSourceTextLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jSourceTextLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
@ -16,7 +16,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.contentviewers;
|
package org.sleuthkit.autopsy.contentviewers.contextviewer;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -79,8 +79,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
|||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
|
|
||||||
javax.swing.JButton jSourceGoToResultButton = new javax.swing.JButton();
|
jSourceGoToResultButton = new javax.swing.JButton();
|
||||||
javax.swing.JLabel jSourceLabel = new javax.swing.JLabel();
|
jSourceLabel = new javax.swing.JLabel();
|
||||||
jSourceNameLabel = new javax.swing.JLabel();
|
jSourceNameLabel = new javax.swing.JLabel();
|
||||||
jSourceTextLabel = 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.title=Context",
|
||||||
"ContextViewer.toolTip=Displays context for selected file."
|
"ContextViewer.toolTip=Displays context for selected file."
|
||||||
})
|
})
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return Bundle.ContextViewer_title();
|
return Bundle.ContextViewer_title();
|
||||||
@ -188,6 +188,7 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetComponent() {
|
public void resetComponent() {
|
||||||
|
jSourceGoToResultButton.setVisible(false);
|
||||||
setSourceName("");
|
setSourceName("");
|
||||||
setSourceText("");
|
setSourceText("");
|
||||||
}
|
}
|
||||||
@ -238,23 +239,23 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
|||||||
boolean foundASource = false;
|
boolean foundASource = false;
|
||||||
for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SOURCE_CONTEXT_ARTIFACTS) {
|
for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SOURCE_CONTEXT_ARTIFACTS) {
|
||||||
List<BlackboardArtifact> artifactsList = tskCase.getBlackboardArtifacts(artifactType, sourceFile.getId());
|
List<BlackboardArtifact> artifactsList = tskCase.getBlackboardArtifacts(artifactType, sourceFile.getId());
|
||||||
|
|
||||||
foundASource = !artifactsList.isEmpty();
|
foundASource = !artifactsList.isEmpty();
|
||||||
for (BlackboardArtifact contextArtifact : artifactsList) {
|
for (BlackboardArtifact contextArtifact : artifactsList) {
|
||||||
addSourceEntry(contextArtifact);
|
addSourceEntry(contextArtifact);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
jSourceGoToResultButton.setVisible(true);
|
||||||
if (foundASource == false) {
|
if (foundASource == false) {
|
||||||
setSourceName("Unknown");
|
setSourceName("Unknown");
|
||||||
showSourceText(false);
|
showSourceText(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a source context entry for the selected file based on the given context
|
* Adds a source context entry for the selected file based on the given
|
||||||
* providing artifact.
|
* context providing artifact.
|
||||||
*
|
*
|
||||||
* @param artifact Artifact that may provide context.
|
* @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.
|
* Sets the source text string.
|
||||||
*
|
*
|
||||||
* @param nameLabel String value for source text.
|
* @param text String value for source text.
|
||||||
*/
|
*/
|
||||||
private void setSourceText(String text) {
|
private void setSourceText(String text) {
|
||||||
jSourceTextLabel.setText(text);
|
jSourceTextLabel.setText(text);
|
||||||
showSourceText(true);
|
showSourceText(!text.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSourceText(boolean isVisible) {
|
private void showSourceText(boolean show) {
|
||||||
jSourceTextLabel.setVisible(isVisible);
|
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.email=Email",
|
||||||
"ContextViewer.messageFrom=From",
|
"ContextViewer.messageFrom=From",
|
||||||
"ContextViewer.messageTo=To",
|
"ContextViewer.messageTo=To",
|
||||||
"ContextViewer.messageOn=On",
|
"ContextViewer.messageOn=On",})
|
||||||
})
|
|
||||||
private String msgArtifactToAbbreviatedString(BlackboardArtifact artifact) throws TskCoreException {
|
private String msgArtifactToAbbreviatedString(BlackboardArtifact artifact) throws TskCoreException {
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(ARTIFACT_STR_MAX_LEN);
|
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
|
* Looks up specified attribute in the given map and, if found, appends its
|
||||||
* value to the given string builder.
|
* value to the given string builder.
|
||||||
*
|
*
|
||||||
* @param sb String builder to append to.
|
* @param sb String builder to append to.
|
||||||
* @param attribType Attribute type to look for.
|
* @param attribType Attribute type to look for.
|
||||||
* @param attributesMap Attributes map.
|
* @param attributesMap Attributes map.
|
||||||
* @param prependStr Optional string that is prepended before the attribute
|
* @param prependStr Optional string that is prepended before the
|
||||||
* value.
|
* attribute value.
|
||||||
*/
|
*/
|
||||||
private void appendAttributeString(StringBuilder sb, BlackboardAttribute.ATTRIBUTE_TYPE attribType,
|
private void appendAttributeString(StringBuilder sb, BlackboardAttribute.ATTRIBUTE_TYPE attribType,
|
||||||
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributesMap, String prependStr) {
|
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> 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
|
// 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 jSourceNameLabel;
|
||||||
private javax.swing.JLabel jSourceTextLabel;
|
private javax.swing.JLabel jSourceTextLabel;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
BIN
Core/src/org/sleuthkit/autopsy/contentviewers/images/Fast-forward-01.png
Executable file
After Width: | Height: | Size: 415 B |
BIN
Core/src/org/sleuthkit/autopsy/contentviewers/images/Fast-rewind-01.png
Executable file
After Width: | Height: | Size: 436 B |
BIN
Core/src/org/sleuthkit/autopsy/contentviewers/images/Pause-01.png
Executable file
After Width: | Height: | Size: 247 B |
BIN
Core/src/org/sleuthkit/autopsy/contentviewers/images/Play-arrow-01.png
Executable file
After Width: | Height: | Size: 418 B |
@ -610,7 +610,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|
|||||||
* display string.
|
* display string.
|
||||||
*
|
*
|
||||||
* @param element JSON element to convert
|
* @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.
|
* @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.
|
* 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 startIndent Starting indentation for the element.
|
||||||
* @param sb String builder to append to.
|
* @param sb String builder to append to.
|
||||||
*/
|
*/
|
||||||
|
@ -304,9 +304,6 @@ OpenReportAction.actionPerformed.ReportFileOpenPermissionDeniedMessage=Permissio
|
|||||||
PoolNode.createSheet.name.desc=no description
|
PoolNode.createSheet.name.desc=no description
|
||||||
PoolNode.createSheet.name.displayName=Name
|
PoolNode.createSheet.name.displayName=Name
|
||||||
PoolNode.createSheet.name.name=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.desc=no description
|
||||||
PoolNode.createSheet.type.displayName=Type
|
PoolNode.createSheet.type.displayName=Type
|
||||||
PoolNode.createSheet.type.name=Type
|
PoolNode.createSheet.type.name=Type
|
||||||
|
@ -227,6 +227,7 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
|||||||
// maps the artifact type to its child node
|
// maps the artifact type to its child node
|
||||||
private final HashMap<BlackboardArtifact.Type, TypeNode> typeNodeList = new HashMap<>();
|
private final HashMap<BlackboardArtifact.Type, TypeNode> typeNodeList = new HashMap<>();
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
TypeFactory() {
|
TypeFactory() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2017-2018 Basis Technology Corp.
|
* Copyright 2017-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -23,6 +23,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction;
|
||||||
import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction;
|
import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction;
|
||||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||||
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
|
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
|
||||||
@ -37,7 +38,7 @@ import org.sleuthkit.datamodel.SpecialDirectory;
|
|||||||
* Parent class for special directory types (Local and Virtual)
|
* Parent class for special directory types (Local and Virtual)
|
||||||
*/
|
*/
|
||||||
public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode<SpecialDirectory> {
|
public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode<SpecialDirectory> {
|
||||||
|
|
||||||
public SpecialDirectoryNode(SpecialDirectory sd) {
|
public SpecialDirectoryNode(SpecialDirectory sd) {
|
||||||
super(sd);
|
super(sd);
|
||||||
}
|
}
|
||||||
@ -68,6 +69,7 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode<Spec
|
|||||||
if (content.isDataSource()) {
|
if (content.isDataSource()) {
|
||||||
actions.add(new ViewSummaryInformationAction(content.getId()));
|
actions.add(new ViewSummaryInformationAction(content.getId()));
|
||||||
actions.add(new RunIngestModulesAction(Collections.<Content>singletonList(content)));
|
actions.add(new RunIngestModulesAction(Collections.<Content>singletonList(content)));
|
||||||
|
actions.add(new DeleteDataSourceAction(content.getId()));
|
||||||
} else {
|
} else {
|
||||||
actions.add(new RunIngestModulesAction(content));
|
actions.add(new RunIngestModulesAction(content));
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
|||||||
/**
|
/**
|
||||||
* Indicates if the display name of the XRY key is a recognized type.
|
* Indicates if the display name of the XRY key is a recognized type.
|
||||||
*
|
*
|
||||||
* @param xryKey
|
* @param name
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static boolean contains(String name) {
|
public static boolean contains(String name) {
|
||||||
@ -125,7 +125,7 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
|||||||
* IllegalArgumentException is thrown. Test all membership with
|
* IllegalArgumentException is thrown. Test all membership with
|
||||||
* contains() before hand.
|
* contains() before hand.
|
||||||
*
|
*
|
||||||
* @param xryKey
|
* @param name
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static XryKey fromDisplayName(String name) {
|
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.
|
* Indicates if the display name of the XRY key is a recognized type.
|
||||||
*
|
*
|
||||||
* @param xryKey
|
* @param name
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static boolean contains(String name) {
|
public static boolean contains(String name) {
|
||||||
@ -236,7 +236,7 @@ final class XRYMessagesFileParser implements XRYFileParser {
|
|||||||
* IllegalArgumentException is thrown. Test all membership with
|
* IllegalArgumentException is thrown. Test all membership with
|
||||||
* contains() before hand.
|
* contains() before hand.
|
||||||
*
|
*
|
||||||
* @param xryKey
|
* @param name
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static XryMetaKey fromDisplayName(String name) {
|
public static XryMetaKey fromDisplayName(String name) {
|
||||||
|
@ -41,7 +41,6 @@ import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction;
|
|||||||
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode;
|
import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode;
|
||||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
||||||
import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory;
|
import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory;
|
||||||
@ -259,15 +258,20 @@ public class DataResultFilterNode extends FilterNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Node[] createNodes(Node key) {
|
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);
|
BlackboardArtifact art = key.getLookup().lookup(BlackboardArtifact.class);
|
||||||
if (art != null
|
if (art != null && filterArtifacts
|
||||||
&& filterArtifacts
|
&& ((FilterNodeUtils.showMessagesInDatasourceTree() == false)
|
||||||
&& art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
|
|| (FilterNodeUtils.showMessagesInDatasourceTree()
|
||||||
&& art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) {
|
&& art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
|
||||||
|
&& art.getArtifactTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()))) {
|
||||||
return new Node[]{};
|
return new Node[]{};
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Node[]{new DataResultFilterNode(key, sourceEm)};
|
return new Node[]{new DataResultFilterNode(key, sourceEm)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.directorytree;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
@ -33,17 +32,12 @@ import org.sleuthkit.autopsy.core.UserPreferences;
|
|||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.datamodel.AbstractContentNode;
|
import org.sleuthkit.autopsy.datamodel.AbstractContentNode;
|
||||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
||||||
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction;
|
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.Directory;
|
|
||||||
import org.sleuthkit.datamodel.Image;
|
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
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
|
* A node filter (decorator) that sets the actions for a node in the tree view
|
||||||
@ -137,11 +131,18 @@ class DirectoryTreeFilterNode extends FilterNode {
|
|||||||
numVisibleChildren--;
|
numVisibleChildren--;
|
||||||
}
|
}
|
||||||
} else if (child instanceof BlackboardArtifact) {
|
} else if (child instanceof BlackboardArtifact) {
|
||||||
BlackboardArtifact bba = (BlackboardArtifact) child;
|
|
||||||
|
if (FilterNodeUtils.showMessagesInDatasourceTree()) {
|
||||||
// Only message type artifacts are displayed in the tree
|
// In older versions of Autopsy, attachments were children of email/message artifacts
|
||||||
if ((bba.getArtifactTypeID() != ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID())
|
// and hence email/messages with attachments are shown in the directory tree.
|
||||||
&& (bba.getArtifactTypeID() != ARTIFACT_TYPE.TSK_MESSAGE.getTypeID())) {
|
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--;
|
numVisibleChildren--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2019 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sleuthkit.autopsy.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,7 +16,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.coreutils;
|
package org.sleuthkit.autopsy.exceptions;
|
||||||
|
|
||||||
import java.util.logging.Filter;
|
import java.util.logging.Filter;
|
||||||
import java.util.logging.Handler;
|
import java.util.logging.Handler;
|
||||||
@ -26,6 +26,9 @@ import java.util.logging.SimpleFormatter;
|
|||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
import org.netbeans.core.NbErrorManager;
|
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.
|
* Replaces default NetBeans exception handler. Displays messages in a dialog.
|
@ -44,7 +44,7 @@ FileSearchPanel.hashSetCheckbox.text=Hash Set:
|
|||||||
FileSearchPanel.tagsCheckbox.text=Tag:
|
FileSearchPanel.tagsCheckbox.text=Tag:
|
||||||
FileSearchPanel.interestingItemsCheckbox.text=Interesting Item:
|
FileSearchPanel.interestingItemsCheckbox.text=Interesting Item:
|
||||||
FileSearchPanel.scoreCheckbox.text=Has Score:
|
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.notableCheckbox.text=Must have been tagged as notable
|
||||||
FileSearchPanel.objectsCheckbox.text=Object Detected:
|
FileSearchPanel.objectsCheckbox.text=Object Detected:
|
||||||
ResultsPanel.currentPageLabel.text=Page: -
|
ResultsPanel.currentPageLabel.text=Page: -
|
||||||
|
@ -150,7 +150,7 @@ FileSearchPanel.hashSetCheckbox.text=Hash Set:
|
|||||||
FileSearchPanel.tagsCheckbox.text=Tag:
|
FileSearchPanel.tagsCheckbox.text=Tag:
|
||||||
FileSearchPanel.interestingItemsCheckbox.text=Interesting Item:
|
FileSearchPanel.interestingItemsCheckbox.text=Interesting Item:
|
||||||
FileSearchPanel.scoreCheckbox.text=Has Score:
|
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.notableCheckbox.text=Must have been tagged as notable
|
||||||
FileSearchPanel.objectsCheckbox.text=Object Detected:
|
FileSearchPanel.objectsCheckbox.text=Object Detected:
|
||||||
FileSorter.SortingMethod.datasource.displayName=Data Source
|
FileSorter.SortingMethod.datasource.displayName=Data Source
|
||||||
|
@ -47,7 +47,7 @@ final class FileSearchData {
|
|||||||
UNIQUE(0, 1, Bundle.FileSearchData_Frequency_unique_displayName()),
|
UNIQUE(0, 1, Bundle.FileSearchData_Frequency_unique_displayName()),
|
||||||
RARE(1, 10, Bundle.FileSearchData_Frequency_rare_displayName()),
|
RARE(1, 10, Bundle.FileSearchData_Frequency_rare_displayName()),
|
||||||
COMMON(2, 100, Bundle.FileSearchData_Frequency_common_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()),
|
KNOWN(4, 0, Bundle.FileSearchData_Frequency_known_displayName()),
|
||||||
UNKNOWN(5, 0, Bundle.FileSearchData_Frequency_unknown_displayName());
|
UNKNOWN(5, 0, Bundle.FileSearchData_Frequency_unknown_displayName());
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2019 Basis Technology Corp.
|
* Copyright 2019-2020 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -22,21 +22,17 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeIns
|
|||||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
|
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
|
||||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
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.FileSize;
|
||||||
import org.sleuthkit.autopsy.filequery.FileSearchData.FileType;
|
import org.sleuthkit.autopsy.filequery.FileSearchData.FileType;
|
||||||
import org.sleuthkit.autopsy.filequery.FileSearchData.Frequency;
|
import org.sleuthkit.autopsy.filequery.FileSearchData.Frequency;
|
||||||
import org.sleuthkit.autopsy.filequery.FileSearchData.Score;
|
import org.sleuthkit.autopsy.filequery.FileSearchData.Score;
|
||||||
|
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
@ -48,8 +44,6 @@ import org.sleuthkit.datamodel.TskData;
|
|||||||
*/
|
*/
|
||||||
class FileSearchFiltering {
|
class FileSearchFiltering {
|
||||||
|
|
||||||
private final static Logger logger = Logger.getLogger(FileSearchFiltering.class.getName());
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the given filters to get a list of matching files.
|
* Run the given filters to get a list of matching files.
|
||||||
*
|
*
|
||||||
@ -61,18 +55,9 @@ class FileSearchFiltering {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
static List<ResultFile> runQueries(List<FileFilter> filters, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException {
|
static List<ResultFile> runQueries(List<FileFilter> filters, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException {
|
||||||
|
|
||||||
if (caseDb == null) {
|
if (caseDb == null) {
|
||||||
throw new FileSearchException("Case DB parameter is null"); // NON-NLS
|
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
|
// Combine all the SQL queries from the filters into one query
|
||||||
String combinedQuery = "";
|
String combinedQuery = "";
|
||||||
for (FileFilter filter : filters) {
|
for (FileFilter filter : filters) {
|
||||||
@ -112,8 +97,6 @@ class FileSearchFiltering {
|
|||||||
private static List<ResultFile> getResultList(List<FileFilter> filters, String combinedQuery, SleuthkitCase caseDb, EamDb centralRepoDb) throws TskCoreException, FileSearchException {
|
private static List<ResultFile> getResultList(List<FileFilter> filters, String combinedQuery, SleuthkitCase caseDb, EamDb centralRepoDb) throws TskCoreException, FileSearchException {
|
||||||
// Get all matching abstract files
|
// Get all matching abstract files
|
||||||
List<ResultFile> resultList = new ArrayList<>();
|
List<ResultFile> resultList = new ArrayList<>();
|
||||||
|
|
||||||
logger.log(Level.INFO, "Running SQL query: {0}", combinedQuery);
|
|
||||||
List<AbstractFile> sqlResults = caseDb.findAllFilesWhere(combinedQuery);
|
List<AbstractFile> sqlResults = caseDb.findAllFilesWhere(combinedQuery);
|
||||||
|
|
||||||
// If there are no results, return now
|
// If there are no results, return now
|
||||||
|
@ -404,15 +404,6 @@
|
|||||||
<Connection code="new DefaultListModel<ParentSearchTerm>()" type="code"/>
|
<Connection code="new DefaultListModel<ParentSearchTerm>()" type="code"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="enabled" type="boolean" value="false"/>
|
<Property name="enabled" type="boolean" value="false"/>
|
||||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="null"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[0, 30]"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[0, 30]"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="visibleRowCount" type="int" value="4"/>
|
<Property name="visibleRowCount" type="int" value="4"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<Events>
|
<Events>
|
||||||
@ -457,12 +448,6 @@
|
|||||||
<Connection code="new DefaultListModel<String>()" type="code"/>
|
<Connection code="new DefaultListModel<String>()" type="code"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="enabled" type="boolean" value="false"/>
|
<Property name="enabled" type="boolean" value="false"/>
|
||||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[0, 30]"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[0, 30]"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="visibleRowCount" type="int" value="3"/>
|
<Property name="visibleRowCount" type="int" value="3"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<AuxValues>
|
<AuxValues>
|
||||||
@ -573,12 +558,6 @@
|
|||||||
<Connection code="new DefaultListModel<String>()" type="code"/>
|
<Connection code="new DefaultListModel<String>()" type="code"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="enabled" type="boolean" value="false"/>
|
<Property name="enabled" type="boolean" value="false"/>
|
||||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[0, 30]"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[0, 30]"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="visibleRowCount" type="int" value="2"/>
|
<Property name="visibleRowCount" type="int" value="2"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<AuxValues>
|
<AuxValues>
|
||||||
@ -631,12 +610,6 @@
|
|||||||
<Connection code="new DefaultListModel<String>()" type="code"/>
|
<Connection code="new DefaultListModel<String>()" type="code"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="enabled" type="boolean" value="false"/>
|
<Property name="enabled" type="boolean" value="false"/>
|
||||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[0, 30]"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[0, 30]"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="visibleRowCount" type="int" value="2"/>
|
<Property name="visibleRowCount" type="int" value="2"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<AuxValues>
|
<AuxValues>
|
||||||
|
@ -1328,9 +1328,6 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener
|
|||||||
|
|
||||||
parentList.setModel(new DefaultListModel<ParentSearchTerm>());
|
parentList.setModel(new DefaultListModel<ParentSearchTerm>());
|
||||||
parentList.setEnabled(false);
|
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.setVisibleRowCount(4);
|
||||||
parentList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
|
parentList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
|
||||||
public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
|
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<String>());
|
hashSetList.setModel(new DefaultListModel<String>());
|
||||||
hashSetList.setEnabled(false);
|
hashSetList.setEnabled(false);
|
||||||
hashSetList.setMinimumSize(new java.awt.Dimension(0, 30));
|
|
||||||
hashSetList.setPreferredSize(new java.awt.Dimension(0, 30));
|
|
||||||
hashSetList.setVisibleRowCount(3);
|
hashSetList.setVisibleRowCount(3);
|
||||||
hashSetScrollPane.setViewportView(hashSetList);
|
hashSetScrollPane.setViewportView(hashSetList);
|
||||||
|
|
||||||
@ -1454,8 +1449,6 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener
|
|||||||
|
|
||||||
objectsList.setModel(new DefaultListModel<String>());
|
objectsList.setModel(new DefaultListModel<String>());
|
||||||
objectsList.setEnabled(false);
|
objectsList.setEnabled(false);
|
||||||
objectsList.setMinimumSize(new java.awt.Dimension(0, 30));
|
|
||||||
objectsList.setPreferredSize(new java.awt.Dimension(0, 30));
|
|
||||||
objectsList.setVisibleRowCount(2);
|
objectsList.setVisibleRowCount(2);
|
||||||
objectsScrollPane.setViewportView(objectsList);
|
objectsScrollPane.setViewportView(objectsList);
|
||||||
|
|
||||||
@ -1487,8 +1480,6 @@ final class FileSearchPanel extends javax.swing.JPanel implements ActionListener
|
|||||||
|
|
||||||
interestingItemsList.setModel(new DefaultListModel<String>());
|
interestingItemsList.setModel(new DefaultListModel<String>());
|
||||||
interestingItemsList.setEnabled(false);
|
interestingItemsList.setEnabled(false);
|
||||||
interestingItemsList.setMinimumSize(new java.awt.Dimension(0, 30));
|
|
||||||
interestingItemsList.setPreferredSize(new java.awt.Dimension(0, 30));
|
|
||||||
interestingItemsList.setVisibleRowCount(2);
|
interestingItemsList.setVisibleRowCount(2);
|
||||||
interestingItemsScrollPane.setViewportView(interestingItemsList);
|
interestingItemsScrollPane.setViewportView(interestingItemsList);
|
||||||
|
|
||||||
|
@ -290,11 +290,11 @@
|
|||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
<DimensionLayout dim="1">
|
<DimensionLayout dim="1">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<EmptySpace min="0" pref="221" max="32767" attributes="0"/>
|
<EmptySpace min="0" pref="68" max="32767" attributes="0"/>
|
||||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" alignment="1" attributes="0">
|
<Group type="102" alignment="1" attributes="0">
|
||||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||||
<Component id="instancesScrollPane" pref="221" max="32767" attributes="0"/>
|
<Component id="instancesScrollPane" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
@ -331,9 +331,6 @@
|
|||||||
<Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
<Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||||
<Connection code="new InstancesCellRenderer()" type="code"/>
|
<Connection code="new InstancesCellRenderer()" type="code"/>
|
||||||
</Property>
|
</Property>
|
||||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[0, 50]"/>
|
|
||||||
</Property>
|
|
||||||
<Property name="visibleRowCount" type="int" value="2"/>
|
<Property name="visibleRowCount" type="int" value="2"/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<AuxValues>
|
<AuxValues>
|
||||||
|
@ -146,14 +146,22 @@ public class ResultsPanel extends javax.swing.JPanel {
|
|||||||
*/
|
*/
|
||||||
synchronized void populateInstancesList() {
|
synchronized void populateInstancesList() {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
instancesList.removeListSelectionListener(listener);
|
List<AbstractFile> files = getInstancesForSelected();
|
||||||
instancesListModel.removeAllElements();
|
if (files.isEmpty()) {
|
||||||
for (AbstractFile file : getInstancesForSelected()) {
|
//if there are no files currently remove the current items without removing listener to cause content viewer to reset
|
||||||
instancesListModel.addElement(file);
|
instancesListModel.removeAllElements();
|
||||||
}
|
} else {
|
||||||
instancesList.addListSelectionListener(listener);
|
//remove listener so content viewer node is not set multiple times
|
||||||
if (!instancesListModel.isEmpty()) {
|
instancesList.removeListSelectionListener(listener);
|
||||||
instancesList.setSelectedIndex(0);
|
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.setModel(instancesListModel);
|
||||||
instancesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
instancesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||||
instancesList.setCellRenderer(new InstancesCellRenderer());
|
instancesList.setCellRenderer(new InstancesCellRenderer());
|
||||||
instancesList.setPreferredSize(new java.awt.Dimension(0, 50));
|
|
||||||
instancesList.setVisibleRowCount(2);
|
instancesList.setVisibleRowCount(2);
|
||||||
instancesScrollPane.setViewportView(instancesList);
|
instancesScrollPane.setViewportView(instancesList);
|
||||||
|
|
||||||
@ -502,11 +509,11 @@ public class ResultsPanel extends javax.swing.JPanel {
|
|||||||
);
|
);
|
||||||
instancesPanelLayout.setVerticalGroup(
|
instancesPanelLayout.setVerticalGroup(
|
||||||
instancesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
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(instancesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, instancesPanelLayout.createSequentialGroup()
|
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, instancesPanelLayout.createSequentialGroup()
|
||||||
.addGap(0, 0, 0)
|
.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);
|
resultsSplitPane.setRightComponent(instancesPanel);
|
||||||
|
@ -85,6 +85,8 @@ public final class GeolocationTopComponent extends TopComponent {
|
|||||||
|
|
||||||
// This is the hardcoded report name from KMLReport.java
|
// This is the hardcoded report name from KMLReport.java
|
||||||
private static final String REPORT_KML = "ReportKML.kml";
|
private static final String REPORT_KML = "ReportKML.kml";
|
||||||
|
|
||||||
|
private boolean mapInitalized = false;
|
||||||
|
|
||||||
@Messages({
|
@Messages({
|
||||||
"GLTopComponent_name=Geolocation",
|
"GLTopComponent_name=Geolocation",
|
||||||
@ -194,20 +196,26 @@ public final class GeolocationTopComponent extends TopComponent {
|
|||||||
@Override
|
@Override
|
||||||
public void open() {
|
public void open() {
|
||||||
super.open();
|
super.open();
|
||||||
|
mapPanel.clearWaypoints();
|
||||||
geoFilterPanel.clearDataSourceList();
|
geoFilterPanel.clearDataSourceList();
|
||||||
geoFilterPanel.updateDataSourceList();
|
geoFilterPanel.updateDataSourceList();
|
||||||
try {
|
|
||||||
mapPanel.initMap();
|
// Let's make sure we only do this on the first open
|
||||||
} catch (GeoLocationDataException ex) {
|
if (!mapInitalized) {
|
||||||
JOptionPane.showMessageDialog(this,
|
try {
|
||||||
Bundle.GeolocationTC_connection_failure_message(),
|
mapPanel.initMap();
|
||||||
Bundle.GeolocationTC_connection_failure_message_title(),
|
mapInitalized = true;
|
||||||
JOptionPane.ERROR_MESSAGE);
|
} catch (GeoLocationDataException ex) {
|
||||||
MessageNotifyUtil.Notify.error(
|
JOptionPane.showMessageDialog(this,
|
||||||
Bundle.GeolocationTC_connection_failure_message_title(),
|
Bundle.GeolocationTC_connection_failure_message(),
|
||||||
Bundle.GeolocationTC_connection_failure_message());
|
Bundle.GeolocationTC_connection_failure_message_title(),
|
||||||
logger.log(Level.SEVERE, ex.getMessage(), ex);
|
JOptionPane.ERROR_MESSAGE);
|
||||||
return; // Doen't set the waypoints.
|
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<>());
|
mapPanel.setWaypoints(new ArrayList<>());
|
||||||
updateWaypoints();
|
updateWaypoints();
|
||||||
@ -288,7 +296,7 @@ public final class GeolocationTopComponent extends TopComponent {
|
|||||||
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss", Locale.US);
|
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss", Locale.US);
|
||||||
Date date = new Date();
|
Date date = new Date();
|
||||||
String dateNoTime = dateFormat.format(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.
|
// Create the root reports directory.
|
||||||
try {
|
try {
|
||||||
FileUtil.createFolder(new File(reportPath));
|
FileUtil.createFolder(new File(reportPath));
|
||||||
@ -431,6 +439,8 @@ public final class GeolocationTopComponent extends TopComponent {
|
|||||||
Bundle.GeoTopComponent_filter_exception_Title(),
|
Bundle.GeoTopComponent_filter_exception_Title(),
|
||||||
Bundle.GeoTopComponent_filter_exception_msg(),
|
Bundle.GeoTopComponent_filter_exception_msg(),
|
||||||
JOptionPane.ERROR_MESSAGE);
|
JOptionPane.ERROR_MESSAGE);
|
||||||
|
|
||||||
|
setWaypointLoading(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -444,7 +454,7 @@ public final class GeolocationTopComponent extends TopComponent {
|
|||||||
private class WaypointCallBack implements WaypointFilterQueryCallBack {
|
private class WaypointCallBack implements WaypointFilterQueryCallBack {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(List<Waypoint> waypoints) {
|
public void process(final List<Waypoint> waypoints) {
|
||||||
// Make sure that the waypoints are added to the map panel in
|
// Make sure that the waypoints are added to the map panel in
|
||||||
// the correct thread.
|
// the correct thread.
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
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
|
// If the list is empty, tell the user and do not change
|
||||||
// the visible waypoints.
|
// the visible waypoints.
|
||||||
if (waypoints == null || waypoints.isEmpty()) {
|
if (waypoints == null || waypoints.isEmpty()) {
|
||||||
|
mapPanel.clearWaypoints();
|
||||||
JOptionPane.showMessageDialog(GeolocationTopComponent.this,
|
JOptionPane.showMessageDialog(GeolocationTopComponent.this,
|
||||||
Bundle.GeoTopComponent_no_waypoints_returned_Title(),
|
Bundle.GeoTopComponent_no_waypoints_returned_Title(),
|
||||||
Bundle.GeoTopComponent_no_waypoints_returned_mgs(),
|
Bundle.GeoTopComponent_no_waypoints_returned_mgs(),
|
||||||
JOptionPane.INFORMATION_MESSAGE);
|
JOptionPane.INFORMATION_MESSAGE);
|
||||||
|
setWaypointLoading(false);
|
||||||
|
geoFilterPanel.setEnabled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mapPanel.clearWaypoints();
|
||||||
mapPanel.setWaypoints(MapWaypoint.getWaypoints(waypoints));
|
mapPanel.setWaypoints(MapWaypoint.getWaypoints(waypoints));
|
||||||
setWaypointLoading(false);
|
setWaypointLoading(false);
|
||||||
geoFilterPanel.setEnabled(true);
|
geoFilterPanel.setEnabled(true);
|
||||||
|
@ -417,8 +417,6 @@ public class KdTree<T extends KdTree.XYZPoint> implements Iterable<T> {
|
|||||||
}
|
}
|
||||||
Double nodeDistance = node.id.euclideanDistance(value);
|
Double nodeDistance = node.id.euclideanDistance(value);
|
||||||
if (nodeDistance.compareTo(lastDistance) < 0) {
|
if (nodeDistance.compareTo(lastDistance) < 0) {
|
||||||
if (results.size() == K && lastNode != null)
|
|
||||||
results.remove(lastNode);
|
|
||||||
results.add(node);
|
results.add(node);
|
||||||
} else if (nodeDistance.equals(lastDistance)) {
|
} else if (nodeDistance.equals(lastDistance)) {
|
||||||
results.add(node);
|
results.add(node);
|
||||||
|
@ -256,7 +256,7 @@ final class MBTilesTileFactory extends TileFactory {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An inner class which actually loads the tiles. Used by the thread queue.
|
* 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.
|
* necessary.
|
||||||
*/
|
*/
|
||||||
private class TileRunner implements Runnable {
|
private class TileRunner implements Runnable {
|
||||||
|
115
Core/src/org/sleuthkit/autopsy/geolocation/MapPanMouseInputListener.java
Executable file
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2019 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,6 @@ import java.awt.event.ActionEvent;
|
|||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.ComponentAdapter;
|
import java.awt.event.ComponentAdapter;
|
||||||
import java.awt.event.ComponentEvent;
|
import java.awt.event.ComponentEvent;
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
@ -54,7 +53,6 @@ import org.jxmapviewer.JXMapViewer;
|
|||||||
import org.jxmapviewer.OSMTileFactoryInfo;
|
import org.jxmapviewer.OSMTileFactoryInfo;
|
||||||
import org.jxmapviewer.VirtualEarthTileFactoryInfo;
|
import org.jxmapviewer.VirtualEarthTileFactoryInfo;
|
||||||
import org.jxmapviewer.input.CenterMapListener;
|
import org.jxmapviewer.input.CenterMapListener;
|
||||||
import org.jxmapviewer.input.PanMouseInputListener;
|
|
||||||
import org.jxmapviewer.input.ZoomMouseWheelListenerCursor;
|
import org.jxmapviewer.input.ZoomMouseWheelListenerCursor;
|
||||||
import org.jxmapviewer.viewer.DefaultTileFactory;
|
import org.jxmapviewer.viewer.DefaultTileFactory;
|
||||||
import org.jxmapviewer.viewer.GeoPosition;
|
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.autopsy.geolocation.datamodel.GeoLocationDataException;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
import org.jxmapviewer.viewer.DefaultWaypointRenderer;
|
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);
|
mapViewer.setTileFactory(tileFactory);
|
||||||
|
|
||||||
// Add Mouse interactions
|
// Add Mouse interactions
|
||||||
MouseInputListener mia = new PanMouseInputListener(mapViewer);
|
MouseInputListener mia = new MapPanMouseInputListener(mapViewer);
|
||||||
mapViewer.addMouseListener(mia);
|
mapViewer.addMouseListener(mia);
|
||||||
mapViewer.addMouseMotionListener(mia);
|
mapViewer.addMouseMotionListener(mia);
|
||||||
|
|
||||||
@ -204,13 +205,12 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
Iterator<MapWaypoint> iterator = waypointTree.iterator();
|
Iterator<MapWaypoint> iterator = waypointTree.iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
MapWaypoint point = iterator.next();
|
MapWaypoint point = iterator.next();
|
||||||
if (point != currentlySelectedWaypoint) {
|
set.add(point);
|
||||||
set.add(point);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Add the currentlySelectedWaypoint to the end so that
|
// Add the currentlySelectedWaypoint to the end so that
|
||||||
// it will be painted last.
|
// it will be painted last.
|
||||||
if (currentlySelectedWaypoint != null) {
|
if (currentlySelectedWaypoint != null) {
|
||||||
|
set.remove(currentlySelectedWaypoint);
|
||||||
set.add(currentlySelectedWaypoint);
|
set.add(currentlySelectedWaypoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,7 +279,7 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
/**
|
/**
|
||||||
* Create the TileFactoryInfo for OSM zip File
|
* Create the TileFactoryInfo for OSM zip File
|
||||||
*
|
*
|
||||||
* @param zipPath Path to zip file.
|
* @param path Path to zip file.
|
||||||
*
|
*
|
||||||
* @return TileFactoryInfo for zip file.
|
* @return TileFactoryInfo for zip file.
|
||||||
*
|
*
|
||||||
@ -332,6 +332,11 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
*/
|
*/
|
||||||
void clearWaypoints() {
|
void clearWaypoints() {
|
||||||
waypointTree = null;
|
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) {
|
private void showPopupMenu(Point point) {
|
||||||
try {
|
try {
|
||||||
MapWaypoint waypoint = findClosestWaypoint(point);
|
List<MapWaypoint> waypoints = findClosestWaypoint(point);
|
||||||
|
MapWaypoint waypoint = null;
|
||||||
|
if(waypoints.size() > 0) {
|
||||||
|
waypoint = waypoints.get(0);
|
||||||
|
}
|
||||||
showPopupMenu(waypoint, point);
|
showPopupMenu(waypoint, point);
|
||||||
// Change the details popup to the currently selected point only if
|
// Change the details popup to the currently selected point only if
|
||||||
// it the popup is currently visible
|
// 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 = popupFactory.getPopup(this, detailPane, popupLocation.x, popupLocation.y);
|
||||||
currentPopup.show();
|
currentPopup.show();
|
||||||
|
|
||||||
|
mapViewer.revalidate();
|
||||||
mapViewer.repaint();
|
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
|
* @return A waypoint that is within 10 pixels of the given point, or null
|
||||||
* if none was found.
|
* if none was found.
|
||||||
*/
|
*/
|
||||||
private MapWaypoint findClosestWaypoint(Point mouseClickPoint) {
|
private List<MapWaypoint> findClosestWaypoint(Point mouseClickPoint) {
|
||||||
if (waypointTree == null) {
|
if (waypointTree == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -446,7 +456,7 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
GeoPosition geopos = mapViewer.getTileFactory().pixelToGeo(mouseClickPoint, mapViewer.getZoom());
|
GeoPosition geopos = mapViewer.getTileFactory().pixelToGeo(mouseClickPoint, mapViewer.getZoom());
|
||||||
|
|
||||||
// Get the 5 nearest neightbors to the point
|
// Get the 5 nearest neightbors to the point
|
||||||
Collection<MapWaypoint> waypoints = waypointTree.nearestNeighbourSearch(20, MapWaypoint.getDummyWaypoint(geopos));
|
Collection<MapWaypoint> waypoints = waypointTree.nearestNeighbourSearch(10, MapWaypoint.getDummyWaypoint(geopos));
|
||||||
|
|
||||||
if (waypoints == null || waypoints.isEmpty()) {
|
if (waypoints == null || waypoints.isEmpty()) {
|
||||||
return null;
|
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
|
// These maybe the points closest to lat/log was clicked but
|
||||||
// that doesn't mean they are close in terms of pixles.
|
// that doesn't mean they are close in terms of pixles.
|
||||||
|
List<MapWaypoint> closestPoints = new ArrayList<>();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
MapWaypoint nextWaypoint = iterator.next();
|
MapWaypoint nextWaypoint = iterator.next();
|
||||||
|
|
||||||
@ -466,11 +477,11 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
(int) point.getY() - rect.y);
|
(int) point.getY() - rect.y);
|
||||||
|
|
||||||
if (converted_gp_pt.distance(mouseClickPoint) < 10) {
|
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();
|
public abstract void resizeTimedOut();
|
||||||
}
|
}
|
||||||
@ -629,8 +640,14 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
}//GEN-LAST:event_mapViewerMouseMoved
|
}//GEN-LAST:event_mapViewerMouseMoved
|
||||||
|
|
||||||
private void mapViewerMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseClicked
|
private void mapViewerMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseClicked
|
||||||
if(!evt.isPopupTrigger() && (evt.getButton() == MouseEvent.BUTTON1)) {
|
if(!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) {
|
||||||
currentlySelectedWaypoint = findClosestWaypoint(evt.getPoint());
|
List<MapWaypoint> waypoints = findClosestWaypoint(evt.getPoint());
|
||||||
|
if(waypoints.size() > 0) {
|
||||||
|
currentlySelectedWaypoint = waypoints.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// currentlySelectedWaypoint = findClosestWaypoint(evt.getPoint());
|
||||||
showDetailsPopup();
|
showDetailsPopup();
|
||||||
}
|
}
|
||||||
}//GEN-LAST:event_mapViewerMouseClicked
|
}//GEN-LAST:event_mapViewerMouseClicked
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2019 Basis Technology Corp.
|
||||||
|
* contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.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<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> 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<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) {
|
||||||
|
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
|
||||||
|
if (attribute != null) {
|
||||||
|
return attribute.getDisplayString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -71,7 +71,7 @@ final class LastKnownWaypoint extends Waypoint {
|
|||||||
"LastKnownWaypoint_Label=Last Known Location",})
|
"LastKnownWaypoint_Label=Last Known Location",})
|
||||||
private static String getLabelFromArtifact(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
|
private static String getLabelFromArtifact(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
|
||||||
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
|
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()) {
|
if (label == null || label.isEmpty()) {
|
||||||
label = Bundle.LastKnownWaypoint_Label();
|
label = Bundle.LastKnownWaypoint_Label();
|
||||||
|
@ -125,6 +125,7 @@ public final class Route {
|
|||||||
/**
|
/**
|
||||||
* Get the route start point.
|
* Get the route start point.
|
||||||
*
|
*
|
||||||
|
* @param artifact
|
||||||
* @param attributeMap Map of artifact attributes for this waypoint.
|
* @param attributeMap Map of artifact attributes for this waypoint.
|
||||||
*
|
*
|
||||||
* An exception will be thrown if longitude or latitude is null.
|
* An exception will be thrown if longitude or latitude is null.
|
||||||
|
@ -187,7 +187,7 @@ public class Waypoint {
|
|||||||
/**
|
/**
|
||||||
* Gets the label for this 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
|
* @return Returns a label for the waypoint, or empty string if no label was
|
||||||
* found.
|
* found.
|
||||||
@ -232,7 +232,7 @@ public class Waypoint {
|
|||||||
* will not include attributes that the Waypoint interfact has get functions
|
* will not include attributes that the Waypoint interfact has get functions
|
||||||
* for.
|
* for.
|
||||||
*
|
*
|
||||||
* @param artifact Blackboard artifact to get attributes\properties from
|
* @param attributeMap Attributes for the given artifact
|
||||||
*
|
*
|
||||||
* @return A List of Waypoint.Property objects
|
* @return A List of Waypoint.Property objects
|
||||||
*
|
*
|
||||||
|
@ -56,7 +56,8 @@ public final class WaypointBuilder {
|
|||||||
final static String GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY
|
final static String GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY
|
||||||
= "SELECT blackboard_attributes.artifact_id "
|
= "SELECT blackboard_attributes.artifact_id "
|
||||||
+ "FROM blackboard_attributes, blackboard_artifacts "
|
+ "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
|
+ "AND data_source_obj_id IN (%s)"; //NON-NLS
|
||||||
|
|
||||||
// Select will return the "most recent" timestamp from all waypoings
|
// Select will return the "most recent" timestamp from all waypoings
|
||||||
@ -469,6 +470,18 @@ public final class WaypointBuilder {
|
|||||||
* @return SQL SELECT statement
|
* @return SQL SELECT statement
|
||||||
*/
|
*/
|
||||||
static private String buildQueryForWaypointsWOTimeStamps(List<DataSource> dataSources) {
|
static private String buildQueryForWaypointsWOTimeStamps(List<DataSource> 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,
|
return String.format(SELECT_WO_TIMESTAMP,
|
||||||
String.format(GEO_ARTIFACT_QUERY_ID_ONLY,
|
String.format(GEO_ARTIFACT_QUERY_ID_ONLY,
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
|
||||||
@ -502,16 +515,26 @@ public final class WaypointBuilder {
|
|||||||
String mostRecentQuery = "";
|
String mostRecentQuery = "";
|
||||||
|
|
||||||
if (!showAll && cntDaysFromRecent > 0) {
|
if (!showAll && cntDaysFromRecent > 0) {
|
||||||
mostRecentQuery = String.format("AND value_int64 > (%s)", //NON-NLS
|
// MOST_RECENT_TIME
|
||||||
String.format(MOST_RECENT_TIME,
|
// SELECT MAX(value_int64) - (%d * 86400)
|
||||||
cntDaysFromRecent,
|
// FROM blackboard_attributes
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
|
// WHERE attribute_type_id IN(%d, %d)
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID(),
|
// AND artifact_id
|
||||||
getWaypointListQuery(dataSources)
|
// 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,
|
String query = String.format(GEO_ARTIFACT_QUERY,
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID());
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID());
|
||||||
@ -542,6 +565,10 @@ public final class WaypointBuilder {
|
|||||||
static private String getWaypointListQuery(List<DataSource> dataSources) {
|
static private String getWaypointListQuery(List<DataSource> dataSources) {
|
||||||
|
|
||||||
if (dataSources == null || dataSources.isEmpty()) {
|
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,
|
return String.format(GEO_ARTIFACT_QUERY,
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(),
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(),
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID());
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID());
|
||||||
@ -592,8 +619,11 @@ public final class WaypointBuilder {
|
|||||||
Route route = new Route(artifact);
|
Route route = new Route(artifact);
|
||||||
waypoints.addAll(route.getRoute());
|
waypoints.addAll(route.getRoute());
|
||||||
break;
|
break;
|
||||||
|
case TSK_GPS_LAST_KNOWN_LOCATION:
|
||||||
|
waypoints.add(new LastKnownWaypoint(artifact));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new GeoLocationDataException(String.format("Unable to create waypoint for artifact of type %s", type.toString()));
|
waypoints.add(new CustomArtifactWaypoint(artifact));
|
||||||
}
|
}
|
||||||
|
|
||||||
return waypoints;
|
return waypoints;
|
||||||
|
@ -16,7 +16,6 @@ EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=Possib
|
|||||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=Compression ratio is {0}, skipping items in {1}.
|
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.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.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.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.notEnoughDiskSpace.details=The archive item is too large to unpack, skipping unpacking this item.
|
||||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg=Error unpacking {0}
|
EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg=Error unpacking {0}
|
||||||
|
@ -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.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.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.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.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.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
|
EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.msg={0} \u306e\u958b\u5c01\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
|
||||||
|
@ -449,10 +449,6 @@ class SevenZipExtractor {
|
|||||||
} else {
|
} else {
|
||||||
pathInArchive = "/" + useName;
|
pathInArchive = "/" + useName;
|
||||||
}
|
}
|
||||||
String msg = NbBundle.getMessage(SevenZipExtractor.class,
|
|
||||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg",
|
|
||||||
getArchiveFilePath(archiveFile), pathInArchive);
|
|
||||||
logger.log(Level.WARNING, msg);
|
|
||||||
}
|
}
|
||||||
return pathInArchive;
|
return pathInArchive;
|
||||||
}
|
}
|
||||||
@ -1196,6 +1192,7 @@ class SevenZipExtractor {
|
|||||||
*
|
*
|
||||||
* @param parent
|
* @param parent
|
||||||
* @param tokenPath
|
* @param tokenPath
|
||||||
|
* @param tokenPathBytes
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
CannotRunFileTypeDetection=Cannot run file type detection.
|
CannotRunFileTypeDetection=Cannot run file type detection.
|
||||||
ExifParserFileIngestModule.indexError.message=Failed to post EXIF Metadata artifact(s).
|
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-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-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
|
OpenIDE-Module-Name=ExifParser
|
||||||
|
@ -49,6 +49,7 @@ import org.sleuthkit.datamodel.AbstractFile;
|
|||||||
import org.sleuthkit.datamodel.Blackboard;
|
import org.sleuthkit.datamodel.Blackboard;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
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_METADATA_EXIF;
|
||||||
|
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_USER_CONTENT_SUSPECTED;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
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_DATETIME_CREATED;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE;
|
||||||
@ -130,6 +131,7 @@ public final class ExifParserFileIngestModule implements FileIngestModule {
|
|||||||
return processFile(content);
|
return processFile(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Messages({"ExifParserFileIngestModule.userContent.description=EXIF metadata exists for this file."})
|
||||||
private ProcessResult processFile(AbstractFile file) {
|
private ProcessResult processFile(AbstractFile file) {
|
||||||
|
|
||||||
try (BufferedInputStream bin = new BufferedInputStream(new ReadContentInputStream(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.
|
// Create artifact if it doesn't already exist.
|
||||||
if (!blackboard.artifactExists(file, TSK_METADATA_EXIF, attributes)) {
|
if (!blackboard.artifactExists(file, TSK_METADATA_EXIF, attributes)) {
|
||||||
BlackboardArtifact bba = file.newArtifact(TSK_METADATA_EXIF);
|
BlackboardArtifact bba = file.newArtifact(TSK_METADATA_EXIF);
|
||||||
|
BlackboardArtifact bba2 = file.newArtifact(TSK_USER_CONTENT_SUSPECTED);
|
||||||
bba.addAttributes(attributes);
|
bba.addAttributes(attributes);
|
||||||
|
bba2.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION, MODULE_NAME, Bundle.ExifParserFileIngestModule_userContent_description()));
|
||||||
try {
|
try {
|
||||||
// index the artifact for keyword search
|
// index the artifact for keyword search
|
||||||
blackboard.postArtifact(bba, MODULE_NAME);
|
blackboard.postArtifact(bba, MODULE_NAME);
|
||||||
|
blackboard.postArtifact(bba2, MODULE_NAME);
|
||||||
} catch (Blackboard.BlackboardException ex) {
|
} catch (Blackboard.BlackboardException ex) {
|
||||||
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
|
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
|
||||||
MessageNotifyUtil.Notify.error(
|
MessageNotifyUtil.Notify.error(
|
||||||
|
@ -254,10 +254,10 @@ public class FileTypeDetector {
|
|||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* If the file was marked as an octet stream and the extension is .txt, try to detect a text
|
* 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")) {
|
if (file.getNameExtension().equals("txt")) {
|
||||||
Charset detectedCharset = TextFileExtractor.getEncoding(file);
|
Charset detectedCharset = new TextFileExtractor(file).getEncoding();
|
||||||
if (detectedCharset != TextFileExtractor.UNKNOWN_CHARSET) {
|
if (detectedCharset != TextFileExtractor.UNKNOWN_CHARSET) {
|
||||||
mimeType = MimeTypes.PLAIN_TEXT;
|
mimeType = MimeTypes.PLAIN_TEXT;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ PlasoIngestModule.artifact.progress=Adding events to case: {0}
|
|||||||
PlasoIngestModule.bad.imageFile=Cannot find image file name and path
|
PlasoIngestModule.bad.imageFile=Cannot find image file name and path
|
||||||
PlasoIngestModule.completed=Plaso Processing Completed
|
PlasoIngestModule.completed=Plaso Processing Completed
|
||||||
PlasoIngestModule.create.artifacts.cancelled=Cancelled Plaso Artifact Creation
|
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.creating.output.dir=Error creating Plaso module output directory.
|
||||||
PlasoIngestModule.error.running.log2timeline=Error running log2timeline, see log file.
|
PlasoIngestModule.error.running.log2timeline=Error running log2timeline, see log file.
|
||||||
PlasoIngestModule.error.running.psort=Error running Psort, 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.event.description=Event Description
|
||||||
PlasoIngestModule.exception.posting.artifact=Exception Posting artifact.
|
PlasoIngestModule.exception.posting.artifact=Exception Posting artifact.
|
||||||
PlasoIngestModule.executable.not.found=Plaso Executable Not Found.
|
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.info.empty.database=Plaso database was empty.
|
||||||
PlasoIngestModule.log2timeline.cancelled=Log2timeline run was canceled
|
PlasoIngestModule.log2timeline.cancelled=Log2timeline run was canceled
|
||||||
PlasoIngestModule.psort.cancelled=psort run was canceled
|
PlasoIngestModule.psort.cancelled=psort run was canceled
|
||||||
|
@ -117,6 +117,7 @@ public final class KMLReport implements GeneralReportModule {
|
|||||||
*
|
*
|
||||||
* @param baseReportDir path to save the report
|
* @param baseReportDir path to save the report
|
||||||
* @param progressPanel panel to update the report's progress
|
* @param progressPanel panel to update the report's progress
|
||||||
|
* @param waypointList
|
||||||
*/
|
*/
|
||||||
@Messages({
|
@Messages({
|
||||||
"KMLReport.unableToExtractPhotos=Could not extract photo information.",
|
"KMLReport.unableToExtractPhotos=Could not extract photo information.",
|
||||||
@ -524,10 +525,8 @@ public final class KMLReport implements GeneralReportModule {
|
|||||||
*
|
*
|
||||||
* @param startLatitude Starting latitude
|
* @param startLatitude Starting latitude
|
||||||
* @param startLongitude Starting longitude
|
* @param startLongitude Starting longitude
|
||||||
* @param startAltitude Starting altitude. Currently ignored.
|
|
||||||
* @param stopLatitude Ending latitude
|
* @param stopLatitude Ending latitude
|
||||||
* @param stopLongitude Ending longitude
|
* @param stopLongitude Ending longitude
|
||||||
* @param stopAltitude Ending altitude. Currently ignored.
|
|
||||||
*
|
*
|
||||||
* @return the Line as an Element
|
* @return the Line as an Element
|
||||||
*/
|
*/
|
||||||
|
@ -85,14 +85,14 @@ public class TextExtractorFactory {
|
|||||||
* @param content AbstractFile content
|
* @param content AbstractFile content
|
||||||
* @param context Lookup containing extractor configurations
|
* @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<TextExtractor> getFileExtractors(AbstractFile content, Lookup context) {
|
private static List<TextExtractor> getFileExtractors(AbstractFile content, Lookup context) {
|
||||||
List<TextExtractor> fileExtractors = Arrays.asList(
|
List<TextExtractor> fileExtractors = Arrays.asList(
|
||||||
new TextFileExtractor(content),
|
new TextFileExtractor(content),
|
||||||
new HtmlTextExtractor(content),
|
new HtmlTextExtractor(content),
|
||||||
new SqliteTextExtractor(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) -> {
|
fileExtractors.forEach((fileExtractor) -> {
|
||||||
fileExtractor.setExtractionSettings(context);
|
fileExtractor.setExtractionSettings(context);
|
||||||
|
@ -31,17 +31,24 @@ import java.nio.charset.CharsetEncoder;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.charset.UnsupportedCharsetException;
|
import java.nio.charset.UnsupportedCharsetException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
import org.apache.tika.parser.txt.CharsetDetector;
|
import org.apache.tika.parser.txt.CharsetDetector;
|
||||||
import org.apache.tika.parser.txt.CharsetMatch;
|
import org.apache.tika.parser.txt.CharsetMatch;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.Content;
|
|
||||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
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 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
|
@Override
|
||||||
public boolean contains(Charset cs) {
|
public boolean contains(Charset cs) {
|
||||||
return false;
|
return false;
|
||||||
@ -59,33 +66,45 @@ public final class TextFileExtractor implements TextExtractor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// This value will be used as a threshold for determining which encoding
|
// This value will be used as a threshold for determining which encoding
|
||||||
// detection library to use. If Tika's own confidence is at least
|
// detection library to use. If CharsetDetector's own confidence is at least
|
||||||
// MIN_MATCH_CONFIDENCE, Tika's result will be used for decoding.
|
// MIN_MATCH_CONFIDENCE, CharsetDetector's result will be used for decoding.
|
||||||
// Otherwise, Decodetect will be used.
|
// 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
|
// 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;
|
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 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) {
|
public TextFileExtractor(AbstractFile file) {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Reader getReader() {
|
public Reader getReader() {
|
||||||
Charset encoding = getEncoding(file);
|
Charset enc = getEncoding();
|
||||||
if (encoding.equals(UNKNOWN_CHARSET)) {
|
if (enc.equals(UNKNOWN_CHARSET)) {
|
||||||
encoding = StandardCharsets.UTF_8;
|
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);
|
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");
|
return file.getMIMEType().equals("text/plain");
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TextFileExtractorException extends Exception {
|
/**
|
||||||
public TextFileExtractorException(String msg, Throwable ex) {
|
* Returns the encoding of the file.
|
||||||
super(msg, ex);
|
*
|
||||||
|
* @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) {
|
// Encoding detection is hard. We use several libraries since the data passed in is often messy.
|
||||||
try (InputStream stream = new BufferedInputStream(new ReadContentInputStream(content))) {
|
// First try CharsetDetector (from Tika / ICU4J).
|
||||||
// Tika first
|
// It is a rule-based detection approach.
|
||||||
|
try (InputStream stream = new BufferedInputStream(new ReadContentInputStream(file))) {
|
||||||
CharsetDetector detector = new CharsetDetector();
|
CharsetDetector detector = new CharsetDetector();
|
||||||
detector.setText(stream);
|
detector.setText(stream);
|
||||||
CharsetMatch tikaResult = detector.detect();
|
CharsetMatch tikaResult = detector.detect();
|
||||||
if (tikaResult != null && tikaResult.getConfidence() >= MIN_TIKA_MATCH_CONFIDENCE) {
|
if (tikaResult != null && tikaResult.getConfidence() >= MIN_CHARSETDETECT_MATCH_CONFIDENCE) {
|
||||||
try {
|
try {
|
||||||
return Charset.forName(tikaResult.getName());
|
encoding = Charset.forName(tikaResult.getName());
|
||||||
} catch (UnsupportedCharsetException ignored) {
|
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 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];
|
byte[] targetArray = new byte[numBytes];
|
||||||
stream.read(targetArray);
|
file.read(targetArray, 0, numBytes);
|
||||||
List<DecodetectResult> results = Decodetect.DECODETECT.getResults(targetArray);
|
List<DecodetectResult> results = Decodetect.DECODETECT.getResults(targetArray);
|
||||||
if (!results.isEmpty()) {
|
if (!results.isEmpty()) {
|
||||||
DecodetectResult topResult = results.get(0);
|
DecodetectResult topResult = results.get(0);
|
||||||
if (topResult.getConfidence() >= MIN_DECODETECT_MATCH_CONFIDENCE) {
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@
|
|||||||
<compile-dependency/>
|
<compile-dependency/>
|
||||||
<run-dependency>
|
<run-dependency>
|
||||||
<release-version>10</release-version>
|
<release-version>10</release-version>
|
||||||
<specification-version>10.17</specification-version>
|
<specification-version>10.18</specification-version>
|
||||||
</run-dependency>
|
</run-dependency>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -127,7 +127,7 @@
|
|||||||
<compile-dependency/>
|
<compile-dependency/>
|
||||||
<run-dependency>
|
<run-dependency>
|
||||||
<release-version>10</release-version>
|
<release-version>10</release-version>
|
||||||
<specification-version>10.17</specification-version>
|
<specification-version>10.18</specification-version>
|
||||||
</run-dependency>
|
</run-dependency>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -2150,10 +2150,15 @@ public final class DrawableDB {
|
|||||||
public void deleteDataSource(long dataSourceID) throws SQLException, TskCoreException {
|
public void deleteDataSource(long dataSourceID) throws SQLException, TskCoreException {
|
||||||
dbWriteLock();
|
dbWriteLock();
|
||||||
DrawableTransaction trans = null;
|
DrawableTransaction trans = null;
|
||||||
|
String whereClause = "WHERE data_source_obj_id = " + dataSourceID;
|
||||||
|
String tableName = "image_gallery_groups";
|
||||||
try {
|
try {
|
||||||
trans = beginTransaction();
|
trans = beginTransaction();
|
||||||
deleteDataSourceStmt.setLong(1, dataSourceID);
|
deleteDataSourceStmt.setLong(1, dataSourceID);
|
||||||
deleteDataSourceStmt.executeUpdate();
|
deleteDataSourceStmt.executeUpdate();
|
||||||
|
if (caseDb.getCaseDbAccessManager().tableExists(tableName)) {
|
||||||
|
caseDb.getCaseDbAccessManager().delete(tableName, whereClause);
|
||||||
|
}
|
||||||
commitTransaction(trans, true);
|
commitTransaction(trans, true);
|
||||||
} catch (SQLException | TskCoreException ex) {
|
} catch (SQLException | TskCoreException ex) {
|
||||||
if (null != trans) {
|
if (null != trans) {
|
||||||
|
@ -126,11 +126,12 @@ class TextMessageAnalyzer(general.AndroidComponentAnalyzer):
|
|||||||
|
|
||||||
artifact.addAttributes(attributes)
|
artifact.addAttributes(attributes)
|
||||||
|
|
||||||
# Create an account
|
if address is not None:
|
||||||
msgAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, address, general.MODULE_NAME, abstractFile);
|
# Create an account
|
||||||
|
msgAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, address, general.MODULE_NAME, abstractFile);
|
||||||
|
|
||||||
# create relationship between accounts
|
# create relationship between accounts
|
||||||
Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [msgAccountInstance], artifact,Relationship.Type.MESSAGE, date);
|
Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [msgAccountInstance], artifact,Relationship.Type.MESSAGE, date);
|
||||||
|
|
||||||
bbartifacts.append(artifact)
|
bbartifacts.append(artifact)
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@
|
|||||||
<compile-dependency/>
|
<compile-dependency/>
|
||||||
<run-dependency>
|
<run-dependency>
|
||||||
<release-version>10</release-version>
|
<release-version>10</release-version>
|
||||||
<specification-version>10.17</specification-version>
|
<specification-version>10.18</specification-version>
|
||||||
</run-dependency>
|
</run-dependency>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -680,7 +680,6 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
|
|||||||
* Returns true if indexing was successful and false otherwise.
|
* Returns true if indexing was successful and false otherwise.
|
||||||
*
|
*
|
||||||
* @param aFile Text file to analyze
|
* @param aFile Text file to analyze
|
||||||
* @param detectedCharset the encoding of the file
|
|
||||||
*/
|
*/
|
||||||
private boolean indexTextFile(AbstractFile aFile) {
|
private boolean indexTextFile(AbstractFile aFile) {
|
||||||
try {
|
try {
|
||||||
|
33
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 --------------
|
---------------- VERSION 4.13.0 --------------
|
||||||
General:
|
General:
|
||||||
- Switch from Oracle JDK to OpenJDK.
|
- Switch from Oracle JDK to OpenJDK.
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
<compile-dependency/>
|
<compile-dependency/>
|
||||||
<run-dependency>
|
<run-dependency>
|
||||||
<release-version>10</release-version>
|
<release-version>10</release-version>
|
||||||
<specification-version>10.17</specification-version>
|
<specification-version>10.18</specification-version>
|
||||||
</run-dependency>
|
</run-dependency>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
<project name="TSK_VERSION">
|
<project name="TSK_VERSION">
|
||||||
<property name="TSK_VERSION" value="4.7.0"/>
|
<property name="TSK_VERSION" value="4.8.0"/>
|
||||||
</project>
|
</project>
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
<compile-dependency/>
|
<compile-dependency/>
|
||||||
<run-dependency>
|
<run-dependency>
|
||||||
<release-version>10</release-version>
|
<release-version>10</release-version>
|
||||||
<specification-version>10.17</specification-version>
|
<specification-version>10.18</specification-version>
|
||||||
</run-dependency>
|
</run-dependency>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<hr/>
|
<hr/>
|
||||||
<p><i>Copyright © 2012-2019 Basis Technology. Generated on $date<br/>
|
<p><i>Copyright © 2012-2020 Basis Technology. Generated on $date<br/>
|
||||||
This work is licensed under a
|
This work is licensed under a
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>.
|
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>.
|
||||||
</i></p>
|
</i></p>
|
||||||
|
@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy User Documentation"
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# 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
|
# 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
|
# 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.
|
# The default directory is: html.
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
# 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
|
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
|
||||||
# generated HTML page (for example: .htm, .php, .asp).
|
# generated HTML page (for example: .htm, .php, .asp).
|
||||||
|
@ -52,14 +52,10 @@ There are no runtime ingest settings required.
|
|||||||
|
|
||||||
Seeing Results
|
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
|
\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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
@ -54,6 +54,10 @@ It will display most image types, which can be scaled and rotated:
|
|||||||
|
|
||||||
\image html content_viewer_app_image.png
|
\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:
|
It also allows you to browse SQLite tables and export their contents as CSV:
|
||||||
|
|
||||||
\image html content_viewer_app_sqlite.png
|
\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
|
\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
|
\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.
|
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.
|
||||||
|
@ -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.
|
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)
|
- 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)
|
- 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)
|
- 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)
|
- 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)
|
- 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
|
\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.
|
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
|
\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.
|
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
|
||||||
|
|
||||||
*/
|
*/
|
@ -23,12 +23,14 @@ There are no runtime ingest settings required.
|
|||||||
|
|
||||||
Seeing Results
|
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
|
\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.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
@ -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
|
\image html extension-mismatch-detected-configuration.PNG
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
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
|
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.
|
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.
|
||||||
|
189
docs/doxygen-user/file_discovery.dox
Normal file
@ -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:
|
||||||
|
<ul>
|
||||||
|
<li>\ref file_type_identification_page
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
Optional ingest modules:
|
||||||
|
<ul>
|
||||||
|
<li>\ref cr_ingest_module - Needed to use the \ref file_disc_occur_filter
|
||||||
|
<li> \ref EXIF_parser_page - Needed to use the \ref file_disc_user_filter
|
||||||
|
<li>\ref hash_db_page - Needed to use the \ref file_disc_hash_filter and to de-duplicate files
|
||||||
|
<li>\ref interesting_files_identifier_page - Needed to use the \ref file_disc_int_filter
|
||||||
|
<li>\ref object_detection_page - Needed to use the \ref file_disc_obj_filter
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
\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:
|
||||||
|
<ol>
|
||||||
|
<li>\ref file_disc_type "Choose the file type"
|
||||||
|
<li>\ref file_disc_filtering "Set up filters"
|
||||||
|
<li>\ref file_disc_grouping "Choose how to group and sort the results
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
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:
|
||||||
|
<ul>
|
||||||
|
<li>The file path must match every "exclude" rule to pass
|
||||||
|
<li>If any "include" rules exist, the file path must match at least one "include" rule to pass
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
*/
|
@ -1,5 +1,5 @@
|
|||||||
<hr/>
|
<hr/>
|
||||||
<p><i>Copyright © 2012-2019 Basis Technology. Generated on $date<br/>
|
<p><i>Copyright © 2012-2020 Basis Technology. Generated on $date<br/>
|
||||||
This work is licensed under a
|
This work is licensed under a
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>.
|
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>.
|
||||||
</i></p>
|
</i></p>
|
||||||
|
137
docs/doxygen-user/geolocation.dox
Normal file
@ -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
|
||||||
|
|
||||||
|
<p>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.
|
||||||
|
<ul>
|
||||||
|
<li><b>Default online tile server</b>
|
||||||
|
<ul>
|
||||||
|
<li>The default Geolocation window tile data source is the Microsoft Virtual Earth server https://www.bing.com/maps.
|
||||||
|
</ul>
|
||||||
|
<li><b>OpenStreetMap server</b>
|
||||||
|
<ul>
|
||||||
|
<li>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
|
||||||
|
</ul>
|
||||||
|
<li><b>OpenStreetMap zip file</b>
|
||||||
|
<ul>
|
||||||
|
<li>Allows offline use of zip file of OSM tile images
|
||||||
|
<li>Details on how to generate tile zip files are \ref geo_generate_zip "below".
|
||||||
|
</ul>
|
||||||
|
<li><b>MBTiles file</b>
|
||||||
|
<ul>
|
||||||
|
<li>Allows offline use of MBTiles file containing raster tiles.
|
||||||
|
<li>MBTiles raster tiles files can be downloaded from <a href="https://openmaptiles.com/downloads/planet/">OpenMapTiles</a>.
|
||||||
|
<li>OpenMapTiles provides downloads of MBTile files for areas as large as the whole planet to as small as regions of countries.
|
||||||
|
<li>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".
|
||||||
|
</ul>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
\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:
|
||||||
|
<ol>
|
||||||
|
<li>Download and run Maperative.
|
||||||
|
<li>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.
|
||||||
|
<li>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.
|
||||||
|
<ul>
|
||||||
|
<li>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.
|
||||||
|
|
||||||
|
<li>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 <a href="http://maperitive.net/docs/">http://maperitive.net/docs/</a>. The <tt>generate-tiles</tt> 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 <a href="http://maperitive.net/docs/Commands/GenerateTiles.html">http://maperitive.net/docs/Commands/GenerateTiles.html</a>. 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
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<li>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.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
|
||||||
|
\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:
|
||||||
|
<ol>
|
||||||
|
<li>Select from the menu bar File->Open Map Source...
|
||||||
|
|
||||||
|
\image html geo_add_ds.png
|
||||||
|
|
||||||
|
<li>The new data source will appear in the bottom right corner of the window in the "Map Sources" list.
|
||||||
|
<li>To disable a Map Source, select the Map Source from the list and click the X button.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
\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
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
BIN
docs/doxygen-user/images/FileDiscovery/fd_dataSourceFilter.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_dupeEx.png
Normal file
After Width: | Height: | Size: 155 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_fileSizeFilter.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_fileType.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_grouping.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_groupingInt.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_groupingSize.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_hashSetFilter.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_icon.png
Normal file
After Width: | Height: | Size: 142 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_instanceContext.png
Normal file
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 2.0 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_loadSettings.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_main.png
Normal file
After Width: | Height: | Size: 448 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_objectFilter.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_paging.png
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_parentEx2.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_parentFilter.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_pastOccur.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
docs/doxygen-user/images/FileDiscovery/fd_resultGroups.png
Normal file
After Width: | Height: | Size: 265 KiB |
After Width: | Height: | Size: 22 KiB |