mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-15 09:17:42 +00:00
Case class merge conflict - report delete events and tag events - resolved
This commit is contained in:
commit
b374e9bb0b
@ -11,7 +11,7 @@ correct C libraries.
|
|||||||
STEPS:
|
STEPS:
|
||||||
1) Get Java Setup
|
1) Get Java Setup
|
||||||
|
|
||||||
1a) Download and install JDK version 1.8. You can now use 32-bit or 64-bit, but special work is needed to get The Sleuth Kit to compile as 64-bit. So, 32-bit is easier.
|
1a) Download and install JDK version 1.8. For the current version of JavaFX that we use, you'll need 1.8.0_40 or greater. You can now use 32-bit or 64-bit, but special work is needed to get The Sleuth Kit to compile as 64-bit. So, 32-bit is easier.
|
||||||
|
|
||||||
Autopsy has been used and tested with Oracle JavaSE and the included JavaFX support
|
Autopsy has been used and tested with Oracle JavaSE and the included JavaFX support
|
||||||
(http://www.oracle.com/technetwork/java/javase/downloads/index.html).
|
(http://www.oracle.com/technetwork/java/javase/downloads/index.html).
|
||||||
|
@ -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: 13
|
OpenIDE-Module-Implementation-Version: 14
|
||||||
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
|
||||||
|
@ -17,5 +17,5 @@ license.file=../LICENSE-2.0.txt
|
|||||||
nbm.homepage=http://www.sleuthkit.org/
|
nbm.homepage=http://www.sleuthkit.org/
|
||||||
nbm.module.author=Brian Carrier
|
nbm.module.author=Brian Carrier
|
||||||
nbm.needs.restart=true
|
nbm.needs.restart=true
|
||||||
spec.version.base=10.2
|
spec.version.base=10.3
|
||||||
|
|
||||||
|
@ -192,6 +192,7 @@
|
|||||||
<package>org.sleuthkit.autopsy.coreutils</package>
|
<package>org.sleuthkit.autopsy.coreutils</package>
|
||||||
<package>org.sleuthkit.autopsy.datamodel</package>
|
<package>org.sleuthkit.autopsy.datamodel</package>
|
||||||
<package>org.sleuthkit.autopsy.directorytree</package>
|
<package>org.sleuthkit.autopsy.directorytree</package>
|
||||||
|
<package>org.sleuthkit.autopsy.events</package>
|
||||||
<package>org.sleuthkit.autopsy.externalresults</package>
|
<package>org.sleuthkit.autopsy.externalresults</package>
|
||||||
<package>org.sleuthkit.autopsy.filesearch</package>
|
<package>org.sleuthkit.autopsy.filesearch</package>
|
||||||
<package>org.sleuthkit.autopsy.ingest</package>
|
<package>org.sleuthkit.autopsy.ingest</package>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013 Basis Technology Corp.
|
* Copyright 2013-15 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");
|
||||||
@ -19,26 +19,26 @@
|
|||||||
package org.sleuthkit.autopsy.actions;
|
package org.sleuthkit.autopsy.actions;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.util.Collections;
|
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.AbstractAction;
|
||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.actions.Presenter;
|
import org.openide.util.actions.Presenter;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract base class for Actions that allow users to tag SleuthKit data
|
* An abstract base class for Actions that allow users to tag SleuthKit data
|
||||||
* model objects.
|
* model objects.
|
||||||
*/
|
*/
|
||||||
abstract class AddTagAction extends TagAction implements Presenter.Popup {
|
abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
|
||||||
|
|
||||||
private static final String NO_COMMENT = "";
|
private static final String NO_COMMENT = "";
|
||||||
|
|
||||||
AddTagAction(String menuText) {
|
AddTagAction(String menuText) {
|
||||||
@ -50,19 +50,26 @@ abstract class AddTagAction extends TagAction implements Presenter.Popup {
|
|||||||
return new TagMenu();
|
return new TagMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses of AddTagAction, should not override actionPerformed, but
|
||||||
|
* instead override addTag.
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void doAction(ActionEvent event) {
|
@SuppressWarnings("NoopMethodInAbstractClass")
|
||||||
|
public void actionPerformed(ActionEvent event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template method to allow derived classes to provide a string for for a
|
* Template method to allow derived classes to provide a string for a menu
|
||||||
* menu item label.
|
* item label.
|
||||||
*/
|
*/
|
||||||
abstract protected String getActionDisplayName();
|
abstract protected String getActionDisplayName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template method to allow derived classes to add the indicated tag and
|
* Template method to allow derived classes to add the indicated tag and
|
||||||
* comment to one or more a SleuthKit data model objects.
|
* comment to one or more SleuthKit data model objects.
|
||||||
*/
|
*/
|
||||||
abstract protected void addTag(TagName tagName, String comment);
|
abstract protected void addTag(TagName tagName, String comment);
|
||||||
|
|
||||||
@ -74,6 +81,7 @@ abstract class AddTagAction extends TagAction implements Presenter.Popup {
|
|||||||
// @@@ This user interface has some significant usability issues and needs
|
// @@@ This user interface has some significant usability issues and needs
|
||||||
// to be reworked.
|
// to be reworked.
|
||||||
private class TagMenu extends JMenu {
|
private class TagMenu extends JMenu {
|
||||||
|
|
||||||
TagMenu() {
|
TagMenu() {
|
||||||
super(getActionDisplayName());
|
super(getActionDisplayName());
|
||||||
|
|
||||||
@ -83,8 +91,7 @@ abstract class AddTagAction extends TagAction implements Presenter.Popup {
|
|||||||
try {
|
try {
|
||||||
tagNames = tagsManager.getAllTagNames();
|
tagNames = tagsManager.getAllTagNames();
|
||||||
Collections.sort(tagNames);
|
Collections.sort(tagNames);
|
||||||
}
|
} catch (TskCoreException ex) {
|
||||||
catch (TskCoreException ex) {
|
|
||||||
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
|
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,17 +105,12 @@ abstract class AddTagAction extends TagAction implements Presenter.Popup {
|
|||||||
if (null != tagNames && !tagNames.isEmpty()) {
|
if (null != tagNames && !tagNames.isEmpty()) {
|
||||||
for (final TagName tagName : tagNames) {
|
for (final TagName tagName : tagNames) {
|
||||||
JMenuItem tagNameItem = new JMenuItem(tagName.getDisplayName());
|
JMenuItem tagNameItem = new JMenuItem(tagName.getDisplayName());
|
||||||
tagNameItem.addActionListener(new ActionListener() {
|
tagNameItem.addActionListener((ActionEvent e) -> {
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
addTag(tagName, NO_COMMENT);
|
addTag(tagName, NO_COMMENT);
|
||||||
refreshDirectoryTree();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
quickTagMenu.add(tagNameItem);
|
quickTagMenu.add(tagNameItem);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
JMenuItem empty = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.noTags"));
|
JMenuItem empty = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.noTags"));
|
||||||
empty.setEnabled(false);
|
empty.setEnabled(false);
|
||||||
quickTagMenu.add(empty);
|
quickTagMenu.add(empty);
|
||||||
@ -120,14 +122,10 @@ abstract class AddTagAction extends TagAction implements Presenter.Popup {
|
|||||||
// Selecting this item initiates a dialog that can be used to create
|
// Selecting this item initiates a dialog that can be used to create
|
||||||
// or select a tag name and adds a tag with the resulting name.
|
// or select a tag name and adds a tag with the resulting name.
|
||||||
JMenuItem newTagMenuItem = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.newTag"));
|
JMenuItem newTagMenuItem = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.newTag"));
|
||||||
newTagMenuItem.addActionListener(new ActionListener() {
|
newTagMenuItem.addActionListener((ActionEvent e) -> {
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
TagName tagName = GetTagNameDialog.doDialog();
|
TagName tagName = GetTagNameDialog.doDialog();
|
||||||
if (tagName != null) {
|
if (null != tagName) {
|
||||||
addTag(tagName, NO_COMMENT);
|
addTag(tagName, NO_COMMENT);
|
||||||
refreshDirectoryTree();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
quickTagMenu.add(newTagMenuItem);
|
quickTagMenu.add(newTagMenuItem);
|
||||||
@ -137,14 +135,10 @@ abstract class AddTagAction extends TagAction implements Presenter.Popup {
|
|||||||
// optional comment and adds a tag with the resulting name.
|
// optional comment and adds a tag with the resulting name.
|
||||||
JMenuItem tagAndCommentItem = new JMenuItem(
|
JMenuItem tagAndCommentItem = new JMenuItem(
|
||||||
NbBundle.getMessage(this.getClass(), "AddTagAction.tagAndComment"));
|
NbBundle.getMessage(this.getClass(), "AddTagAction.tagAndComment"));
|
||||||
tagAndCommentItem.addActionListener(new ActionListener() {
|
tagAndCommentItem.addActionListener((ActionEvent e) -> {
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
GetTagNameAndCommentDialog.TagNameAndComment tagNameAndComment = GetTagNameAndCommentDialog.doDialog();
|
GetTagNameAndCommentDialog.TagNameAndComment tagNameAndComment = GetTagNameAndCommentDialog.doDialog();
|
||||||
if (null != tagNameAndComment) {
|
if (null != tagNameAndComment) {
|
||||||
addTag(tagNameAndComment.getTagName(), tagNameAndComment.getComment());
|
addTag(tagNameAndComment.getTagName(), tagNameAndComment.getComment());
|
||||||
refreshDirectoryTree();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
add(tagAndCommentItem);
|
add(tagAndCommentItem);
|
||||||
|
@ -21,19 +21,19 @@ package org.sleuthkit.autopsy.actions;
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
|
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.Utilities;
|
import org.openide.util.Utilities;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instances of this Action allow users to delete tags applied to blackboard artifacts.
|
* Instances of this Action allow users to delete tags applied to blackboard artifacts.
|
||||||
*/
|
*/
|
||||||
public class DeleteBlackboardArtifactTagAction extends TagAction {
|
public class DeleteBlackboardArtifactTagAction extends AbstractAction {
|
||||||
private static final String MENU_TEXT = NbBundle.getMessage(DeleteBlackboardArtifactTagAction.class,
|
private static final String MENU_TEXT = NbBundle.getMessage(DeleteBlackboardArtifactTagAction.class,
|
||||||
"DeleteBlackboardArtifactTagAction.deleteTags");
|
"DeleteBlackboardArtifactTagAction.deleteTags");
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ public class DeleteBlackboardArtifactTagAction extends TagAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doAction(ActionEvent event) {
|
public void actionPerformed(ActionEvent event) {
|
||||||
Collection<? extends BlackboardArtifactTag> selectedTags = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactTag.class);
|
Collection<? extends BlackboardArtifactTag> selectedTags = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactTag.class);
|
||||||
for (BlackboardArtifactTag tag : selectedTags) {
|
for (BlackboardArtifactTag tag : selectedTags) {
|
||||||
try {
|
try {
|
||||||
|
@ -21,19 +21,19 @@ package org.sleuthkit.autopsy.actions;
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
|
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.Utilities;
|
import org.openide.util.Utilities;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.ContentTag;
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instances of this Action allow users to delete tags applied to content.
|
* Instances of this Action allow users to delete tags applied to content.
|
||||||
*/
|
*/
|
||||||
public class DeleteContentTagAction extends TagAction {
|
public class DeleteContentTagAction extends AbstractAction {
|
||||||
private static final String MENU_TEXT = NbBundle.getMessage(DeleteContentTagAction.class,
|
private static final String MENU_TEXT = NbBundle.getMessage(DeleteContentTagAction.class,
|
||||||
"DeleteContentTagAction.deleteTags");
|
"DeleteContentTagAction.deleteTags");
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ public class DeleteContentTagAction extends TagAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doAction(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
Collection<? extends ContentTag> selectedTags = Utilities.actionsGlobalContext().lookupAll(ContentTag.class);
|
Collection<? extends ContentTag> selectedTags = Utilities.actionsGlobalContext().lookupAll(ContentTag.class);
|
||||||
for (ContentTag tag : selectedTags) {
|
for (ContentTag tag : selectedTags) {
|
||||||
try {
|
try {
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2013 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.actions;
|
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract base class for Actions involving tags.
|
|
||||||
*/
|
|
||||||
abstract class TagAction extends AbstractAction {
|
|
||||||
public TagAction(String menuText) {
|
|
||||||
super(menuText);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent event) {
|
|
||||||
doAction(event);
|
|
||||||
refreshDirectoryTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derived classes must implement this Template Method for actionPerformed().
|
|
||||||
* @param event ActionEvent object passed to actionPerformed()
|
|
||||||
*/
|
|
||||||
abstract protected void doAction(ActionEvent event);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derived classes should call this method any time a tag is created, updated
|
|
||||||
* or deleted outside of an actionPerformed() call.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
protected void refreshDirectoryTree() {
|
|
||||||
|
|
||||||
/* Note: this is a hack. In an ideal world, TagsManager would fire events so
|
|
||||||
* that the directory tree would refresh. But, we haven't had a chance to add
|
|
||||||
* that so, we fire these events and the tree refreshes based on them.
|
|
||||||
*/
|
|
||||||
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent("TagAction", BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE)); //NON-NLS
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,6 +19,7 @@
|
|||||||
package org.sleuthkit.autopsy.casemodule;
|
package org.sleuthkit.autopsy.casemodule;
|
||||||
|
|
||||||
import java.awt.Frame;
|
import java.awt.Frame;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.beans.PropertyChangeSupport;
|
import java.beans.PropertyChangeSupport;
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
@ -53,8 +54,19 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
|||||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||||
import org.sleuthkit.autopsy.coreutils.Version;
|
import org.sleuthkit.autopsy.coreutils.Version;
|
||||||
import org.sleuthkit.datamodel.*;
|
import org.sleuthkit.autopsy.events.BlackBoardArtifactTagAddedEvent;
|
||||||
|
import org.sleuthkit.autopsy.events.BlackBoardArtifactTagDeletedEvent;
|
||||||
|
import org.sleuthkit.autopsy.events.ContentTagAddedEvent;
|
||||||
|
import org.sleuthkit.autopsy.events.ContentTagDeletedEvent;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
|
import org.sleuthkit.datamodel.Image;
|
||||||
|
import org.sleuthkit.datamodel.Report;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.SleuthkitJNI.CaseDbHandle.AddImageProcess;
|
import org.sleuthkit.datamodel.SleuthkitJNI.CaseDbHandle.AddImageProcess;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
import org.sleuthkit.datamodel.TskException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores all information for a given case. Only a single case can currently be
|
* Stores all information for a given case. Only a single case can currently be
|
||||||
@ -132,11 +144,31 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
*/
|
*/
|
||||||
REPORT_ADDED,
|
REPORT_ADDED,
|
||||||
/**
|
/**
|
||||||
* Name for the property change event when a report/s is/are deleted
|
* Name for the property change event when a report is deleted
|
||||||
* from the case. Both the old value and the new value supplied by the
|
* from the case. Both the old value and the new value supplied by the
|
||||||
* event object are null.
|
* event object are null.
|
||||||
*/
|
*/
|
||||||
REPORT_DELETED;
|
REPORT_DELETED,
|
||||||
|
/**
|
||||||
|
* Property name for the event when a new BlackBoardArtifactTag is
|
||||||
|
* added. The new value is tag added, the old value is empty
|
||||||
|
*/
|
||||||
|
BLACKBOARD_ARTIFACT_TAG_ADDED,
|
||||||
|
/**
|
||||||
|
* Property name for the event when a new BlackBoardArtifactTag is
|
||||||
|
* deleted. The new value is empty, the old value is the deleted tag
|
||||||
|
*/
|
||||||
|
BLACKBOARD_ARTIFACT_TAG_DELETED,
|
||||||
|
/**
|
||||||
|
* Property name for the event when a new ContentTag is added. The new
|
||||||
|
* value is tag added, the old value is empty
|
||||||
|
*/
|
||||||
|
CONTENT_TAG_ADDED,
|
||||||
|
/**
|
||||||
|
* Property name for the event when a new ContentTag is deleted. The new
|
||||||
|
* value is empty, the old value is the deleted tag
|
||||||
|
*/
|
||||||
|
CONTENT_TAG_DELETED;
|
||||||
};
|
};
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
@ -277,7 +309,8 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
/**
|
/**
|
||||||
* Creates a new case (create the XML config file and database)
|
* Creates a new case (create the XML config file and database)
|
||||||
*
|
*
|
||||||
* @param caseDir The directory to store case data in. Will be created if it
|
* @param caseDir The directory to store case data in. Will be created if
|
||||||
|
* it
|
||||||
* doesn't already exist. If it exists, it should have all of the needed sub
|
* doesn't already exist. If it exists, it should have all of the needed sub
|
||||||
* dirs that createCaseDirectory() will create.
|
* dirs that createCaseDirectory() will create.
|
||||||
* @param caseName the name of case
|
* @param caseName the name of case
|
||||||
@ -473,9 +506,54 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
* @param newDataSource new data source added
|
* @param newDataSource new data source added
|
||||||
*/
|
*/
|
||||||
void notifyNewDataSource(Content newDataSource) {
|
void notifyNewDataSource(Content newDataSource) {
|
||||||
|
notifyPropertyChangeEvent(new PropertyChangeEvent(Case.class, Events.DATA_SOURCE_ADDED.toString(), null, newDataSource));
|
||||||
|
CoreComponentControl.openCoreWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the UI that a new ContentTag has been added.
|
||||||
|
*
|
||||||
|
* @param newTag new ContentTag added
|
||||||
|
*/
|
||||||
|
public void notifyContentTagAdded(ContentTag newTag) {
|
||||||
|
notifyPropertyChangeEvent(new ContentTagAddedEvent(newTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the UI that a ContentTag has been deleted.
|
||||||
|
*
|
||||||
|
* @param deletedTag ContentTag deleted
|
||||||
|
*/
|
||||||
|
public void notifyContentTagDeleted(ContentTag deletedTag) {
|
||||||
|
notifyPropertyChangeEvent(new ContentTagDeletedEvent(deletedTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the UI that a new BlackboardArtifactTag has been added.
|
||||||
|
*
|
||||||
|
* @param newTag new BlackboardArtifactTag added
|
||||||
|
*/
|
||||||
|
public void notifyBlackBoardArtifactTagAdded(BlackboardArtifactTag newTag) {
|
||||||
|
notifyPropertyChangeEvent(new BlackBoardArtifactTagAddedEvent(newTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the UI that a BlackboardArtifactTag has been.
|
||||||
|
*
|
||||||
|
* @param deletedTag BlackboardArtifactTag deleted
|
||||||
|
*/
|
||||||
|
public void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag) {
|
||||||
|
notifyPropertyChangeEvent(new BlackBoardArtifactTagDeletedEvent(deletedTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the UI about a Case level event.
|
||||||
|
*
|
||||||
|
* @param propertyChangeEvent the event to distribute
|
||||||
|
*/
|
||||||
|
private void notifyPropertyChangeEvent(final PropertyChangeEvent propertyChangeEvent) {
|
||||||
try {
|
try {
|
||||||
pcs.firePropertyChange(Events.DATA_SOURCE_ADDED.toString(), null, newDataSource);
|
pcs.firePropertyChange(propertyChangeEvent);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.SEVERE, "Case threw exception", e); //NON-NLS
|
logger.log(Level.SEVERE, "Case threw exception", e); //NON-NLS
|
||||||
MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "Case.moduleErr"),
|
MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "Case.moduleErr"),
|
||||||
@ -483,7 +561,6 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
"Case.changeCase.errListenToCaseUpdates.msg"),
|
"Case.changeCase.errListenToCaseUpdates.msg"),
|
||||||
MessageNotifyUtil.MessageType.ERROR);
|
MessageNotifyUtil.MessageType.ERROR);
|
||||||
}
|
}
|
||||||
CoreComponentControl.openCoreWindows();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -811,6 +888,7 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
* Get the data model Content objects in the root of this case's hierarchy.
|
* Get the data model Content objects in the root of this case's hierarchy.
|
||||||
*
|
*
|
||||||
* @return a list of the root objects
|
* @return a list of the root objects
|
||||||
|
*
|
||||||
* @throws org.sleuthkit.datamodel.TskCoreException
|
* @throws org.sleuthkit.datamodel.TskCoreException
|
||||||
*/
|
*/
|
||||||
public List<Content> getDataSources() throws TskCoreException {
|
public List<Content> getDataSources() throws TskCoreException {
|
||||||
@ -1164,12 +1242,14 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
|||||||
/**
|
/**
|
||||||
* Adds a report to the case.
|
* Adds a report to the case.
|
||||||
*
|
*
|
||||||
* @param [in] localPath The path of the report file, must be in the case
|
* @param localPath The path of the report file, must be in the case
|
||||||
* directory or one of its subdirectories.
|
* directory or one of its subdirectories.
|
||||||
* @param [in] sourceModuleName The name of the module that created the
|
* @param sourceModuleName The name of the module that created the
|
||||||
* report.
|
* report.
|
||||||
* @param [in] reportName The report name, may be empty.
|
* @param reportName The report name, may be empty.
|
||||||
|
*
|
||||||
* @return A Report data transfer object (DTO) for the new row.
|
* @return A Report data transfer object (DTO) for the new row.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException {
|
public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException {
|
||||||
|
@ -15,3 +15,7 @@ TagsManager.addContentTag.exception.beginByteOffsetOOR.msg=beginByteOffset \= {0
|
|||||||
TagsManager.addContentTag.exception.endByteOffsetOOR.msg=endByteOffset \= {0} out of content size range (0 - {1})
|
TagsManager.addContentTag.exception.endByteOffsetOOR.msg=endByteOffset \= {0} out of content size range (0 - {1})
|
||||||
TagsManager.addContentTag.exception.endLTbegin.msg=endByteOffset < beginByteOffset
|
TagsManager.addContentTag.exception.endLTbegin.msg=endByteOffset < beginByteOffset
|
||||||
TagsManager.predefTagNames.bookmark.text=Bookmark
|
TagsManager.predefTagNames.bookmark.text=Bookmark
|
||||||
|
TagsManager.addContentTag.noCaseWarning=Failed to add publish new content tag event. There is no case open.
|
||||||
|
TagsManager.deleteContentTag.noCaseWarning=Failed to add publish content tag deleted event. There is no case open.
|
||||||
|
TagsManager.addBlackboardArtifactTag.noCaseWarning=Failed to add publish new blackboard artifact tag event. There is no case open.
|
||||||
|
TagsManager.deleteBlackboardArtifactTag.noCaseWarning=Failed to add publish blackboard artifact tag deleted event. There is no case open.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013 Basis Technology Corp.
|
* Copyright 2013-15 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");
|
||||||
@ -24,9 +24,9 @@ import java.util.Arrays;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
|
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||||
@ -42,23 +42,27 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
* blackboard artifacts by users.
|
* blackboard artifacts by users.
|
||||||
*/
|
*/
|
||||||
public class TagsManager implements Closeable {
|
public class TagsManager implements Closeable {
|
||||||
|
|
||||||
private static final String TAGS_SETTINGS_NAME = "Tags"; //NON-NLS
|
private static final String TAGS_SETTINGS_NAME = "Tags"; //NON-NLS
|
||||||
private static final String TAG_NAMES_SETTING_KEY = "TagNames"; //NON-NLS
|
private static final String TAG_NAMES_SETTING_KEY = "TagNames"; //NON-NLS
|
||||||
private final SleuthkitCase tskCase;
|
private final SleuthkitCase tskCase;
|
||||||
private final HashMap<String, TagName> uniqueTagNames = new HashMap<>();
|
private final HashMap<String, TagName> uniqueTagNames = new HashMap<>();
|
||||||
private boolean tagNamesInitialized = false; // @@@ This is part of a work around to be removed when database access on the EDT is correctly synchronized.
|
private boolean tagNamesInitialized = false; // @@@ This is part of a work around to be removed when database access on the EDT is correctly synchronized.
|
||||||
|
|
||||||
// Use this exception and the member hash map to manage uniqueness of hash
|
/**
|
||||||
// names. This is deemed more proactive and informative than leaving this to
|
* Use this exception and the member hash map to manage uniqueness of hash
|
||||||
// the UNIQUE constraint on the display_name field of the tag_names table in
|
* names. This is deemed more proactive and informative than leaving this to
|
||||||
// the case database.
|
* the UNIQUE constraint on the display_name field of the tag_names table in
|
||||||
public class TagNameAlreadyExistsException extends Exception {
|
* the case database.
|
||||||
|
*/
|
||||||
|
public static class TagNameAlreadyExistsException extends Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Package-scope constructor for use of the Services class. An instance of
|
* Package-scope constructor for use of the Services class. An instance of
|
||||||
* TagsManager should be created for each case that is opened.
|
* TagsManager should be created for each case that is opened.
|
||||||
* @param [in] tskCase The SleuthkitCase object for the current case.
|
*
|
||||||
|
* @param tskCase The SleuthkitCase object for the current case.
|
||||||
*/
|
*/
|
||||||
TagsManager(SleuthkitCase tskCase) {
|
TagsManager(SleuthkitCase tskCase) {
|
||||||
this.tskCase = tskCase;
|
this.tskCase = tskCase;
|
||||||
@ -69,7 +73,9 @@ public class TagsManager implements Closeable {
|
|||||||
/**
|
/**
|
||||||
* Gets a list of all tag names currently available for tagging content or
|
* Gets a list of all tag names currently available for tagging content or
|
||||||
* blackboard artifacts.
|
* blackboard artifacts.
|
||||||
|
*
|
||||||
* @return A list, possibly empty, of TagName data transfer objects (DTOs).
|
* @return A list, possibly empty, of TagName data transfer objects (DTOs).
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized List<TagName> getAllTagNames() throws TskCoreException {
|
public synchronized List<TagName> getAllTagNames() throws TskCoreException {
|
||||||
@ -84,7 +90,9 @@ public class TagsManager implements Closeable {
|
|||||||
/**
|
/**
|
||||||
* Gets a list of all tag names currently used for tagging content or
|
* Gets a list of all tag names currently used for tagging content or
|
||||||
* blackboard artifacts.
|
* blackboard artifacts.
|
||||||
|
*
|
||||||
* @return A list, possibly empty, of TagName data transfer objects (DTOs).
|
* @return A list, possibly empty, of TagName data transfer objects (DTOs).
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized List<TagName> getTagNamesInUse() throws TskCoreException {
|
public synchronized List<TagName> getTagNamesInUse() throws TskCoreException {
|
||||||
@ -98,7 +106,9 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a tag name with a given display name exists.
|
* Checks whether a tag name with a given display name exists.
|
||||||
* @param [in] tagDisplayName The display name for which to check.
|
*
|
||||||
|
* @param tagDisplayName The display name for which to check.
|
||||||
|
*
|
||||||
* @return True if the tag name exists, false otherwise.
|
* @return True if the tag name exists, false otherwise.
|
||||||
*/
|
*/
|
||||||
public synchronized boolean tagNameExists(String tagDisplayName) {
|
public synchronized boolean tagNameExists(String tagDisplayName) {
|
||||||
@ -112,8 +122,12 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new tag name to the current case and to the tags settings.
|
* Adds a new tag name to the current case and to the tags settings.
|
||||||
* @param [in] displayName The display name for the new tag name.
|
*
|
||||||
* @return A TagName data transfer object (DTO) representing the new tag name.
|
* @param displayName The display name for the new tag name.
|
||||||
|
*
|
||||||
|
* @return A TagName data transfer object (DTO) representing the new tag
|
||||||
|
* name.
|
||||||
|
*
|
||||||
* @throws TagNameAlreadyExistsException, TskCoreException
|
* @throws TagNameAlreadyExistsException, TskCoreException
|
||||||
*/
|
*/
|
||||||
public TagName addTagName(String displayName) throws TagNameAlreadyExistsException, TskCoreException {
|
public TagName addTagName(String displayName) throws TagNameAlreadyExistsException, TskCoreException {
|
||||||
@ -122,9 +136,13 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new tag name to the current case and to the tags settings.
|
* Adds a new tag name to the current case and to the tags settings.
|
||||||
* @param [in] displayName The display name for the new tag name.
|
*
|
||||||
* @param [in] description The description for the new tag name.
|
* @param displayName The display name for the new tag name.
|
||||||
* @return A TagName data transfer object (DTO) representing the new tag name.
|
* @param description The description for the new tag name.
|
||||||
|
*
|
||||||
|
* @return A TagName data transfer object (DTO) representing the new tag
|
||||||
|
* name.
|
||||||
|
*
|
||||||
* @throws TagNameAlreadyExistsException, TskCoreException
|
* @throws TagNameAlreadyExistsException, TskCoreException
|
||||||
*/
|
*/
|
||||||
public TagName addTagName(String displayName, String description) throws TagNameAlreadyExistsException, TskCoreException {
|
public TagName addTagName(String displayName, String description) throws TagNameAlreadyExistsException, TskCoreException {
|
||||||
@ -133,10 +151,14 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new tag name to the current case and to the tags settings.
|
* Adds a new tag name to the current case and to the tags settings.
|
||||||
* @param [in] displayName The display name for the new tag name.
|
*
|
||||||
* @param [in] description The description for the new tag name.
|
* @param displayName The display name for the new tag name.
|
||||||
* @param [in] color The HTML color to associate with the new tag name.
|
* @param description The description for the new tag name.
|
||||||
* @return A TagName data transfer object (DTO) representing the new tag name.
|
* @param color The HTML color to associate with the new tag name.
|
||||||
|
*
|
||||||
|
* @return A TagName data transfer object (DTO) representing the new tag
|
||||||
|
* name.
|
||||||
|
*
|
||||||
* @throws TagNameAlreadyExistsException, TskCoreException
|
* @throws TagNameAlreadyExistsException, TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized TagName addTagName(String displayName, String description, TagName.HTML_COLOR color) throws TagNameAlreadyExistsException, TskCoreException {
|
public synchronized TagName addTagName(String displayName, String description, TagName.HTML_COLOR color) throws TagNameAlreadyExistsException, TskCoreException {
|
||||||
@ -161,9 +183,12 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tags a content object.
|
* Tags a content object.
|
||||||
* @param [in] content The content to tag.
|
*
|
||||||
* @param [in] tagName The name to use for the tag.
|
* @param content The content to tag.
|
||||||
|
* @param tagName The name to use for the tag.
|
||||||
|
*
|
||||||
* @return A ContentTag data transfer object (DTO) representing the new tag.
|
* @return A ContentTag data transfer object (DTO) representing the new tag.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public ContentTag addContentTag(Content content, TagName tagName) throws TskCoreException {
|
public ContentTag addContentTag(Content content, TagName tagName) throws TskCoreException {
|
||||||
@ -172,10 +197,13 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tags a content object.
|
* Tags a content object.
|
||||||
* @param [in] content The content to tag.
|
*
|
||||||
* @param [in] tagName The name to use for the tag.
|
* @param content The content to tag.
|
||||||
* @param [in] comment A comment to store with the tag.
|
* @param tagName The name to use for the tag.
|
||||||
|
* @param comment A comment to store with the tag.
|
||||||
|
*
|
||||||
* @return A ContentTag data transfer object (DTO) representing the new tag.
|
* @return A ContentTag data transfer object (DTO) representing the new tag.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public ContentTag addContentTag(Content content, TagName tagName, String comment) throws TskCoreException {
|
public ContentTag addContentTag(Content content, TagName tagName, String comment) throws TskCoreException {
|
||||||
@ -184,12 +212,15 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tags a content object or a section of a content object.
|
* Tags a content object or a section of a content object.
|
||||||
* @param [in] content The content to tag.
|
*
|
||||||
* @param [in] tagName The name to use for the tag.
|
* @param content The content to tag.
|
||||||
* @param [in] comment A comment to store with the tag.
|
* @param tagName The name to use for the tag.
|
||||||
* @param [in] beginByteOffset Designates the beginning of a tagged section.
|
* @param comment A comment to store with the tag.
|
||||||
* @param [in] endByteOffset Designates the end of a tagged section.
|
* @param beginByteOffset Designates the beginning of a tagged section.
|
||||||
|
* @param endByteOffset Designates the end of a tagged section.
|
||||||
|
*
|
||||||
* @return A ContentTag data transfer object (DTO) representing the new tag.
|
* @return A ContentTag data transfer object (DTO) representing the new tag.
|
||||||
|
*
|
||||||
* @throws IllegalArgumentException, TskCoreException
|
* @throws IllegalArgumentException, TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized ContentTag addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws IllegalArgumentException, TskCoreException {
|
public synchronized ContentTag addContentTag(Content content, TagName tagName, String comment, long beginByteOffset, long endByteOffset) throws IllegalArgumentException, TskCoreException {
|
||||||
@ -216,13 +247,20 @@ public class TagsManager implements Closeable {
|
|||||||
NbBundle.getMessage(this.getClass(), "TagsManager.addContentTag.exception.endLTbegin.msg"));
|
NbBundle.getMessage(this.getClass(), "TagsManager.addContentTag.exception.endLTbegin.msg"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
final ContentTag newContentTag = tskCase.addContentTag(content, tagName, comment, beginByteOffset, endByteOffset);
|
||||||
return tskCase.addContentTag(content, tagName, comment, beginByteOffset, endByteOffset);
|
try {
|
||||||
|
Case.getCurrentCase().notifyContentTagAdded(newContentTag);
|
||||||
|
} catch (IllegalStateException ex) {
|
||||||
|
Logger.getLogger(TagsManager.class.getName()).log(Level.WARNING, NbBundle.getMessage(TagsManager.class, "TagsManager.addContentTag.noCaseWarning"));
|
||||||
|
}
|
||||||
|
return newContentTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a content tag.
|
* Deletes a content tag.
|
||||||
* @param [in] tag The tag to delete.
|
*
|
||||||
|
* @param tag The tag to delete.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized void deleteContentTag(ContentTag tag) throws TskCoreException {
|
public synchronized void deleteContentTag(ContentTag tag) throws TskCoreException {
|
||||||
@ -232,11 +270,18 @@ public class TagsManager implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tskCase.deleteContentTag(tag);
|
tskCase.deleteContentTag(tag);
|
||||||
|
try {
|
||||||
|
Case.getCurrentCase().notifyContentTagDeleted(tag);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Logger.getLogger(TagsManager.class.getName()).log(Level.WARNING, NbBundle.getMessage(TagsManager.class, "TagsManager.deleteContentTag.noCaseWarning"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all content tags for the current case.
|
* Gets all content tags for the current case.
|
||||||
|
*
|
||||||
* @return A list, possibly empty, of content tags.
|
* @return A list, possibly empty, of content tags.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public List<ContentTag> getAllContentTags() throws TskCoreException {
|
public List<ContentTag> getAllContentTags() throws TskCoreException {
|
||||||
@ -250,8 +295,11 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets content tags count by tag name.
|
* Gets content tags count by tag name.
|
||||||
* @param [in] tagName The tag name of interest.
|
*
|
||||||
|
* @param tagName The tag name of interest.
|
||||||
|
*
|
||||||
* @return A count of the content tags with the specified tag name.
|
* @return A count of the content tags with the specified tag name.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized long getContentTagsCountByTagName(TagName tagName) throws TskCoreException {
|
public synchronized long getContentTagsCountByTagName(TagName tagName) throws TskCoreException {
|
||||||
@ -265,8 +313,12 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets content tags by tag name.
|
* Gets content tags by tag name.
|
||||||
* @param [in] tagName The tag name of interest.
|
*
|
||||||
* @return A list, possibly empty, of the content tags with the specified tag name.
|
* @param tagName The tag name of interest.
|
||||||
|
*
|
||||||
|
* @return A list, possibly empty, of the content tags with the specified
|
||||||
|
* tag name.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized List<ContentTag> getContentTagsByTagName(TagName tagName) throws TskCoreException {
|
public synchronized List<ContentTag> getContentTagsByTagName(TagName tagName) throws TskCoreException {
|
||||||
@ -280,8 +332,12 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets content tags count by content.
|
* Gets content tags count by content.
|
||||||
* @param [in] content The content of interest.
|
*
|
||||||
* @return A list, possibly empty, of the tags that have been applied to the artifact.
|
* @param content The content of interest.
|
||||||
|
*
|
||||||
|
* @return A list, possibly empty, of the tags that have been applied to the
|
||||||
|
* artifact.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized List<ContentTag> getContentTagsByContent(Content content) throws TskCoreException {
|
public synchronized List<ContentTag> getContentTagsByContent(Content content) throws TskCoreException {
|
||||||
@ -295,9 +351,13 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tags a blackboard artifact object.
|
* Tags a blackboard artifact object.
|
||||||
* @param [in] artifact The blackboard artifact to tag.
|
*
|
||||||
* @param [in] tagName The name to use for the tag.
|
* @param artifact The blackboard artifact to tag.
|
||||||
* @return A BlackboardArtifactTag data transfer object (DTO) representing the new tag.
|
* @param tagName The name to use for the tag.
|
||||||
|
*
|
||||||
|
* @return A BlackboardArtifactTag data transfer object (DTO) representing
|
||||||
|
* the new tag.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName) throws TskCoreException {
|
public BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName) throws TskCoreException {
|
||||||
@ -306,10 +366,14 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tags a blackboard artifact object.
|
* Tags a blackboard artifact object.
|
||||||
* @param [in] artifact The blackboard artifact to tag.
|
*
|
||||||
* @param [in] tagName The name to use for the tag.
|
* @param artifact The blackboard artifact to tag.
|
||||||
* @param [in] comment A comment to store with the tag.
|
* @param tagName The name to use for the tag.
|
||||||
* @return A BlackboardArtifactTag data transfer object (DTO) representing the new tag.
|
* @param comment A comment to store with the tag.
|
||||||
|
*
|
||||||
|
* @return A BlackboardArtifactTag data transfer object (DTO) representing
|
||||||
|
* the new tag.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException {
|
public synchronized BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException {
|
||||||
@ -318,12 +382,20 @@ public class TagsManager implements Closeable {
|
|||||||
getExistingTagNames();
|
getExistingTagNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
return tskCase.addBlackboardArtifactTag(artifact, tagName, comment);
|
BlackboardArtifactTag addBlackboardArtifactTag = tskCase.addBlackboardArtifactTag(artifact, tagName, comment);
|
||||||
|
try {
|
||||||
|
Case.getCurrentCase().notifyBlackBoardArtifactTagAdded(addBlackboardArtifactTag);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Logger.getLogger(TagsManager.class.getName()).log(Level.WARNING, NbBundle.getMessage(TagsManager.class, "TagsManager.addBlackboardArtifactTag.noCaseWarning"));
|
||||||
|
}
|
||||||
|
return addBlackboardArtifactTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a blackboard artifact tag.
|
* Deletes a blackboard artifact tag.
|
||||||
* @param [in] tag The tag to delete.
|
*
|
||||||
|
* @param tag The tag to delete.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCoreException {
|
public synchronized void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCoreException {
|
||||||
@ -333,11 +405,18 @@ public class TagsManager implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tskCase.deleteBlackboardArtifactTag(tag);
|
tskCase.deleteBlackboardArtifactTag(tag);
|
||||||
|
try {
|
||||||
|
Case.getCurrentCase().notifyBlackBoardArtifactTagDeleted(tag);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Logger.getLogger(TagsManager.class.getName()).log(Level.WARNING, NbBundle.getMessage(TagsManager.class, "TagsManager.deleteBlackboardArtifactTag.noCaseWarning"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all blackboard artifact tags for the current case.
|
* Gets all blackboard artifact tags for the current case.
|
||||||
|
*
|
||||||
* @return A list, possibly empty, of blackboard artifact tags.
|
* @return A list, possibly empty, of blackboard artifact tags.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public List<BlackboardArtifactTag> getAllBlackboardArtifactTags() throws TskCoreException {
|
public List<BlackboardArtifactTag> getAllBlackboardArtifactTags() throws TskCoreException {
|
||||||
@ -351,8 +430,12 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets blackboard artifact tags count by tag name.
|
* Gets blackboard artifact tags count by tag name.
|
||||||
* @param [in] tagName The tag name of interest.
|
*
|
||||||
* @return A count of the blackboard artifact tags with the specified tag name.
|
* @param tagName The tag name of interest.
|
||||||
|
*
|
||||||
|
* @return A count of the blackboard artifact tags with the specified tag
|
||||||
|
* name.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized long getBlackboardArtifactTagsCountByTagName(TagName tagName) throws TskCoreException {
|
public synchronized long getBlackboardArtifactTagsCountByTagName(TagName tagName) throws TskCoreException {
|
||||||
@ -366,8 +449,12 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets blackboard artifact tags by tag name.
|
* Gets blackboard artifact tags by tag name.
|
||||||
* @param [in] tagName The tag name of interest.
|
*
|
||||||
* @return A list, possibly empty, of the blackboard artifact tags with the specified tag name.
|
* @param tagName The tag name of interest.
|
||||||
|
*
|
||||||
|
* @return A list, possibly empty, of the blackboard artifact tags with the
|
||||||
|
* specified tag name.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized List<BlackboardArtifactTag> getBlackboardArtifactTagsByTagName(TagName tagName) throws TskCoreException {
|
public synchronized List<BlackboardArtifactTag> getBlackboardArtifactTagsByTagName(TagName tagName) throws TskCoreException {
|
||||||
@ -381,8 +468,12 @@ public class TagsManager implements Closeable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets blackboard artifact tags for a particular blackboard artifact.
|
* Gets blackboard artifact tags for a particular blackboard artifact.
|
||||||
* @param [in] artifact The blackboard artifact of interest.
|
*
|
||||||
* @return A list, possibly empty, of the tags that have been applied to the artifact.
|
* @param artifact The blackboard artifact of interest.
|
||||||
|
*
|
||||||
|
* @return A list, possibly empty, of the tags that have been applied to the
|
||||||
|
* artifact.
|
||||||
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public synchronized List<BlackboardArtifactTag> getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact) throws TskCoreException {
|
public synchronized List<BlackboardArtifactTag> getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact) throws TskCoreException {
|
||||||
@ -413,8 +504,7 @@ public class TagsManager implements Closeable {
|
|||||||
for (TagName tagName : currentTagNames) {
|
for (TagName tagName : currentTagNames) {
|
||||||
uniqueTagNames.put(tagName.getDisplayName(), tagName);
|
uniqueTagNames.put(tagName.getDisplayName(), tagName);
|
||||||
}
|
}
|
||||||
}
|
} catch (TskCoreException ex) {
|
||||||
catch (TskCoreException ex) {
|
|
||||||
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag types from the current case", ex); //NON-NLS
|
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag types from the current case", ex); //NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -433,8 +523,7 @@ public class TagsManager implements Closeable {
|
|||||||
try {
|
try {
|
||||||
TagName tagName = tskCase.addTagName(tagNameAttributes[0], tagNameAttributes[1], TagName.HTML_COLOR.getColorByName(tagNameAttributes[2]));
|
TagName tagName = tskCase.addTagName(tagNameAttributes[0], tagNameAttributes[1], TagName.HTML_COLOR.getColorByName(tagNameAttributes[2]));
|
||||||
uniqueTagNames.put(tagName.getDisplayName(), tagName);
|
uniqueTagNames.put(tagName.getDisplayName(), tagName);
|
||||||
}
|
} catch (TskCoreException ex) {
|
||||||
catch (TskCoreException ex) {
|
|
||||||
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to add saved tag name " + tagNameAttributes[0], ex); //NON-NLS
|
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to add saved tag name " + tagNameAttributes[0], ex); //NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -448,8 +537,7 @@ public class TagsManager implements Closeable {
|
|||||||
TagName tagName = tskCase.addTagName(
|
TagName tagName = tskCase.addTagName(
|
||||||
NbBundle.getMessage(this.getClass(), "TagsManager.predefTagNames.bookmark.text"), "", TagName.HTML_COLOR.NONE);
|
NbBundle.getMessage(this.getClass(), "TagsManager.predefTagNames.bookmark.text"), "", TagName.HTML_COLOR.NONE);
|
||||||
uniqueTagNames.put(tagName.getDisplayName(), tagName);
|
uniqueTagNames.put(tagName.getDisplayName(), tagName);
|
||||||
}
|
} catch (TskCoreException ex) {
|
||||||
catch (TskCoreException ex) {
|
|
||||||
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to add predefined 'Bookmark' tag name", ex); //NON-NLS
|
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to add predefined 'Bookmark' tag name", ex); //NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,3 +147,7 @@ AutopsyOptionsPanel.jLabelNumThreads.text=Number of threads to use for file inge
|
|||||||
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
|
||||||
FXVideoPanel.progress.errorWritingVideoToDisk=Error writing video to disk
|
FXVideoPanel.progress.errorWritingVideoToDisk=Error writing video to disk
|
||||||
|
DataContentViewerHex.goToOffsetLabel.text=Jump to Offset
|
||||||
|
DataContentViewerHex.goToOffsetTextField.text=
|
||||||
|
DataContentViewerHex.goToOffsetTextField.msgDlg=Invalid Offset: {0}
|
||||||
|
DataContentViewerHex.setDataView.invalidOffset.negativeOffsetValue=Cannot jump to the resultant offset
|
0
Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties
Normal file → Executable file
0
Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties
Normal file → Executable file
@ -88,9 +88,13 @@
|
|||||||
<Component id="goToPageLabel" min="-2" max="-2" attributes="0"/>
|
<Component id="goToPageLabel" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="goToPageTextField" min="-2" pref="79" max="-2" attributes="0"/>
|
<Component id="goToPageTextField" min="-2" pref="79" max="-2" attributes="0"/>
|
||||||
<EmptySpace pref="205" max="32767" attributes="0"/>
|
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||||
|
<Component id="goToOffsetLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="goToOffsetTextField" min="-2" pref="79" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<Component id="jScrollPane1" alignment="0" pref="622" max="32767" attributes="0"/>
|
<Component id="jScrollPane1" alignment="0" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
<DimensionLayout dim="1">
|
<DimensionLayout dim="1">
|
||||||
@ -108,9 +112,11 @@
|
|||||||
<Component id="prevPageButton" alignment="0" min="-2" pref="23" max="-2" attributes="0"/>
|
<Component id="prevPageButton" alignment="0" min="-2" pref="23" max="-2" attributes="0"/>
|
||||||
<Component id="goToPageLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
<Component id="goToPageLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="goToPageTextField" alignment="0" min="-2" max="-2" attributes="0"/>
|
<Component id="goToPageTextField" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="goToOffsetLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="goToOffsetTextField" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="jScrollPane1" pref="388" max="32767" attributes="0"/>
|
<Component id="jScrollPane1" pref="382" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
@ -131,6 +137,9 @@
|
|||||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||||
<Font name="Courier New" size="11" style="0"/>
|
<Font name="Courier New" size="11" style="0"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
|
||||||
|
<Color id="Default Cursor"/>
|
||||||
|
</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="[700, 20]"/>
|
<Dimension value="[700, 20]"/>
|
||||||
</Property>
|
</Property>
|
||||||
@ -140,7 +149,7 @@
|
|||||||
</Properties>
|
</Properties>
|
||||||
<AuxValues>
|
<AuxValues>
|
||||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new JTextPane(){
 public boolean getScrollableTracksViewportWidth() {
 return (getSize().width < 400);
 }};"/>
|
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new JTextPane(){
 public boolean getScrollableTracksViewportWidth() {
 return (getSize().width < 400);
 }};"/>
|
||||||
<AuxValue name="JavaCodeGenerator_CreateCodePost" type="java.lang.String" value="this.outputViewPane.setBackground(new java.awt.Color(255, 255, 255)); // to make sure the background color is white"/>
|
<AuxValue name="JavaCodeGenerator_CreateCodePost" type="java.lang.String" value="this.outputViewPane.setBackground(new java.awt.Color(255, 255, 255)); // to make sure the background color is white
this.outputViewPane.requestFocusInWindow();
this.outputViewPane.setCursor(Cursor.getDefaultCursor());
"/>
|
||||||
</AuxValues>
|
</AuxValues>
|
||||||
</Component>
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
@ -278,6 +287,23 @@
|
|||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="goToOffsetLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerHex.goToOffsetLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JTextField" name="goToOffsetTextField">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerHex.goToOffsetTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="goToOffsetTextFieldActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Container>
|
</Container>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
|
@ -28,6 +28,8 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
|||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.JTextPane;
|
import javax.swing.JTextPane;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
import javax.swing.text.Utilities;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||||
@ -94,6 +96,8 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
return (getSize().width < 400);
|
return (getSize().width < 400);
|
||||||
}};
|
}};
|
||||||
this.outputViewPane.setBackground(new java.awt.Color(255, 255, 255)); // to make sure the background color is white
|
this.outputViewPane.setBackground(new java.awt.Color(255, 255, 255)); // to make sure the background color is white
|
||||||
|
this.outputViewPane.requestFocusInWindow();
|
||||||
|
this.outputViewPane.setCursor(Cursor.getDefaultCursor());
|
||||||
totalPageLabel = new javax.swing.JLabel();
|
totalPageLabel = new javax.swing.JLabel();
|
||||||
ofLabel = new javax.swing.JLabel();
|
ofLabel = new javax.swing.JLabel();
|
||||||
currentPageLabel = new javax.swing.JLabel();
|
currentPageLabel = new javax.swing.JLabel();
|
||||||
@ -103,6 +107,8 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
pageLabel2 = new javax.swing.JLabel();
|
pageLabel2 = new javax.swing.JLabel();
|
||||||
goToPageTextField = new javax.swing.JTextField();
|
goToPageTextField = new javax.swing.JTextField();
|
||||||
goToPageLabel = new javax.swing.JLabel();
|
goToPageLabel = new javax.swing.JLabel();
|
||||||
|
goToOffsetLabel = new javax.swing.JLabel();
|
||||||
|
goToOffsetTextField = new javax.swing.JTextField();
|
||||||
|
|
||||||
copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.copyMenuItem.text")); // NOI18N
|
copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.copyMenuItem.text")); // NOI18N
|
||||||
rightClickMenu.add(copyMenuItem);
|
rightClickMenu.add(copyMenuItem);
|
||||||
@ -113,7 +119,8 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
jScrollPane1.setBackground(new java.awt.Color(255, 255, 255));
|
jScrollPane1.setBackground(new java.awt.Color(255, 255, 255));
|
||||||
|
|
||||||
outputViewPane.setEditable(false);
|
outputViewPane.setEditable(false);
|
||||||
outputViewPane.setFont(new Font("Courier New", Font.PLAIN, 11)); // NOI18N NON-NLS
|
outputViewPane.setFont(new java.awt.Font("Courier New", 0, 11)); // NOI18N
|
||||||
|
outputViewPane.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
|
||||||
outputViewPane.setMinimumSize(new java.awt.Dimension(700, 20));
|
outputViewPane.setMinimumSize(new java.awt.Dimension(700, 20));
|
||||||
outputViewPane.setPreferredSize(new java.awt.Dimension(700, 400));
|
outputViewPane.setPreferredSize(new java.awt.Dimension(700, 400));
|
||||||
jScrollPane1.setViewportView(outputViewPane);
|
jScrollPane1.setViewportView(outputViewPane);
|
||||||
@ -132,28 +139,28 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
pageLabel.setMinimumSize(new java.awt.Dimension(33, 14));
|
pageLabel.setMinimumSize(new java.awt.Dimension(33, 14));
|
||||||
pageLabel.setPreferredSize(new java.awt.Dimension(33, 14));
|
pageLabel.setPreferredSize(new java.awt.Dimension(33, 14));
|
||||||
|
|
||||||
prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N NON-NLS
|
prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
|
||||||
prevPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.prevPageButton.text")); // NOI18N
|
prevPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.prevPageButton.text")); // NOI18N
|
||||||
prevPageButton.setBorderPainted(false);
|
prevPageButton.setBorderPainted(false);
|
||||||
prevPageButton.setContentAreaFilled(false);
|
prevPageButton.setContentAreaFilled(false);
|
||||||
prevPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N NON-NLS
|
prevPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
|
||||||
prevPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
prevPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
||||||
prevPageButton.setPreferredSize(new java.awt.Dimension(23, 23));
|
prevPageButton.setPreferredSize(new java.awt.Dimension(23, 23));
|
||||||
prevPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N NON-NLS
|
prevPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N
|
||||||
prevPageButton.addActionListener(new java.awt.event.ActionListener() {
|
prevPageButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
prevPageButtonActionPerformed(evt);
|
prevPageButtonActionPerformed(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N NON-NLS
|
nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
|
||||||
nextPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.nextPageButton.text")); // NOI18N
|
nextPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.nextPageButton.text")); // NOI18N
|
||||||
nextPageButton.setBorderPainted(false);
|
nextPageButton.setBorderPainted(false);
|
||||||
nextPageButton.setContentAreaFilled(false);
|
nextPageButton.setContentAreaFilled(false);
|
||||||
nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N NON-NLS
|
nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
|
||||||
nextPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
nextPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
||||||
nextPageButton.setPreferredSize(new java.awt.Dimension(23, 23));
|
nextPageButton.setPreferredSize(new java.awt.Dimension(23, 23));
|
||||||
nextPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N NON-NLS
|
nextPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N
|
||||||
nextPageButton.addActionListener(new java.awt.event.ActionListener() {
|
nextPageButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
nextPageButtonActionPerformed(evt);
|
nextPageButtonActionPerformed(evt);
|
||||||
@ -174,6 +181,15 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
|
|
||||||
goToPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.goToPageLabel.text")); // NOI18N
|
goToPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.goToPageLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
goToOffsetLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.goToOffsetLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
goToOffsetTextField.setText(org.openide.util.NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.goToOffsetTextField.text")); // NOI18N
|
||||||
|
goToOffsetTextField.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
goToOffsetTextFieldActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
javax.swing.GroupLayout hexViewerPanelLayout = new javax.swing.GroupLayout(hexViewerPanel);
|
javax.swing.GroupLayout hexViewerPanelLayout = new javax.swing.GroupLayout(hexViewerPanel);
|
||||||
hexViewerPanel.setLayout(hexViewerPanelLayout);
|
hexViewerPanel.setLayout(hexViewerPanelLayout);
|
||||||
hexViewerPanelLayout.setHorizontalGroup(
|
hexViewerPanelLayout.setHorizontalGroup(
|
||||||
@ -197,8 +213,12 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
.addComponent(goToPageLabel)
|
.addComponent(goToPageLabel)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(goToPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(goToPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addContainerGap(205, Short.MAX_VALUE))
|
.addGap(18, 18, 18)
|
||||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 622, Short.MAX_VALUE)
|
.addComponent(goToOffsetLabel)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(goToOffsetTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||||
|
.addComponent(jScrollPane1)
|
||||||
);
|
);
|
||||||
hexViewerPanelLayout.setVerticalGroup(
|
hexViewerPanelLayout.setVerticalGroup(
|
||||||
hexViewerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
hexViewerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
@ -213,9 +233,11 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
.addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addComponent(goToPageLabel)
|
.addComponent(goToPageLabel)
|
||||||
.addComponent(goToPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
.addComponent(goToPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addGap(0, 0, 0)
|
.addComponent(goToOffsetLabel)
|
||||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 388, Short.MAX_VALUE))
|
.addComponent(goToOffsetTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 382, Short.MAX_VALUE))
|
||||||
);
|
);
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
@ -241,11 +263,13 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
|
private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
|
||||||
setDataView(currentPage - 1);
|
setDataViewByPageNumber(currentPage - 1);
|
||||||
|
goToPageTextField.setText(Integer.toString(currentPage));
|
||||||
}//GEN-LAST:event_prevPageButtonActionPerformed
|
}//GEN-LAST:event_prevPageButtonActionPerformed
|
||||||
|
|
||||||
private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
|
private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
|
||||||
setDataView(currentPage + 1);
|
setDataViewByPageNumber(currentPage + 1);
|
||||||
|
goToPageTextField.setText(Integer.toString(currentPage));
|
||||||
}//GEN-LAST:event_nextPageButtonActionPerformed
|
}//GEN-LAST:event_nextPageButtonActionPerformed
|
||||||
|
|
||||||
private void goToPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_goToPageTextFieldActionPerformed
|
private void goToPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_goToPageTextFieldActionPerformed
|
||||||
@ -267,11 +291,59 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
JOptionPane.WARNING_MESSAGE);
|
JOptionPane.WARNING_MESSAGE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setDataView(pageNumber);
|
setDataViewByPageNumber(pageNumber);
|
||||||
}//GEN-LAST:event_goToPageTextFieldActionPerformed
|
}//GEN-LAST:event_goToPageTextFieldActionPerformed
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Calculates the offset relative to the current caret position.
|
||||||
|
* @param userInput the user provided signed offset value.
|
||||||
|
* @return returns the resultant offset value relative to the current caret
|
||||||
|
* position. -1L is returned if the resultant offset cannot be calculated.
|
||||||
|
*/
|
||||||
|
private long getOffsetRelativeToCaretPosition(Long userInput) {
|
||||||
|
String userSelectedLine;
|
||||||
|
try {
|
||||||
|
// get the selected line. Extract the current hex offset location.
|
||||||
|
userSelectedLine = outputViewPane.getText().subSequence(
|
||||||
|
Utilities.getRowStart(outputViewPane, outputViewPane.getCaretPosition()),
|
||||||
|
Utilities.getRowEnd(outputViewPane, outputViewPane.getCaretPosition()))
|
||||||
|
.toString();
|
||||||
|
// NOTE: This needs to change if the outputFormat of outputViewPane changes.
|
||||||
|
String hexForUserSelectedLine = userSelectedLine.substring(0, userSelectedLine.indexOf(":"));
|
||||||
|
|
||||||
|
return Long.decode(hexForUserSelectedLine) + userInput;
|
||||||
|
} catch (BadLocationException | StringIndexOutOfBoundsException | NumberFormatException ex) {
|
||||||
|
// thrown in case the caret location is out of the range of the outputViewPane.
|
||||||
|
return -1L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void goToOffsetTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_goToOffsetTextFieldActionPerformed
|
||||||
|
long offset;
|
||||||
|
try {
|
||||||
|
if (goToOffsetTextField.getText().startsWith("+") || goToOffsetTextField.getText().startsWith("-")) {
|
||||||
|
offset = getOffsetRelativeToCaretPosition(Long.decode(goToOffsetTextField.getText()));
|
||||||
|
} else {
|
||||||
|
offset = Long.decode(goToOffsetTextField.getText());
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
// notify the user and return
|
||||||
|
JOptionPane.showMessageDialog(this, NbBundle.getMessage(this.getClass(), "DataContentViewerHex.goToOffsetTextField.msgDlg", goToOffsetTextField.getText()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset >= 0) {
|
||||||
|
setDataViewByOffset(offset);
|
||||||
|
} else {
|
||||||
|
outputViewPane.setText(NbBundle.getMessage(DataContentViewerHex.class, "DataContentViewerHex.setDataView.invalidOffset.negativeOffsetValue"));
|
||||||
|
}
|
||||||
|
}//GEN-LAST:event_goToOffsetTextFieldActionPerformed
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JMenuItem copyMenuItem;
|
private javax.swing.JMenuItem copyMenuItem;
|
||||||
private javax.swing.JLabel currentPageLabel;
|
private javax.swing.JLabel currentPageLabel;
|
||||||
|
private javax.swing.JLabel goToOffsetLabel;
|
||||||
|
private javax.swing.JTextField goToOffsetTextField;
|
||||||
private javax.swing.JLabel goToPageLabel;
|
private javax.swing.JLabel goToPageLabel;
|
||||||
private javax.swing.JTextField goToPageTextField;
|
private javax.swing.JTextField goToPageTextField;
|
||||||
private javax.swing.JPanel hexViewerPanel;
|
private javax.swing.JPanel hexViewerPanel;
|
||||||
@ -289,22 +361,37 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the DataView (The tabbed panel)
|
* Sets the DataView (The tabbed panel) by page number
|
||||||
*
|
*
|
||||||
* @param page Page to display (1-based counting)
|
* @param page Page to display (1-based counting)
|
||||||
*/
|
*/
|
||||||
private void setDataView(int page) {
|
private void setDataViewByPageNumber(int page) {
|
||||||
if (this.dataSource == null) {
|
if (this.dataSource == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page == 0) {
|
if (page == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPage = page;
|
currentPage = page;
|
||||||
long offset = (currentPage - 1) * pageLength;
|
long offset = (currentPage - 1) * pageLength;
|
||||||
|
setDataView(offset);
|
||||||
|
goToOffsetTextField.setText(Long.toString(offset));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sets the DataView (The tabbed panel) by offset
|
||||||
|
*
|
||||||
|
* @param page Page to display (1-based counting)
|
||||||
|
*/
|
||||||
|
private void setDataViewByOffset(long offset) {
|
||||||
|
if (this.dataSource == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentPage = (int) (offset / pageLength) + 1;
|
||||||
|
setDataView(offset);
|
||||||
|
goToPageTextField.setText(Integer.toString(currentPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDataView(long offset) {
|
||||||
// change the cursor to "waiting cursor" for this operation
|
// change the cursor to "waiting cursor" for this operation
|
||||||
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
|
||||||
@ -327,19 +414,16 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
offset + pageLength);
|
offset + pageLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// disable or enable the next button
|
// disable or enable the next button
|
||||||
if ((errorText == null) && (currentPage < totalPages)) {
|
if ((errorText == null) && (currentPage < totalPages)) {
|
||||||
nextPageButton.setEnabled(true);
|
nextPageButton.setEnabled(true);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
nextPageButton.setEnabled(false);
|
nextPageButton.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((errorText == null) && (currentPage > 1)) {
|
if ((errorText == null) && (currentPage > 1)) {
|
||||||
prevPageButton.setEnabled(true);
|
prevPageButton.setEnabled(true);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
prevPageButton.setEnabled(false);
|
prevPageButton.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,8 +434,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
if (errorText == null) {
|
if (errorText == null) {
|
||||||
int showLength = bytesRead < pageLength ? bytesRead : (int) pageLength;
|
int showLength = bytesRead < pageLength ? bytesRead : (int) pageLength;
|
||||||
outputViewPane.setText(DataConversion.byteArrayToHex(data, showLength, offset));
|
outputViewPane.setText(DataConversion.byteArrayToHex(data, showLength, offset));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
outputViewPane.setText(errorText);
|
outputViewPane.setText(errorText);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,7 +462,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
}
|
}
|
||||||
totalPageLabel.setText(Integer.toString(totalPages));
|
totalPageLabel.setText(Integer.toString(totalPages));
|
||||||
|
|
||||||
this.setDataView(1);
|
this.setDataViewByPageNumber(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -423,6 +506,8 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
|
|||||||
pageLabel2.setVisible(isVisible);
|
pageLabel2.setVisible(isVisible);
|
||||||
goToPageTextField.setVisible(isVisible);
|
goToPageTextField.setVisible(isVisible);
|
||||||
goToPageLabel.setVisible(isVisible);
|
goToPageLabel.setVisible(isVisible);
|
||||||
|
goToOffsetTextField.setVisible(isVisible);
|
||||||
|
goToOffsetLabel.setVisible(isVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -22,6 +22,7 @@ import java.awt.CardLayout;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import static java.util.Objects.nonNull;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
@ -33,8 +34,10 @@ import org.openide.util.lookup.ServiceProviders;
|
|||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||||
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.autopsy.modules.filetypeid.FileTypeDetector;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.AbstractFile.MimeMatchEnum;
|
import org.sleuthkit.datamodel.AbstractFile.MimeMatchEnum;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
|
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -180,54 +183,70 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* is the given file a video we can display?
|
||||||
*
|
*
|
||||||
* @param file
|
* @param file
|
||||||
|
*
|
||||||
* @return True if a video file that can be displayed
|
* @return True if a video file that can be displayed
|
||||||
*/
|
*/
|
||||||
private boolean isVideoSupported(AbstractFile file) {
|
private boolean isVideoSupported(AbstractFile file) {
|
||||||
String name = file.getName().toLowerCase();
|
String name = file.getName().toLowerCase();
|
||||||
|
|
||||||
if ((containsExt(name, AUDIO_EXTENSIONS) || containsExt(name, videoExtensions)) &&
|
//TODO: is this what we want, to require both extension and mimetype support?
|
||||||
(!videoMimes.isEmpty() && file.isMimeType(videoMimes) == MimeMatchEnum.TRUE)) {
|
if (AUDIO_EXTENSIONS.contains("." + name) || videoExtensions.contains("." + name)) {
|
||||||
|
try {
|
||||||
|
String mimeType = new FileTypeDetector().detect(file);
|
||||||
|
if (nonNull(mimeType) && videoMimes.contains(mimeType)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} catch (FileTypeDetector.FileTypeDetectorInitException | TskCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, "Failed to look up mimetype for " + file.getName() + " using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
|
||||||
|
if (!videoMimes.isEmpty() && file.isMimeType(videoMimes) == MimeMatchEnum.TRUE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* is the given file an image that we can display?
|
||||||
*
|
*
|
||||||
* @param file
|
* @param file
|
||||||
|
*
|
||||||
* @return True if an image file that can be displayed
|
* @return True if an image file that can be displayed
|
||||||
*/
|
*/
|
||||||
private boolean isImageSupported(AbstractFile file) {
|
private boolean isImageSupported(AbstractFile file) {
|
||||||
String name = file.getName().toLowerCase();
|
String name = file.getName().toLowerCase();
|
||||||
|
|
||||||
// blackboard
|
// blackboard
|
||||||
|
try {
|
||||||
|
String mimeType = new FileTypeDetector().detect(file);
|
||||||
|
if (nonNull(mimeType) && imageMimes.contains(mimeType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (FileTypeDetector.FileTypeDetectorInitException | TskCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, "Failed to look up mimetype for " + file.getName() + " using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
|
||||||
if (!imageMimes.isEmpty()) {
|
if (!imageMimes.isEmpty()) {
|
||||||
MimeMatchEnum mimeMatch = file.isMimeType(imageMimes);
|
MimeMatchEnum mimeMatch = file.isMimeType(imageMimes);
|
||||||
if (mimeMatch == MimeMatchEnum.TRUE) {
|
if (mimeMatch == MimeMatchEnum.TRUE) {
|
||||||
return true;
|
return true;
|
||||||
}
|
} else if (mimeMatch == MimeMatchEnum.FALSE) {
|
||||||
else if (mimeMatch == MimeMatchEnum.FALSE) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// extension
|
// extension
|
||||||
if (containsExt(name, imageExtensions)) {
|
if (imageExtensions.contains("." + name)) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// our own signature checks for important types
|
|
||||||
else if (ImageUtils.isJpegFileHeader(file)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (ImageUtils.isPngFileHeader(file)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//for gstreamer formats, check if initialized first, then
|
// our own signature checks for important types
|
||||||
//support audio formats, and video formats
|
if (ImageUtils.isJpegFileHeader(file)) {
|
||||||
return false;
|
return true;
|
||||||
|
}
|
||||||
|
return ImageUtils.isPngFileHeader(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -246,14 +265,16 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (imagePanelInited) {
|
if (imagePanelInited) {
|
||||||
if (isImageSupported(file))
|
if (isImageSupported(file)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (videoPanelInited && videoPanel.isInited()) {
|
if (videoPanelInited && videoPanel.isInited()) {
|
||||||
if (isVideoSupported(file))
|
if (isVideoSupported(file)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -265,24 +286,13 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
if (file == null) {
|
if (file == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
String name = file.getName().toLowerCase();
|
String extension = file.getNameExtension();
|
||||||
boolean deleted = file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC);
|
boolean deleted = file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC);
|
||||||
|
|
||||||
if (containsExt(name, videoExtensions) && deleted) {
|
if (videoExtensions.contains("." + extension) && deleted) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean containsExt(String name, Set<String> exts) {
|
|
||||||
int extStart = name.lastIndexOf(".");
|
|
||||||
String ext = "";
|
|
||||||
if (extStart != -1) {
|
|
||||||
ext = name.substring(extStart, name.length()).toLowerCase();
|
|
||||||
}
|
|
||||||
return exts.contains(ext);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
|||||||
private DataContent customContentViewer;
|
private DataContent customContentViewer;
|
||||||
private boolean isMain;
|
private boolean isMain;
|
||||||
private String title;
|
private String title;
|
||||||
private final DummyNodeListener dummyNodeListener = new DummyNodeListener();
|
private final RootNodeListener rootNodeListener = new RootNodeListener();
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(DataResultPanel.class.getName() );
|
private static final Logger logger = Logger.getLogger(DataResultPanel.class.getName() );
|
||||||
private boolean listeningToTabbedPane = false;
|
private boolean listeningToTabbedPane = false;
|
||||||
@ -369,7 +369,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
|||||||
@Override
|
@Override
|
||||||
public void setNode(Node selectedNode) {
|
public void setNode(Node selectedNode) {
|
||||||
if (this.rootNode != null) {
|
if (this.rootNode != null) {
|
||||||
this.rootNode.removeNodeListener(dummyNodeListener);
|
this.rootNode.removeNodeListener(rootNodeListener);
|
||||||
}
|
}
|
||||||
// Deferring becoming a listener to the tabbed pane until this point
|
// Deferring becoming a listener to the tabbed pane until this point
|
||||||
// eliminates handling a superfluous stateChanged event during construction.
|
// eliminates handling a superfluous stateChanged event during construction.
|
||||||
@ -380,8 +380,8 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
|||||||
|
|
||||||
this.rootNode = selectedNode;
|
this.rootNode = selectedNode;
|
||||||
if (this.rootNode != null) {
|
if (this.rootNode != null) {
|
||||||
dummyNodeListener.reset();
|
rootNodeListener.reset();
|
||||||
this.rootNode.addNodeListener(dummyNodeListener);
|
this.rootNode.addNodeListener(rootNodeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
resetTabs(selectedNode);
|
resetTabs(selectedNode);
|
||||||
@ -620,28 +620,33 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DummyNodeListener implements NodeListener {
|
private class RootNodeListener implements NodeListener {
|
||||||
|
|
||||||
private volatile boolean load = true;
|
private volatile boolean waitingForData = true;
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
load = true;
|
waitingForData = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void childrenAdded(final NodeMemberEvent nme) {
|
public void childrenAdded(final NodeMemberEvent nme) {
|
||||||
Node[] delta = nme.getDelta();
|
Node[] delta = nme.getDelta();
|
||||||
if (load && containsReal(delta)) {
|
updateMatches();
|
||||||
load = false;
|
|
||||||
|
/* There is a known issue in this code whereby we will only
|
||||||
|
call setupTabs() once even though childrenAdded could be
|
||||||
|
called multiple times. That means that each panel may not
|
||||||
|
have access to all of the children when they decide if they
|
||||||
|
support the content */
|
||||||
|
if (waitingForData && containsReal(delta)) {
|
||||||
|
waitingForData = false;
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
if (SwingUtilities.isEventDispatchThread()) {
|
||||||
setupTabs(nme.getNode());
|
setupTabs(nme.getNode());
|
||||||
updateMatches();
|
|
||||||
} else {
|
} else {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
setupTabs(nme.getNode());
|
setupTabs(nme.getNode());
|
||||||
updateMatches();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013 Basis Technology Corp.
|
* Copyright 2013-2015 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");
|
||||||
@ -20,8 +20,10 @@ package org.sleuthkit.autopsy.coreutils;
|
|||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
@ -52,8 +54,8 @@ public class MessageNotifyUtil {
|
|||||||
INFO(NotifyDescriptor.INFORMATION_MESSAGE, "info-icon-16.png"), //NON-NLS
|
INFO(NotifyDescriptor.INFORMATION_MESSAGE, "info-icon-16.png"), //NON-NLS
|
||||||
ERROR(NotifyDescriptor.ERROR_MESSAGE, "error-icon-16.png"), //NON-NLS
|
ERROR(NotifyDescriptor.ERROR_MESSAGE, "error-icon-16.png"), //NON-NLS
|
||||||
WARNING(NotifyDescriptor.WARNING_MESSAGE, "warning-icon-16.png"); //NON-NLS
|
WARNING(NotifyDescriptor.WARNING_MESSAGE, "warning-icon-16.png"); //NON-NLS
|
||||||
private int notifyDescriptorType;
|
private final int notifyDescriptorType;
|
||||||
private Icon icon;
|
private final Icon icon;
|
||||||
|
|
||||||
private MessageType(int notifyDescriptorType, String resourceName) {
|
private MessageType(int notifyDescriptorType, String resourceName) {
|
||||||
this.notifyDescriptorType = notifyDescriptorType;
|
this.notifyDescriptorType = notifyDescriptorType;
|
||||||
@ -138,10 +140,12 @@ public class MessageNotifyUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility to display notifications with baloons
|
* Utility to display notifications with balloons
|
||||||
*/
|
*/
|
||||||
public static class Notify {
|
public static class Notify {
|
||||||
|
|
||||||
|
private static final SimpleDateFormat TIME_STAMP_FORMAT = new SimpleDateFormat("MM/dd/yy HH:mm:ss z");
|
||||||
|
|
||||||
//notifications to keep track of and to reset when case is closed
|
//notifications to keep track of and to reset when case is closed
|
||||||
private static final List<Notification> notifications = Collections.synchronizedList(new ArrayList<Notification>());
|
private static final List<Notification> notifications = Collections.synchronizedList(new ArrayList<Notification>());
|
||||||
|
|
||||||
@ -149,13 +153,12 @@ public class MessageNotifyUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear pending notifications
|
* Clear pending notifications Should really only be used by Case
|
||||||
* Should really only be used by Case
|
|
||||||
*/
|
*/
|
||||||
public static void clear() {
|
public static void clear() {
|
||||||
for (Notification n : notifications) {
|
notifications.stream().forEach((n) -> {
|
||||||
n.clear();
|
n.clear();
|
||||||
}
|
});
|
||||||
notifications.clear();
|
notifications.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,8 +166,8 @@ public class MessageNotifyUtil {
|
|||||||
* Show message with the specified type and action listener
|
* Show message with the specified type and action listener
|
||||||
*/
|
*/
|
||||||
public static void show(String title, String message, MessageType type, ActionListener actionListener) {
|
public static void show(String title, String message, MessageType type, ActionListener actionListener) {
|
||||||
Notification newNotification =
|
Notification newNotification
|
||||||
NotificationDisplayer.getDefault().notify(title, type.getIcon(), message, actionListener);
|
= NotificationDisplayer.getDefault().notify(addTimeStampToTitle(title), type.getIcon(), message, actionListener);
|
||||||
notifications.add(newNotification);
|
notifications.add(newNotification);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,11 +181,8 @@ public class MessageNotifyUtil {
|
|||||||
* @param type type of the message
|
* @param type type of the message
|
||||||
*/
|
*/
|
||||||
public static void show(String title, final String message, final MessageType type) {
|
public static void show(String title, final String message, final MessageType type) {
|
||||||
ActionListener actionListener = new ActionListener() {
|
ActionListener actionListener = (ActionEvent e) -> {
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
MessageNotifyUtil.Message.show(message, type);
|
MessageNotifyUtil.Message.show(message, type);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
show(title, message, type, actionListener);
|
show(title, message, type, actionListener);
|
||||||
@ -217,5 +217,17 @@ public class MessageNotifyUtil {
|
|||||||
public static void warn(String title, String message) {
|
public static void warn(String title, String message) {
|
||||||
show(title, message, MessageType.WARNING);
|
show(title, message, MessageType.WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a time stamp prefix to the title of notifications so that they
|
||||||
|
* will be in order (they are sorted alphabetically) in the
|
||||||
|
* notifications area.
|
||||||
|
*
|
||||||
|
* @param title A notification title without a time stamp prefix.
|
||||||
|
* @return The notification title with a time stamp prefix.
|
||||||
|
*/
|
||||||
|
private static String addTimeStampToTitle(String title) {
|
||||||
|
return TIME_STAMP_FORMAT.format(new Date()) + " " + title;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ import org.openide.nodes.Node;
|
|||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.lookup.Lookups;
|
import org.openide.util.lookup.Lookups;
|
||||||
|
import org.openide.windows.WindowManager;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||||
@ -112,6 +113,7 @@ public class DeletedContent implements AutopsyVisitableItem {
|
|||||||
"DeletedContent.deletedContentsNode.name");
|
"DeletedContent.deletedContentsNode.name");
|
||||||
private SleuthkitCase skCase;
|
private SleuthkitCase skCase;
|
||||||
|
|
||||||
|
|
||||||
DeletedContentsNode(SleuthkitCase skCase) {
|
DeletedContentsNode(SleuthkitCase skCase) {
|
||||||
super(Children.create(new DeletedContentsChildren(skCase), true), Lookups.singleton(NAME));
|
super(Children.create(new DeletedContentsChildren(skCase), true), Lookups.singleton(NAME));
|
||||||
super.setName(NAME);
|
super.setName(NAME);
|
||||||
@ -151,6 +153,8 @@ public class DeletedContent implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
private SleuthkitCase skCase;
|
private SleuthkitCase skCase;
|
||||||
private Observable notifier;
|
private Observable notifier;
|
||||||
|
// true if we have already told user that not all files will be shown
|
||||||
|
private static boolean maxFilesDialogShown = false;
|
||||||
|
|
||||||
public DeletedContentsChildren(SleuthkitCase skCase) {
|
public DeletedContentsChildren(SleuthkitCase skCase) {
|
||||||
this.skCase = skCase;
|
this.skCase = skCase;
|
||||||
@ -195,6 +199,7 @@ public class DeletedContent implements AutopsyVisitableItem {
|
|||||||
if (evt.getNewValue() == null) {
|
if (evt.getNewValue() == null) {
|
||||||
removeListeners();
|
removeListeners();
|
||||||
}
|
}
|
||||||
|
maxFilesDialogShown = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -333,15 +338,20 @@ public class DeletedContent implements AutopsyVisitableItem {
|
|||||||
List<AbstractFile> queryList = runFsQuery();
|
List<AbstractFile> queryList = runFsQuery();
|
||||||
if (queryList.size() == MAX_OBJECTS) {
|
if (queryList.size() == MAX_OBJECTS) {
|
||||||
queryList.remove(queryList.size() - 1);
|
queryList.remove(queryList.size() - 1);
|
||||||
|
|
||||||
|
// only show the dialog once - not each time we refresh
|
||||||
|
if (maxFilesDialogShown == false) {
|
||||||
|
maxFilesDialogShown = true;
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
JOptionPane.showMessageDialog(null, NbBundle.getMessage(this.getClass(),
|
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), NbBundle.getMessage(this.getClass(),
|
||||||
"DeletedContent.createKeys.maxObjects.msg",
|
"DeletedContent.createKeys.maxObjects.msg",
|
||||||
MAX_OBJECTS - 1));
|
MAX_OBJECTS - 1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
list.addAll(queryList);
|
list.addAll(queryList);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,6 @@ import org.sleuthkit.autopsy.casemodule.Case;
|
|||||||
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||||
import org.sleuthkit.datamodel.ContentTag;
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
@ -119,19 +117,14 @@ public class Tags implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
private final PropertyChangeListener pcl = new PropertyChangeListener() {
|
private final PropertyChangeListener pcl = new PropertyChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
String eventType = evt.getPropertyName();
|
String eventType = evt.getPropertyName();
|
||||||
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
|
if (eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString())
|
||||||
/* Note: this is a hack. In an ideal world, TagsManager
|
|| eventType.equals(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString())
|
||||||
* would fire events so that the directory tree would
|
|| eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())
|
||||||
* refresh. But, we haven't had a chance to add that so, we
|
|| eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
|
||||||
* fire these events and the tree refreshes based on them. */
|
|
||||||
if ((((ModuleDataEvent) evt.getOldValue()).getArtifactType() == BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT)
|
|
||||||
|| ((ModuleDataEvent) evt.getOldValue()).getArtifactType() == BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE) {
|
|
||||||
refresh(true);
|
refresh(true);
|
||||||
tagResults.update();
|
tagResults.update();
|
||||||
}
|
|
||||||
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
|
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
|
||||||
refresh(true);
|
refresh(true);
|
||||||
tagResults.update();
|
tagResults.update();
|
||||||
|
19
Core/src/org/sleuthkit/autopsy/events/AutopsyEvent.java
Normal file
19
Core/src/org/sleuthkit/autopsy/events/AutopsyEvent.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.events;
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a place holder class to be overwritten by the version in the
|
||||||
|
* Collaborative branch.
|
||||||
|
*/
|
||||||
|
abstract class AutopsyEvent extends PropertyChangeEvent {
|
||||||
|
|
||||||
|
AutopsyEvent(Object source, String propertyName, Object oldValue, Object newValue) {
|
||||||
|
super(source, propertyName, oldValue, newValue);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2015 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.events;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BlackBoardArtifactTagAddedEvent extends TagAddedEvent<BlackboardArtifactTag> {
|
||||||
|
|
||||||
|
public BlackBoardArtifactTagAddedEvent(BlackboardArtifactTag newTag) {
|
||||||
|
super(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString(), newTag
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2015 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.events;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BlackBoardArtifactTagDeletedEvent extends TagDeletedEvent<BlackboardArtifactTag> {
|
||||||
|
|
||||||
|
public BlackBoardArtifactTagDeletedEvent(BlackboardArtifactTag oldValue) {
|
||||||
|
super(Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString(), oldValue);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2015 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.events;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that is fired when a ContentTag is added.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public class ContentTagAddedEvent extends TagAddedEvent<ContentTag> {
|
||||||
|
|
||||||
|
public ContentTagAddedEvent(ContentTag newTag) {
|
||||||
|
super(Case.Events.CONTENT_TAG_ADDED.toString(), newTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2015 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.events;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that is fired when a ContentTag is deleted.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public class ContentTagDeletedEvent extends TagDeletedEvent<ContentTag> {
|
||||||
|
|
||||||
|
public ContentTagDeletedEvent(ContentTag deletedTag) {
|
||||||
|
super(Case.Events.CONTENT_TAG_DELETED.toString(), deletedTag);
|
||||||
|
}
|
||||||
|
}
|
@ -16,29 +16,31 @@
|
|||||||
* 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.imagegallery.datamodel;
|
package org.sleuthkit.autopsy.events;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.datamodel.Tag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event broadcast to various UI componenets when one or more files' category
|
* Base Class for events that are fired when a Tag is added
|
||||||
* has been changed
|
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
public class CategoryChangeEvent {
|
abstract class TagAddedEvent<T extends Tag> extends TagEvent<T> {
|
||||||
|
|
||||||
private final Collection<Long> ids;
|
protected TagAddedEvent(String propertyName, T newValue) {
|
||||||
|
super(Case.class, propertyName, null, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the fileIDs of the files whose categories have changed
|
* get the Tag that was added
|
||||||
|
*
|
||||||
|
* @return the tTag
|
||||||
*/
|
*/
|
||||||
public Collection<Long> getIds() {
|
@SuppressWarnings("unchecked")
|
||||||
return Collections.unmodifiableCollection(ids);
|
@Override
|
||||||
|
public T getTag() {
|
||||||
|
return (T) getNewValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CategoryChangeEvent(Collection<Long> ids) {
|
|
||||||
this.ids = ids;
|
|
||||||
}
|
|
||||||
}
|
}
|
45
Core/src/org/sleuthkit/autopsy/events/TagDeletedEvent.java
Normal file
45
Core/src/org/sleuthkit/autopsy/events/TagDeletedEvent.java
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2015 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.events;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.datamodel.Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base Class for events that are fired when a Tag is deleted
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
abstract class TagDeletedEvent<T extends Tag> extends TagEvent<T> {
|
||||||
|
|
||||||
|
protected TagDeletedEvent(String propertyName, T oldValue) {
|
||||||
|
super(Case.class, propertyName, oldValue, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the Tag that was deleted
|
||||||
|
*
|
||||||
|
* @return the Tag
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public T getTag() {
|
||||||
|
return (T) getOldValue();
|
||||||
|
}
|
||||||
|
}
|
26
Core/src/org/sleuthkit/autopsy/events/TagEvent.java
Normal file
26
Core/src/org/sleuthkit/autopsy/events/TagEvent.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.events;
|
||||||
|
|
||||||
|
import org.sleuthkit.datamodel.Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
abstract public class TagEvent<T extends Tag> extends AutopsyEvent {
|
||||||
|
|
||||||
|
public TagEvent(Object source, String propertyName, Object oldValue, Object newValue) {
|
||||||
|
super(source, propertyName, oldValue, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the Tag that this event is for
|
||||||
|
*
|
||||||
|
* @return the Tag
|
||||||
|
*/
|
||||||
|
public abstract T getTag();
|
||||||
|
|
||||||
|
}
|
@ -17,9 +17,9 @@ EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.msg=Error
|
|||||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.details=Error initializing output dir\: {0}\: {1}
|
EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.details=Error initializing output dir\: {0}\: {1}
|
||||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errCantInitLib=Could not initialize 7-ZIP library\: {0}
|
EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errCantInitLib=Could not initialize 7-ZIP library\: {0}
|
||||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=Possible ZIP bomb detected in archive\: {0}, item\: {1}
|
EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=Possible ZIP bomb detected in archive\: {0}, item\: {1}
|
||||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=The archive item compression ratio is {0}, skipping processing of this archive item.
|
EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=Compression ratio is {0}, skipping item 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 this archive and its contents
|
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.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.
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
OpenIDE-Module-Display-Category=\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30E2\u30B8\u30E5\u30FC\u30EB
|
OpenIDE-Module-Display-Category=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb
|
||||||
OpenIDE-Module-Long-Description=\
|
OpenIDE-Module-Long-Description=\
|
||||||
7Zip\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30E2\u30B8\u30E5\u30FC\u30EB\n\n7Zip\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30E2\u30B8\u30E5\u30FC\u30EB\u306F\u30A2\u30FC\u30AB\u30A4\u30D6\u30D5\u30A1\u30A4\u30EB\u3092\u51E6\u7406\u3057\u307E\u3059\uFF08zip\u30847zip\u30A8\u30AF\u30B9\u30C8\u30E9\u30AF\u30BF\u30FC\u306B\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u308B\u305D\u306E\u4ED6\u306E\u30A2\u30FC\u30AB\u30A4\u30D6\u30BF\u30A4\u30D7\u306A\u3069\uFF09\u3002\n\
|
7Zip\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\n\n7Zip\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb\u3092\u51e6\u7406\u3057\u307e\u3059\uff08zip\u30847zip\u30a8\u30af\u30b9\u30c8\u30e9\u30af\u30bf\u30fc\u306b\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u305d\u306e\u4ed6\u306e\u30a2\u30fc\u30ab\u30a4\u30d6\u30bf\u30a4\u30d7\u306a\u3069\uff09\u3002\n\
|
||||||
\u30A2\u30FC\u30AB\u30A4\u30D6\u306E\u30B3\u30F3\u30C6\u30F3\u30C4\u306F\u62BD\u51FA\u3055\u308C\u3001\u6D3E\u751F\u30D5\u30A1\u30A4\u30EB\u306F\u8A2D\u5B9A\u3055\u308C\u305F\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30E2\u30B8\u30E5\u30FC\u30EB\u306B\u51E6\u7406\u3055\u308C\u308B\u305F\u3081\u3001\u73FE\u5728\u306E\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u306B\u8FFD\u52A0\u3055\u308C\u307E\u3059\u3002\n\
|
\u30a2\u30fc\u30ab\u30a4\u30d6\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u306f\u62bd\u51fa\u3055\u308c\u3001\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u306f\u8a2d\u5b9a\u3055\u308c\u305f\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb\u306b\u51e6\u7406\u3055\u308c\u308b\u305f\u3081\u3001\u73fe\u5728\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u8ffd\u52a0\u3055\u308c\u307e\u3059\u3002\n\
|
||||||
\u3082\u3057\u6D3E\u751F\u30D5\u30A1\u30A4\u30EB\u304C\u30A2\u30FC\u30AB\u30A4\u30D6\u30D5\u30A1\u30A4\u30EB\u3067\u3042\u308C\u3070\u30017Zip\u30A8\u30AF\u30B9\u30C8\u30E9\u30AF\u30BF\u30FC\u306B\u3088\u308A\u3001\u518D\u5EA6\u51E6\u7406\u3055\u308C\u307E\u3059 - \u30A8\u30AF\u30B9\u30C8\u30E9\u30AF\u30BF\u30FC\u306F\u30A2\u30FC\u30AB\u30A4\u30D6\u30D5\u30A1\u30A4\u30EB\u3092N-\u30EC\u30D9\u30EB\u306E\u6DF1\u3055\u3067\u51E6\u7406\u3057\u307E\u3059\u3002\n\n\
|
\u3082\u3057\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u304c\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb\u3067\u3042\u308c\u3070\u30017Zip\u30a8\u30af\u30b9\u30c8\u30e9\u30af\u30bf\u30fc\u306b\u3088\u308a\u3001\u518d\u5ea6\u51e6\u7406\u3055\u308c\u307e\u3059 - \u30a8\u30af\u30b9\u30c8\u30e9\u30af\u30bf\u30fc\u306f\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb\u3092N-\u30ec\u30d9\u30eb\u306e\u6df1\u3055\u3067\u51e6\u7406\u3057\u307e\u3059\u3002\n\n\
|
||||||
\u62BD\u51FA\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u306F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30C4\u30EA\u30FC\u3067\u30CA\u30D3\u30B2\u30FC\u30C8\u3067\u304D\u307E\u3059\u3002\n\n\
|
\u62bd\u51fa\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30c4\u30ea\u30fc\u3067\u30ca\u30d3\u30b2\u30fc\u30c8\u3067\u304d\u307e\u3059\u3002\n\n\
|
||||||
\u3053\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u306FWindows\u3001Linux\u3001Mac\u306E\u30AA\u30DA\u30EC\u30FC\u30C6\u30A3\u30F3\u30B0\u30B7\u30B9\u30C6\u30E0\u74B0\u5883\u3092\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u307E\u3059\u3002
|
\u3053\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u306fWindows\u3001Linux\u3001Mac\u306e\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0\u74b0\u5883\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u3059\u3002
|
||||||
OpenIDE-Module-Name=7Zip
|
OpenIDE-Module-Name=7Zip
|
||||||
OpenIDE-Module-Short-Description=7Zip\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u30E2\u30B8\u30E5\u30FC\u30EB
|
OpenIDE-Module-Short-Description=7Zip\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30e2\u30b8\u30e5\u30fc\u30eb
|
||||||
SevenZipContentReadStream.seek.exception.invalidOrigin=\u7121\u52B9\u306A\u30B7\u30FC\u30AF\u30AA\u30EA\u30B8\u30F3\uFF1A {0}
|
SevenZipContentReadStream.seek.exception.invalidOrigin=\u7121\u52b9\u306a\u30b7\u30fc\u30af\u30aa\u30ea\u30b8\u30f3\uff1a {0}
|
||||||
SevenZipContentReadStream.read.exception.errReadStream=\u30B3\u30F3\u30C6\u30F3\u30C4\u30B9\u30C8\u30EA\u30FC\u30E0\u306E\u8AAD\u307F\u53D6\u308A\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
|
SevenZipContentReadStream.read.exception.errReadStream=\u30b3\u30f3\u30c6\u30f3\u30c4\u30b9\u30c8\u30ea\u30fc\u30e0\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
|
||||||
SevenZipIngestModule.moduleName=\u30A2\u30FC\u30AB\u30A4\u30D6\u30A8\u30AF\u30B9\u30C8\u30E9\u30AF\u30BF\u30FC
|
SevenZipIngestModule.moduleName=\u30a2\u30fc\u30ab\u30a4\u30d6\u30a8\u30af\u30b9\u30c8\u30e9\u30af\u30bf\u30fc
|
||||||
SevenZipIngestModule.moduleDesc.text=\u30A2\u30FC\u30AB\u30A4\u30D6\u30D5\u30A1\u30A4\u30EB(zip, rar, arj, 7z, gzip, bzip2, tar)\u3092\u62BD\u51FA\u3057\u3001\u73FE\u5728\u306E\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u306B\u30EA\u30B9\u30B1\u3057\u3001\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30C4\u30EA\u30FC\u306B\u65B0\u898F\u30D5\u30A1\u30A4\u30EB\u3092\u6295\u5165\u3057\u307E\u3059\u3002
|
SevenZipIngestModule.moduleDesc.text=\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb(zip, rar, arj, 7z, gzip, bzip2, tar)\u3092\u62bd\u51fa\u3057\u3001\u73fe\u5728\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u306b\u30ea\u30b9\u30b1\u3057\u3001\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30c4\u30ea\u30fc\u306b\u65b0\u898f\u30d5\u30a1\u30a4\u30eb\u3092\u6295\u5165\u3057\u307e\u3059\u3002
|
||||||
SevenZipIngestModule.encryptionFileLevel=\u30D5\u30A1\u30A4\u30EB\u30EC\u30D9\u30EB\u6697\u53F7\u5316
|
SevenZipIngestModule.encryptionFileLevel=\u30d5\u30a1\u30a4\u30eb\u30ec\u30d9\u30eb\u6697\u53f7\u5316
|
||||||
SevenZipIngestModule.encryptionFull=\u5168\u4F53\u6697\u53F7\u5316
|
SevenZipIngestModule.encryptionFull=\u5168\u4f53\u6697\u53f7\u5316
|
||||||
SevenZipIngestModule.init.errInitModule.msg={0}\u306E\u521D\u671F\u5316\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F
|
SevenZipIngestModule.init.errInitModule.msg={0}\u306e\u521d\u671f\u5316\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
|
||||||
SevenZipIngestModule.init.errInitModule.details=\u30A2\u30A6\u30C8\u30D7\u30C3\u30C8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\: {0}\: {1}\u306E\u521D\u671F\u5316\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F
|
SevenZipIngestModule.init.errInitModule.details=\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\: {0}\: {1}\u306e\u521d\u671f\u5316\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
|
||||||
SevenZipIngestModule.init.errCantInitLib=7-Zip\u30E9\u30A4\u30D6\u30E9\u30EA\: {0}\u3092\u521D\u671F\u5316\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F
|
SevenZipIngestModule.init.errCantInitLib=7-Zip\u30e9\u30a4\u30d6\u30e9\u30ea\: {0}\u3092\u521d\u671f\u5316\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f
|
||||||
SevenZipIngestModule.isZipBombCheck.warnMsg=Zip\u7206\u5F3E\u306E\u53EF\u80FD\u6027\u304C\u3042\u308B\u3082\u306E\u304C\u30A2\u30FC\u30AB\u30A4\u30D6\: {0}, item\: {1}\u306B\u691C\u51FA\u3055\u308C\u307E\u3057\u305F
|
SevenZipIngestModule.isZipBombCheck.warnMsg=Zip\u7206\u5f3e\u306e\u53ef\u80fd\u6027\u304c\u3042\u308b\u3082\u306e\u304c\u30a2\u30fc\u30ab\u30a4\u30d6\: {0}, item\: {1}\u306b\u691c\u51fa\u3055\u308c\u307e\u3057\u305f
|
||||||
SevenZipIngestModule.isZipBombCheck.warnDetails=\u30A2\u30FC\u30AB\u30A4\u30D6\u30A2\u30A4\u30C6\u30E0\u306E\u5727\u7E2E\u7387\u306F{0}\u3001\u3053\u306E\u30A2\u30FC\u30AB\u30A4\u30D6\u30A2\u30A4\u30C6\u30E0\u306E\u51E6\u7406\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3059\u3002
|
SevenZipIngestModule.isZipBombCheck.warnDetails=\u30a2\u30fc\u30ab\u30a4\u30d6\u30a2\u30a4\u30c6\u30e0\u306e\u5727\u7e2e\u7387\u306f{0}\u3001\u3053\u306e\u30a2\u30fc\u30ab\u30a4\u30d6\u30a2\u30a4\u30c6\u30e0\u306e\u51e6\u7406\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059\u3002{1}
|
||||||
SevenZipIngestModule.unpack.warnMsg.zipBomb=Zip\u7206\u5F3E\u306E\u53EF\u80FD\u6027\u304C\u3042\u308B\u3082\u306E\u304C\u691C\u51FA\u3055\u308C\u307E\u3057\u305F\uFF1A {0}
|
SevenZipIngestModule.unpack.warnMsg.zipBomb=Zip\u7206\u5f3e\u306e\u53ef\u80fd\u6027\u304c\u3042\u308b\u3082\u306e\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f\uff1a {0}
|
||||||
SevenZipIngestModule.unpack.warnDetails.zipBomb=\u30A2\u30FC\u30AB\u30A4\u30D6\u306F {0}\u30EC\u30D9\u30EB\u306E\u6DF1\u3055\u3067\u3059\u3001\u3053\u306E\u30A2\u30FC\u30AB\u30A4\u30D6\u3068\u305D\u306E\u30B3\u30F3\u30C6\u30F3\u30C4\u306E\u51E6\u7406\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3059
|
SevenZipIngestModule.unpack.warnDetails.zipBomb=\u30a2\u30fc\u30ab\u30a4\u30d6\u306f {0}\u30ec\u30d9\u30eb\u306e\u6df1\u3055\u3067\u3059\u3001\u3053\u306e\u30a2\u30fc\u30ab\u30a4\u30d6\u3068\u305d\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u51e6\u7406\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059 {1}
|
||||||
SevenZipIngestModule.unpack.unknownPath.msg=\u30A2\u30FC\u30AB\u30A4\u30D6\: {0}\u306B\u4E0D\u660E\u306E\u30A2\u30A4\u30C6\u30E0\u30D1\u30B9\u304C\u5B58\u5728\u3057\u307E\u3059\u3001{1}\u3092\u4F7F\u7528\u3057\u307E\u3059
|
SevenZipIngestModule.unpack.unknownPath.msg=\u30a2\u30fc\u30ab\u30a4\u30d6\: {0}\u306b\u4e0d\u660e\u306e\u30a2\u30a4\u30c6\u30e0\u30d1\u30b9\u304c\u5b58\u5728\u3057\u307e\u3059\u3001{1}\u3092\u4f7f\u7528\u3057\u307e\u3059
|
||||||
SevenZipIngestModule.unpack.notEnoughDiskSpace.msg=\u30A2\u30FC\u30AB\u30A4\u30D6\u30A2\u30A4\u30C6\u30E0\: {0}, {1}\u3092\u89E3\u51CD\u3059\u308B\u306E\u306B\u5341\u5206\u306A\u30C7\u30A3\u30B9\u30AF\u30B9\u30DA\u30FC\u30B9\u304C\u3042\u308A\u307E\u305B\u3093
|
SevenZipIngestModule.unpack.notEnoughDiskSpace.msg=\u30a2\u30fc\u30ab\u30a4\u30d6\u30a2\u30a4\u30c6\u30e0\: {0}, {1}\u3092\u89e3\u51cd\u3059\u308b\u306e\u306b\u5341\u5206\u306a\u30c7\u30a3\u30b9\u30af\u30b9\u30da\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093
|
||||||
SevenZipIngestModule.unpack.notEnoughDiskSpace.details=\u30A2\u30FC\u30AB\u30A4\u30D6\u30A2\u30A4\u30C6\u30E0\u306F\u89E3\u51CD\u3059\u308B\u306E\u306B\u5927\u304D\u3059\u304E\u307E\u3059\u3001\u3053\u306E\u30A2\u30A4\u30C6\u30E0\u306E\u89E3\u51CD\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3059\u3002
|
SevenZipIngestModule.unpack.notEnoughDiskSpace.details=\u30a2\u30fc\u30ab\u30a4\u30d6\u30a2\u30a4\u30c6\u30e0\u306f\u89e3\u51cd\u3059\u308b\u306e\u306b\u5927\u304d\u3059\u304e\u307e\u3059\u3001\u3053\u306e\u30a2\u30a4\u30c6\u30e0\u306e\u89e3\u51cd\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u307e\u3059\u3002
|
||||||
SevenZipIngestModule.unpack.errUnpacking.msg={0}\u89E3\u51CD\u30A8\u30E9\u30FC
|
SevenZipIngestModule.unpack.errUnpacking.msg={0}\u89e3\u51cd\u30a8\u30e9\u30fc
|
||||||
SevenZipIngestModule.unpack.errUnpacking.details={0}. {1}\u306E\u89E3\u51CD\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F
|
SevenZipIngestModule.unpack.errUnpacking.details={0}. {1}\u306e\u89e3\u51cd\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
|
||||||
SevenZipIngestModule.unpack.encrFileDetected.msg=\u30A2\u30FC\u30AB\u30A4\u30D6\u306B\u6697\u53F7\u5316\u30D5\u30A1\u30A4\u30EB\u304C\u691C\u51FA\u3055\u308C\u307E\u3057\u305F\u3002
|
SevenZipIngestModule.unpack.encrFileDetected.msg=\u30a2\u30fc\u30ab\u30a4\u30d6\u306b\u6697\u53f7\u5316\u30d5\u30a1\u30a4\u30eb\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f\u3002
|
||||||
SevenZipIngestModule.unpack.encrFileDetected.details=\u30A2\u30FC\u30AB\u30A4\u30D6\: {0}\u306E\u4E00\u90E8\u306E\u30D5\u30A1\u30A4\u30EB\u304C\u6697\u53F7\u5316\u3055\u308C\u3066\u3044\u307E\u3059\u3002{1}\u30A8\u30AF\u30B9\u30C8\u30E9\u30AF\u30BF\u30FC\u306F\u3053\u306E\u30A2\u30FC\u30AB\u30A4\u30D6\u304B\u3089\u5168\u3066\u306E\u30D5\u30A1\u30A4\u30EB\u306F\u62BD\u51FA\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002
|
SevenZipIngestModule.unpack.encrFileDetected.details=\u30a2\u30fc\u30ab\u30a4\u30d6\: {0}\u306e\u4e00\u90e8\u306e\u30d5\u30a1\u30a4\u30eb\u304c\u6697\u53f7\u5316\u3055\u308c\u3066\u3044\u307e\u3059\u3002{1}\u30a8\u30af\u30b9\u30c8\u30e9\u30af\u30bf\u30fc\u306f\u3053\u306e\u30a2\u30fc\u30ab\u30a4\u30d6\u304b\u3089\u5168\u3066\u306e\u30d5\u30a1\u30a4\u30eb\u306f\u62bd\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
|
||||||
SevenZipIngestModule.UnpackStream.write.exception.msg=\u89E3\u51CD\u3057\u305F\u30D5\u30A1\u30A4\u30EB\u306E\u4E0B\u8A18\u3078\u306E\u66F8\u304D\u8FBC\u307F\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\: {0}
|
SevenZipIngestModule.UnpackStream.write.exception.msg=\u89e3\u51cd\u3057\u305f\u30d5\u30a1\u30a4\u30eb\u306e\u4e0b\u8a18\u3078\u306e\u66f8\u304d\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\: {0}
|
||||||
SevenZipIngestModule.UnpackedTree.exception.msg=\u6D3E\u751F\u30D5\u30A1\u30A4\u30EB\u3092\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\:{0}\u306B\u8FFD\u52A0\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F
|
SevenZipIngestModule.UnpackedTree.exception.msg=\u6d3e\u751f\u30d5\u30a1\u30a4\u30eb\u3092\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\:{0}\u306b\u8ffd\u52a0\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
|
@ -40,6 +40,7 @@ import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
|
|||||||
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
|
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
|
||||||
import org.netbeans.api.progress.ProgressHandle;
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
import org.netbeans.api.progress.ProgressHandleFactory;
|
import org.netbeans.api.progress.ProgressHandleFactory;
|
||||||
|
import org.openide.util.Exceptions;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||||
@ -178,7 +179,7 @@ class SevenZipExtractor {
|
|||||||
* @param archiveFileItem the archive item
|
* @param archiveFileItem the archive item
|
||||||
* @return true if potential zip bomb, false otherwise
|
* @return true if potential zip bomb, false otherwise
|
||||||
*/
|
*/
|
||||||
private boolean isZipBombArchiveItemCheck(String archiveName, ISimpleInArchiveItem archiveFileItem) {
|
private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISimpleInArchiveItem archiveFileItem) {
|
||||||
try {
|
try {
|
||||||
final Long archiveItemSize = archiveFileItem.getSize();
|
final Long archiveItemSize = archiveFileItem.getSize();
|
||||||
|
|
||||||
@ -190,7 +191,7 @@ class SevenZipExtractor {
|
|||||||
final Long archiveItemPackedSize = archiveFileItem.getPackedSize();
|
final Long archiveItemPackedSize = archiveFileItem.getPackedSize();
|
||||||
|
|
||||||
if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) {
|
if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) {
|
||||||
logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", new Object[]{archiveName, archiveFileItem.getPath()}); //NON-NLS
|
logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", new Object[]{archiveFile.getName(), archiveFileItem.getPath()}); //NON-NLS
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,9 +201,15 @@ class SevenZipExtractor {
|
|||||||
String itemName = archiveFileItem.getPath();
|
String itemName = archiveFileItem.getPath();
|
||||||
logger.log(Level.INFO, "Possible zip bomb detected, compression ration: {0} for in archive item: {1}", new Object[]{cRatio, itemName}); //NON-NLS
|
logger.log(Level.INFO, "Possible zip bomb detected, compression ration: {0} for in archive item: {1}", new Object[]{cRatio, itemName}); //NON-NLS
|
||||||
String msg = NbBundle.getMessage(this.getClass(),
|
String msg = NbBundle.getMessage(this.getClass(),
|
||||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveName, itemName);
|
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveFile.getName(), itemName);
|
||||||
|
String path;
|
||||||
|
try {
|
||||||
|
path = archiveFile.getUniquePath();
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
path = archiveFile.getParentPath() + archiveFile.getName();
|
||||||
|
}
|
||||||
String details = NbBundle.getMessage(this.getClass(),
|
String details = NbBundle.getMessage(this.getClass(),
|
||||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails", cRatio);
|
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails", cRatio, path);
|
||||||
//MessageNotifyUtil.Notify.error(msg, details);
|
//MessageNotifyUtil.Notify.error(msg, details);
|
||||||
services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
|
services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
|
||||||
return true;
|
return true;
|
||||||
@ -269,20 +276,28 @@ class SevenZipExtractor {
|
|||||||
* @return list of unpacked derived files
|
* @return list of unpacked derived files
|
||||||
*/
|
*/
|
||||||
void unpack(AbstractFile archiveFile) {
|
void unpack(AbstractFile archiveFile) {
|
||||||
|
String archiveFilePath;
|
||||||
|
try {
|
||||||
|
archiveFilePath = archiveFile.getUniquePath();
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
archiveFilePath = archiveFile.getParentPath() + archiveFile.getName();
|
||||||
|
}
|
||||||
|
|
||||||
//check if already has derived files, skip
|
//check if already has derived files, skip
|
||||||
try {
|
try {
|
||||||
if (archiveFile.hasChildren()) {
|
if (archiveFile.hasChildren()) {
|
||||||
//check if local unpacked dir exists
|
//check if local unpacked dir exists
|
||||||
if (new File(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)).exists()) {
|
if (new File(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)).exists()) {
|
||||||
logger.log(Level.INFO, "File already has been processed as it has children and local unpacked file, skipping: {0}", archiveFile.getName()); //NON-NLS
|
logger.log(Level.INFO, "File already has been processed as it has children and local unpacked file, skipping: {0}", archiveFilePath); //NON-NLS
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (TskCoreException e) {
|
} catch (TskCoreException e) {
|
||||||
logger.log(Level.INFO, "Error checking if file already has been processed, skipping: {0}", archiveFile.getName()); //NON-NLS
|
logger.log(Level.INFO, "Error checking if file already has been processed, skipping: {0}", archiveFilePath); //NON-NLS
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
List<AbstractFile> unpackedFiles = Collections.<AbstractFile>emptyList();
|
List<AbstractFile> unpackedFiles = Collections.<AbstractFile>emptyList();
|
||||||
|
|
||||||
//recursion depth check for zip bomb
|
//recursion depth check for zip bomb
|
||||||
@ -295,7 +310,7 @@ class SevenZipExtractor {
|
|||||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnMsg.zipBomb", archiveFile.getName());
|
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnMsg.zipBomb", archiveFile.getName());
|
||||||
String details = NbBundle.getMessage(this.getClass(),
|
String details = NbBundle.getMessage(this.getClass(),
|
||||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb",
|
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb",
|
||||||
parentAr.getDepth());
|
parentAr.getDepth(), archiveFilePath);
|
||||||
//MessageNotifyUtil.Notify.error(msg, details);
|
//MessageNotifyUtil.Notify.error(msg, details);
|
||||||
services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
|
services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
|
||||||
return;
|
return;
|
||||||
@ -322,7 +337,7 @@ class SevenZipExtractor {
|
|||||||
inArchive = SevenZip.openInArchive(options, stream);
|
inArchive = SevenZip.openInArchive(options, stream);
|
||||||
|
|
||||||
int numItems = inArchive.getNumberOfItems();
|
int numItems = inArchive.getNumberOfItems();
|
||||||
logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{archiveFile.getName(), numItems}); //NON-NLS
|
logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{archiveFilePath, numItems}); //NON-NLS
|
||||||
progress.start(numItems);
|
progress.start(numItems);
|
||||||
progressStarted = true;
|
progressStarted = true;
|
||||||
|
|
||||||
@ -381,7 +396,7 @@ class SevenZipExtractor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String msg = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg",
|
String msg = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg",
|
||||||
archiveFile.getName(), pathInArchive);
|
archiveFilePath, pathInArchive);
|
||||||
logger.log(Level.WARNING, msg);
|
logger.log(Level.WARNING, msg);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -389,7 +404,7 @@ class SevenZipExtractor {
|
|||||||
logger.log(Level.INFO, "Extracted item path: {0}", pathInArchive); //NON-NLS
|
logger.log(Level.INFO, "Extracted item path: {0}", pathInArchive); //NON-NLS
|
||||||
|
|
||||||
//check if possible zip bomb
|
//check if possible zip bomb
|
||||||
if (isZipBombArchiveItemCheck(archiveFile.getName(), item)) {
|
if (isZipBombArchiveItemCheck(archiveFile, item)) {
|
||||||
continue; //skip the item
|
continue; //skip the item
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,12 +443,12 @@ class SevenZipExtractor {
|
|||||||
if (newDiskSpace < MIN_FREE_DISK_SPACE) {
|
if (newDiskSpace < MIN_FREE_DISK_SPACE) {
|
||||||
String msg = NbBundle.getMessage(this.getClass(),
|
String msg = NbBundle.getMessage(this.getClass(),
|
||||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
|
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
|
||||||
archiveFile.getName(), fileName);
|
archiveFilePath, fileName);
|
||||||
String details = NbBundle.getMessage(this.getClass(),
|
String details = NbBundle.getMessage(this.getClass(),
|
||||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
|
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
|
||||||
//MessageNotifyUtil.Notify.error(msg, details);
|
//MessageNotifyUtil.Notify.error(msg, details);
|
||||||
services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
|
services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
|
||||||
logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new Object[]{archiveFile.getUniquePath(), fileName}); //NON-NLS
|
logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new Object[]{archiveFilePath, fileName}); //NON-NLS
|
||||||
logger.log(Level.INFO, "Available disk space: {0}", new Object[]{freeDiskSpace}); //NON-NLS
|
logger.log(Level.INFO, "Available disk space: {0}", new Object[]{freeDiskSpace}); //NON-NLS
|
||||||
continue; //skip this file
|
continue; //skip this file
|
||||||
} else {
|
} else {
|
||||||
@ -523,15 +538,9 @@ class SevenZipExtractor {
|
|||||||
//TODO decide if anything to cleanup, for now bailing
|
//TODO decide if anything to cleanup, for now bailing
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (SevenZipException | TskCoreException ex) {
|
} catch (SevenZipException ex) {
|
||||||
logger.log(Level.SEVERE, "Error unpacking file: " + archiveFile, ex); //NON-NLS
|
logger.log(Level.SEVERE, "Error unpacking file: " + archiveFile, ex); //NON-NLS
|
||||||
//inbox message
|
//inbox message
|
||||||
String fullName;
|
|
||||||
try {
|
|
||||||
fullName = archiveFile.getUniquePath();
|
|
||||||
} catch (TskCoreException ex1) {
|
|
||||||
fullName = archiveFile.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
// print a message if the file is allocated
|
// print a message if the file is allocated
|
||||||
if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
|
if (archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) {
|
||||||
@ -539,7 +548,7 @@ class SevenZipExtractor {
|
|||||||
archiveFile.getName());
|
archiveFile.getName());
|
||||||
String details = NbBundle.getMessage(this.getClass(),
|
String details = NbBundle.getMessage(this.getClass(),
|
||||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details",
|
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.errUnpacking.details",
|
||||||
fullName, ex.getMessage());
|
archiveFilePath, ex.getMessage());
|
||||||
services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
|
services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -573,7 +582,7 @@ class SevenZipExtractor {
|
|||||||
artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), EmbeddedFileExtractorModuleFactory.getModuleName(), encryptionType));
|
artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), EmbeddedFileExtractorModuleFactory.getModuleName(), encryptionType));
|
||||||
services.fireModuleDataEvent(new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED));
|
services.fireModuleDataEvent(new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED));
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
logger.log(Level.SEVERE, "Error creating blackboard artifact for encryption detected for file: " + archiveFile, ex); //NON-NLS
|
logger.log(Level.SEVERE, "Error creating blackboard artifact for encryption detected for file: " + archiveFilePath, ex); //NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
String msg = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg");
|
String msg = NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.encrFileDetected.msg");
|
||||||
|
@ -3,10 +3,14 @@ OpenIDE-Module-Display-Category=Ingest Module
|
|||||||
OpenIDE-Module-Long-Description=PhotoRec Carver ingest module. \n\n Carves unallocated space and feeds the resulting carved files back into the system for processing.
|
OpenIDE-Module-Long-Description=PhotoRec Carver ingest module. \n\n Carves unallocated space and feeds the resulting carved files back into the system for processing.
|
||||||
OpenIDE-Module-Short-Description=Carves unallocated space and feeds carved files back into the system for processing.
|
OpenIDE-Module-Short-Description=Carves unallocated space and feeds carved files back into the system for processing.
|
||||||
moduleDisplayName.text=PhotoRec Carver
|
moduleDisplayName.text=PhotoRec Carver
|
||||||
moduleDescription.text=Runs PhotoRec carver against unallocated space on the system.
|
moduleDescription.text=Runs PhotoRec carver against unallocated space in the data source.
|
||||||
|
|
||||||
unallocatedSpaceProcessingSettingsError.message="Process Unallocated Space" is not checked. This module is designed to carve unallocated space. Either allow processing of unallocated space, or do not use this module.
|
unallocatedSpaceProcessingSettingsError.message="Process Unallocated Space" is not checked. The PhotoRec module is designed to carve unallocated space. Either enable processing of unallocated space or disable this module.
|
||||||
unsupportedOS.message=Module is not supported for other than Windows platforms
|
unsupportedOS.message=PhotoRec Module is supported only on Windows platforms
|
||||||
missingExecutable.message=Unable to locate unallocated carver executable.
|
missingExecutable.message=Unable to locate PhotoRec executable.
|
||||||
cannotRunExecutable.message=Unable to execute unallocated carver
|
cannotRunExecutable.message=Unable to execute PhotoRec
|
||||||
cannotCreateOutputDir.message=Unable to create output directory: {0}
|
cannotCreateOutputDir.message=Unable to create output directory: {0}
|
||||||
|
PhotoRecIngestModule.processTerminated=PhotoRec Carver ingest module was terminated due to exceeding max allowable run time when scanning
|
||||||
|
PhotoRecIngestModule.moduleError=PhotoRec Carver Module Error
|
||||||
|
PhotoRecIngestModule.UnableToCarve=Unable to carve file: {0}
|
||||||
|
PhotoRecIngestModule.NotEnoughDiskSpace=Not enough disk space to save unallocated file. Carving will be skipped.
|
@ -26,10 +26,10 @@ import java.nio.file.FileAlreadyExistsException;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -38,12 +38,18 @@ import org.openide.modules.InstalledFileLocator;
|
|||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.coreutils.ExecUtil;
|
import org.sleuthkit.autopsy.coreutils.ExecUtil;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||||
import org.sleuthkit.autopsy.ingest.FileIngestModule;
|
import org.sleuthkit.autopsy.ingest.FileIngestModule;
|
||||||
|
import org.sleuthkit.autopsy.ingest.FileIngestModuleProcessTerminator;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModule;
|
import org.sleuthkit.autopsy.ingest.IngestModule;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
|
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
|
||||||
|
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||||
|
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.Image;
|
import org.sleuthkit.datamodel.Image;
|
||||||
@ -51,10 +57,6 @@ import org.sleuthkit.datamodel.LayoutFile;
|
|||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
import org.sleuthkit.datamodel.Volume;
|
import org.sleuthkit.datamodel.Volume;
|
||||||
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
|
||||||
import org.sleuthkit.autopsy.ingest.FileIngestModuleProcessTerminator;
|
|
||||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A file ingest module that runs the Unallocated Carver executable with unallocated space files as input.
|
* A file ingest module that runs the Unallocated Carver executable with unallocated space files as input.
|
||||||
@ -74,6 +76,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
private IngestJobContext context;
|
private IngestJobContext context;
|
||||||
private Path rootOutputDirPath;
|
private Path rootOutputDirPath;
|
||||||
private File executableFile;
|
private File executableFile;
|
||||||
|
private IngestServices services;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
@ -81,6 +84,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
@Override
|
@Override
|
||||||
public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
|
public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.services = IngestServices.getInstance();
|
||||||
|
|
||||||
// If the global unallocated space processing setting and the module
|
// If the global unallocated space processing setting and the module
|
||||||
// process unallocated space only setting are not in sych, throw an
|
// process unallocated space only setting are not in sych, throw an
|
||||||
@ -143,9 +147,15 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
|
|
||||||
// Check that we have roughly enough disk space left to complete the operation
|
// Check that we have roughly enough disk space left to complete the operation
|
||||||
long freeDiskSpace = IngestServices.getInstance().getFreeDiskSpace();
|
long freeDiskSpace = IngestServices.getInstance().getFreeDiskSpace();
|
||||||
if ((file.getSize() * 2) > freeDiskSpace) {
|
// Some network drives always return -1 for free disk space.
|
||||||
logger.log(Level.SEVERE, "PhotoRec error processing {0} with {1} Not enough space on primary disk to carve unallocated space.", // NON-NLS
|
// In this case, expect enough space and move on.
|
||||||
|
|
||||||
|
if ((freeDiskSpace != -1) && ((file.getSize() * 1.2) > freeDiskSpace)) {
|
||||||
|
logger.log(Level.SEVERE, "PhotoRec error processing {0} with {1} Not enough space on primary disk to save unallocated space.", // NON-NLS
|
||||||
new Object[]{file.getName(), PhotoRecCarverIngestModuleFactory.getModuleName()}); // NON-NLS
|
new Object[]{file.getName(), PhotoRecCarverIngestModuleFactory.getModuleName()}); // NON-NLS
|
||||||
|
MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.UnableToCarve", file.getName()),
|
||||||
|
NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.NotEnoughDiskSpace"));
|
||||||
|
|
||||||
return IngestModule.ProcessResult.ERROR;
|
return IngestModule.ProcessResult.ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,22 +187,12 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
|
|
||||||
if (this.context.fileIngestIsCancelled() == true) {
|
if (this.context.fileIngestIsCancelled() == true) {
|
||||||
// if it was cancelled by the user, result is OK
|
// if it was cancelled by the user, result is OK
|
||||||
// cleanup the output path
|
cleanup(outputDirPath, tempFilePath);
|
||||||
FileUtil.deleteDir(new File(outputDirPath.toString()));
|
|
||||||
if (null != tempFilePath && Files.exists(tempFilePath)) {
|
|
||||||
tempFilePath.toFile().delete();
|
|
||||||
}
|
|
||||||
logger.log(Level.INFO, "PhotoRec cancelled by user"); // NON-NLS
|
logger.log(Level.INFO, "PhotoRec cancelled by user"); // NON-NLS
|
||||||
return IngestModule.ProcessResult.OK;
|
return IngestModule.ProcessResult.OK;
|
||||||
}
|
} else if (0 != exitValue) {
|
||||||
|
|
||||||
else if (0 != exitValue) {
|
|
||||||
// if it failed or was cancelled by timeout, result is ERROR
|
// if it failed or was cancelled by timeout, result is ERROR
|
||||||
// cleanup the output path
|
cleanup(outputDirPath, tempFilePath);
|
||||||
FileUtil.deleteDir(new File(outputDirPath.toString()));
|
|
||||||
if (null != tempFilePath && Files.exists(tempFilePath)) {
|
|
||||||
tempFilePath.toFile().delete();
|
|
||||||
}
|
|
||||||
logger.log(Level.SEVERE, "PhotoRec carver returned error exit value = {0} when scanning {1}", // NON-NLS
|
logger.log(Level.SEVERE, "PhotoRec carver returned error exit value = {0} when scanning {1}", // NON-NLS
|
||||||
new Object[]{exitValue, file.getName()}); // NON-NLS
|
new Object[]{exitValue, file.getName()}); // NON-NLS
|
||||||
return IngestModule.ProcessResult.ERROR;
|
return IngestModule.ProcessResult.ERROR;
|
||||||
@ -217,6 +217,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
List<LayoutFile> theList = parser.parse(newAuditFile, id, file);
|
List<LayoutFile> theList = parser.parse(newAuditFile, id, file);
|
||||||
if (theList != null) { // if there were any results from carving, add the unallocated carving event to the reports list.
|
if (theList != null) { // if there were any results from carving, add the unallocated carving event to the reports list.
|
||||||
context.addFilesToJob(new ArrayList<>(theList));
|
context.addFilesToJob(new ArrayList<>(theList));
|
||||||
|
services.fireModuleContentEvent(new ModuleContentEvent(theList.get(0))); // fire an event to update the tree
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException ex) {
|
catch (IOException ex) {
|
||||||
@ -234,6 +235,15 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void cleanup(Path outputDirPath, Path tempFilePath) {
|
||||||
|
// cleanup the output path
|
||||||
|
FileUtil.deleteDir(new File(outputDirPath.toString()));
|
||||||
|
if (null != tempFilePath && Files.exists(tempFilePath)) {
|
||||||
|
tempFilePath.toFile().delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
|
@ -120,7 +120,7 @@ public final class JythonModuleLoader {
|
|||||||
interpreter.exec("import sys"); //NON-NLS
|
interpreter.exec("import sys"); //NON-NLS
|
||||||
String path = Matcher.quoteReplacement(script.getParent());
|
String path = Matcher.quoteReplacement(script.getParent());
|
||||||
interpreter.exec("sys.path.append('" + path + "')"); //NON-NLS
|
interpreter.exec("sys.path.append('" + path + "')"); //NON-NLS
|
||||||
String moduleName = script.getName().replaceAll(".py", ""); //NON-NLS
|
String moduleName = script.getName().replaceAll("\\.py$", ""); //NON-NLS
|
||||||
|
|
||||||
// reload the module so that the changes made to it can be loaded.
|
// reload the module so that the changes made to it can be loaded.
|
||||||
interpreter.exec("import " + moduleName); //NON-NLS
|
interpreter.exec("import " + moduleName); //NON-NLS
|
||||||
|
@ -38,7 +38,6 @@
|
|||||||
|
|
||||||
<!-- timeline and image analyzer -->
|
<!-- timeline and image analyzer -->
|
||||||
<dependency conf="autopsy_core->*" org="org.controlsfx" name="controlsfx" rev="8.40.9" />
|
<dependency conf="autopsy_core->*" org="org.controlsfx" name="controlsfx" rev="8.40.9" />
|
||||||
<dependency conf="autopsy_core->*" org="org.controlsfx" name="openjfx-dialogs" rev="1.0.3"/>
|
|
||||||
|
|
||||||
<!-- timeline and -->
|
<!-- timeline and -->
|
||||||
<dependency conf="autopsy_core->*" org="joda-time" name="joda-time" rev="2.4" />
|
<dependency conf="autopsy_core->*" org="joda-time" name="joda-time" rev="2.4" />
|
||||||
|
@ -64,7 +64,6 @@
|
|||||||
<package>com.sun.mail.smtp</package>
|
<package>com.sun.mail.smtp</package>
|
||||||
<package>com.sun.mail.util</package>
|
<package>com.sun.mail.util</package>
|
||||||
<package>com.sun.mail.util.logging</package>
|
<package>com.sun.mail.util.logging</package>
|
||||||
<package>javafx.scene.control</package>
|
|
||||||
<package>javassist</package>
|
<package>javassist</package>
|
||||||
<package>javassist.bytecode</package>
|
<package>javassist.bytecode</package>
|
||||||
<package>javassist.bytecode.analysis</package>
|
<package>javassist.bytecode.analysis</package>
|
||||||
@ -616,10 +615,6 @@
|
|||||||
<runtime-relative-path>ext/logkit-1.0.1.jar</runtime-relative-path>
|
<runtime-relative-path>ext/logkit-1.0.1.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/logkit-1.0.1.jar</binary-origin>
|
<binary-origin>release/modules/ext/logkit-1.0.1.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
|
||||||
<runtime-relative-path>ext/openjfx-dialogs-1.0.3.jar</runtime-relative-path>
|
|
||||||
<binary-origin>release/modules/ext/openjfx-dialogs-1.0.3.jar</binary-origin>
|
|
||||||
</class-path-extension>
|
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/imgscalr-lib-4.2-javadoc.jar</runtime-relative-path>
|
<runtime-relative-path>ext/imgscalr-lib-4.2-javadoc.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/imgscalr-lib-4.2-javadoc.jar</binary-origin>
|
<binary-origin>release/modules/ext/imgscalr-lib-4.2-javadoc.jar</binary-origin>
|
||||||
@ -768,14 +763,14 @@
|
|||||||
<runtime-relative-path>ext/slf4j-simple-1.6.1.jar</runtime-relative-path>
|
<runtime-relative-path>ext/slf4j-simple-1.6.1.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/slf4j-simple-1.6.1.jar</binary-origin>
|
<binary-origin>release/modules/ext/slf4j-simple-1.6.1.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
|
||||||
<runtime-relative-path>ext/controlsfx-8.40.9.jar</runtime-relative-path>
|
|
||||||
<binary-origin>release/modules/ext/controlsfx-8.40.9.jar</binary-origin>
|
|
||||||
</class-path-extension>
|
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/commons-lang3-3.0-javadoc.jar</runtime-relative-path>
|
<runtime-relative-path>ext/commons-lang3-3.0-javadoc.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/commons-lang3-3.0-javadoc.jar</binary-origin>
|
<binary-origin>release/modules/ext/commons-lang3-3.0-javadoc.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
|
<class-path-extension>
|
||||||
|
<runtime-relative-path>ext/controlsfx-8.40.9.jar</runtime-relative-path>
|
||||||
|
<binary-origin>release/modules/ext/controlsfx-8.40.9.jar</binary-origin>
|
||||||
|
</class-path-extension>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/platform-3.4.0.jar</runtime-relative-path>
|
<runtime-relative-path>ext/platform-3.4.0.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/platform-3.4.0.jar</binary-origin>
|
<binary-origin>release/modules/ext/platform-3.4.0.jar</binary-origin>
|
||||||
|
@ -7,7 +7,7 @@ nbplatform.active.dir=${suite.dir}/netbeans-plat/${netbeans-plat-version}
|
|||||||
harness.dir=${nbplatform.active.dir}/harness
|
harness.dir=${nbplatform.active.dir}/harness
|
||||||
bootstrap.url=http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastStableBuild/artifact/nbbuild/netbeans/harness/tasks.jar
|
bootstrap.url=http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastStableBuild/artifact/nbbuild/netbeans/harness/tasks.jar
|
||||||
# Where we get the platform from. To see what versions are available, open URL in browser up to the .../updates part of the URL
|
# Where we get the platform from. To see what versions are available, open URL in browser up to the .../updates part of the URL
|
||||||
autoupdate.catalog.url=http://dlc.sun.com.edgesuite.net/netbeans/updates/${netbeans-plat-version}/uc/final/distribution/catalog.xml.gz
|
autoupdate.catalog.url=http://updates.netbeans.org/netbeans/updates/${netbeans-plat-version}/uc/final/distribution/catalog.xml.gz
|
||||||
cluster.path=\
|
cluster.path=\
|
||||||
${nbplatform.active.dir}/harness:\
|
${nbplatform.active.dir}/harness:\
|
||||||
${nbplatform.active.dir}/java:\
|
${nbplatform.active.dir}/java:\
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2013 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.imagegallery;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.EventListener;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
|
||||||
|
|
||||||
/** represents a change in the database for one or more files. */
|
|
||||||
@Immutable
|
|
||||||
public class FileUpdateEvent {
|
|
||||||
|
|
||||||
/** the obj_ids of affected files */
|
|
||||||
private final Set<Long> fileIDs;
|
|
||||||
|
|
||||||
/** the attribute that was modified */
|
|
||||||
private final DrawableAttribute<?> changedAttribute;
|
|
||||||
|
|
||||||
/** the type of update ( updated/removed) */
|
|
||||||
private final UpdateType updateType;
|
|
||||||
|
|
||||||
public UpdateType getUpdateType() {
|
|
||||||
return updateType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<Long> getFileIDs() {
|
|
||||||
return fileIDs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DrawableAttribute<?> getChangedAttribute() {
|
|
||||||
return changedAttribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FileUpdateEvent newRemovedEvent(Collection<? extends Long> updatedFiles) {
|
|
||||||
return new FileUpdateEvent(updatedFiles, UpdateType.REMOVE, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param updatedFiles the files that have been added or changed in the
|
|
||||||
* database
|
|
||||||
* @param changedAttribute the attribute that was changed for the files, or
|
|
||||||
* null if this represents new files
|
|
||||||
*
|
|
||||||
* @return a new FileUpdateEvent
|
|
||||||
*/
|
|
||||||
public static FileUpdateEvent newUpdateEvent(Collection<? extends Long> updatedFiles, DrawableAttribute<?> changedAttribute) {
|
|
||||||
return new FileUpdateEvent(updatedFiles, UpdateType.UPDATE, changedAttribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FileUpdateEvent(Collection<? extends Long> updatedFiles, UpdateType updateType, DrawableAttribute<?> changedAttribute) {
|
|
||||||
this.fileIDs = new HashSet<>(updatedFiles);
|
|
||||||
this.updateType = updateType;
|
|
||||||
this.changedAttribute = changedAttribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
static public enum UpdateType {
|
|
||||||
|
|
||||||
/** files have been added or updated in the db */
|
|
||||||
UPDATE,
|
|
||||||
/** files have been removed
|
|
||||||
* from the db */
|
|
||||||
REMOVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Interface for listening to FileUpdateEvents */
|
|
||||||
public static interface FileUpdateListener extends EventListener {
|
|
||||||
|
|
||||||
public void handleFileUpdate(FileUpdateEvent evt);
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.imagegallery;
|
|||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@ -56,15 +57,16 @@ import org.sleuthkit.autopsy.casemodule.Case;
|
|||||||
import org.sleuthkit.autopsy.coreutils.History;
|
import org.sleuthkit.autopsy.coreutils.History;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
import org.sleuthkit.autopsy.events.ContentTagAddedEvent;
|
||||||
|
import org.sleuthkit.autopsy.events.ContentTagDeletedEvent;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableTagsManager;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.HashSetManager;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.HashSetManager;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupManager;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupManager;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupViewState;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
|
||||||
import org.sleuthkit.autopsy.imagegallery.gui.NoGroupsDialog;
|
import org.sleuthkit.autopsy.imagegallery.gui.NoGroupsDialog;
|
||||||
import org.sleuthkit.autopsy.imagegallery.gui.SummaryTablePane;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
|
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
@ -126,13 +128,15 @@ public final class ImageGalleryController {
|
|||||||
|
|
||||||
private final GroupManager groupManager = new GroupManager(this);
|
private final GroupManager groupManager = new GroupManager(this);
|
||||||
private final HashSetManager hashSetManager = new HashSetManager();
|
private final HashSetManager hashSetManager = new HashSetManager();
|
||||||
private final CategoryManager categoryManager = new CategoryManager();
|
private final CategoryManager categoryManager = new CategoryManager(this);
|
||||||
|
private final DrawableTagsManager tagsManager = new DrawableTagsManager(null);
|
||||||
|
|
||||||
private StackPane fullUIStackPane;
|
private StackPane fullUIStackPane;
|
||||||
|
|
||||||
private StackPane centralStackPane;
|
private StackPane centralStackPane;
|
||||||
|
|
||||||
private Node infoOverlay;
|
private Node infoOverlay;
|
||||||
|
private SleuthkitCase sleuthKitCase;
|
||||||
|
|
||||||
public ReadOnlyBooleanProperty getMetaDataCollapsed() {
|
public ReadOnlyBooleanProperty getMetaDataCollapsed() {
|
||||||
return metaDataCollapsed.getReadOnlyProperty();
|
return metaDataCollapsed.getReadOnlyProperty();
|
||||||
@ -231,7 +235,6 @@ public final class ImageGalleryController {
|
|||||||
IngestManager.getInstance().addIngestJobEventListener((PropertyChangeEvent evt) -> {
|
IngestManager.getInstance().addIngestJobEventListener((PropertyChangeEvent evt) -> {
|
||||||
Platform.runLater(this::updateRegroupDisabled);
|
Platform.runLater(this::updateRegroupDisabled);
|
||||||
});
|
});
|
||||||
// metaDataCollapsed.bind(Toolbar.getDefault().showMetaDataProperty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyBooleanProperty getCanAdvance() {
|
public ReadOnlyBooleanProperty getCanAdvance() {
|
||||||
@ -333,7 +336,7 @@ public final class ImageGalleryController {
|
|||||||
Platform.runLater(this::updateRegroupDisabled);
|
Platform.runLater(this::updateRegroupDisabled);
|
||||||
});
|
});
|
||||||
|
|
||||||
Thread th = new Thread(dbWorkerThread);
|
Thread th = new Thread(dbWorkerThread, "DB-Worker-Thread");
|
||||||
th.setDaemon(false); // we want it to go away when it is done
|
th.setDaemon(false); // we want it to go away when it is done
|
||||||
th.start();
|
th.start();
|
||||||
}
|
}
|
||||||
@ -344,7 +347,9 @@ public final class ImageGalleryController {
|
|||||||
* @param theNewCase the case to configure the controller for
|
* @param theNewCase the case to configure the controller for
|
||||||
*/
|
*/
|
||||||
public synchronized void setCase(Case theNewCase) {
|
public synchronized void setCase(Case theNewCase) {
|
||||||
this.db = DrawableDB.getDrawableDB(ImageGalleryModule.getModuleOutputDir(theNewCase), getSleuthKitCase());
|
if (Objects.nonNull(theNewCase)) {
|
||||||
|
this.sleuthKitCase = theNewCase.getSleuthkitCase();
|
||||||
|
this.db = DrawableDB.getDrawableDB(ImageGalleryModule.getModuleOutputDir(theNewCase), this);
|
||||||
|
|
||||||
setListeningEnabled(ImageGalleryModule.isEnabledforCase(theNewCase));
|
setListeningEnabled(ImageGalleryModule.isEnabledforCase(theNewCase));
|
||||||
setStale(ImageGalleryModule.isDrawableDBStale(theNewCase));
|
setStale(ImageGalleryModule.isDrawableDBStale(theNewCase));
|
||||||
@ -356,7 +361,13 @@ public final class ImageGalleryController {
|
|||||||
groupManager.setDB(db);
|
groupManager.setDB(db);
|
||||||
hashSetManager.setDb(db);
|
hashSetManager.setDb(db);
|
||||||
categoryManager.setDb(db);
|
categoryManager.setDb(db);
|
||||||
SummaryTablePane.getDefault().refresh();
|
tagsManager.setAutopsyTagsManager(theNewCase.getServices().getTagsManager());
|
||||||
|
tagsManager.registerListener(groupManager);
|
||||||
|
tagsManager.registerListener(categoryManager);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -370,9 +381,11 @@ public final class ImageGalleryController {
|
|||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
historyManager.clear();
|
historyManager.clear();
|
||||||
});
|
});
|
||||||
Category.clearTagNames();
|
tagsManager.clearFollowUpTagName();
|
||||||
|
tagsManager.unregisterListener(groupManager);
|
||||||
|
tagsManager.unregisterListener(categoryManager);
|
||||||
|
|
||||||
Toolbar.getDefault().reset();
|
Toolbar.getDefault(this).reset();
|
||||||
groupManager.clear();
|
groupManager.clear();
|
||||||
if (db != null) {
|
if (db != null) {
|
||||||
db.closeDBCon();
|
db.closeDBCon();
|
||||||
@ -476,6 +489,19 @@ public final class ImageGalleryController {
|
|||||||
setStale(true);
|
setStale(true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case CONTENT_TAG_ADDED:
|
||||||
|
final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) evt;
|
||||||
|
if (getDatabase().isInDB((tagAddedEvent).getTag().getContent().getId())) {
|
||||||
|
getTagsManager().fireTagAddedEvent(tagAddedEvent);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CONTENT_TAG_DELETED:
|
||||||
|
final ContentTagDeletedEvent tagDeletedEvent = (ContentTagDeletedEvent) evt;
|
||||||
|
if (getDatabase().isInDB((tagDeletedEvent).getTag().getContent().getId())) {
|
||||||
|
getTagsManager().fireTagDeletedEvent(tagDeletedEvent);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -488,6 +514,12 @@ public final class ImageGalleryController {
|
|||||||
return categoryManager;
|
return categoryManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DrawableTagsManager getTagsManager() {
|
||||||
|
return tagsManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// @@@ REVIEW IF THIS SHOLD BE STATIC...
|
// @@@ REVIEW IF THIS SHOLD BE STATIC...
|
||||||
//TODO: concept seems like the controller deal with how much work to do at a given time
|
//TODO: concept seems like the controller deal with how much work to do at a given time
|
||||||
// @@@ review this class for synchronization issues (i.e. reset and cancel being called, add, etc.)
|
// @@@ review this class for synchronization issues (i.e. reset and cancel being called, add, etc.)
|
||||||
@ -551,12 +583,8 @@ public final class ImageGalleryController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SleuthkitCase getSleuthKitCase() throws IllegalStateException {
|
public synchronized SleuthkitCase getSleuthKitCase() {
|
||||||
if (Case.isCaseOpen()) {
|
return sleuthKitCase;
|
||||||
return Case.getCurrentCase().getSleuthkitCase();
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("No Case is open!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,10 +34,9 @@ import org.openide.util.NbBundle.Messages;
|
|||||||
import org.openide.windows.Mode;
|
import org.openide.windows.Mode;
|
||||||
import org.openide.windows.TopComponent;
|
import org.openide.windows.TopComponent;
|
||||||
import org.openide.windows.WindowManager;
|
import org.openide.windows.WindowManager;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.imagegallery.gui.GroupPane;
|
import org.sleuthkit.autopsy.imagegallery.gui.drawableviews.GroupPane;
|
||||||
import org.sleuthkit.autopsy.imagegallery.gui.MetaDataPane;
|
import org.sleuthkit.autopsy.imagegallery.gui.drawableviews.MetaDataPane;
|
||||||
import org.sleuthkit.autopsy.imagegallery.gui.StatusBar;
|
import org.sleuthkit.autopsy.imagegallery.gui.StatusBar;
|
||||||
import org.sleuthkit.autopsy.imagegallery.gui.SummaryTablePane;
|
import org.sleuthkit.autopsy.imagegallery.gui.SummaryTablePane;
|
||||||
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
|
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
|
||||||
@ -145,13 +144,13 @@ public final class ImageGalleryTopComponent extends TopComponent implements Expl
|
|||||||
fullUIStack.getChildren().add(borderPane);
|
fullUIStack.getChildren().add(borderPane);
|
||||||
splitPane = new SplitPane();
|
splitPane = new SplitPane();
|
||||||
borderPane.setCenter(splitPane);
|
borderPane.setCenter(splitPane);
|
||||||
borderPane.setTop(Toolbar.getDefault());
|
borderPane.setTop(Toolbar.getDefault(controller));
|
||||||
borderPane.setBottom(new StatusBar(controller));
|
borderPane.setBottom(new StatusBar(controller));
|
||||||
|
|
||||||
metaDataTable = new MetaDataPane(controller);
|
metaDataTable = new MetaDataPane(controller);
|
||||||
|
|
||||||
navPanel = new NavPanel(controller);
|
navPanel = new NavPanel(controller);
|
||||||
leftPane = new VBox(navPanel, SummaryTablePane.getDefault());
|
leftPane = new VBox(navPanel, new SummaryTablePane(controller));
|
||||||
SplitPane.setResizableWithParent(leftPane, Boolean.FALSE);
|
SplitPane.setResizableWithParent(leftPane, Boolean.FALSE);
|
||||||
SplitPane.setResizableWithParent(groupPane, Boolean.TRUE);
|
SplitPane.setResizableWithParent(groupPane, Boolean.TRUE);
|
||||||
SplitPane.setResizableWithParent(metaDataTable, Boolean.FALSE);
|
SplitPane.setResizableWithParent(metaDataTable, Boolean.FALSE);
|
||||||
|
@ -1,137 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2013 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.imagegallery;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.scene.control.MenuItem;
|
|
||||||
import javafx.scene.control.SplitMenuButton;
|
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.actions.AddDrawableTagAction;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
|
||||||
import org.sleuthkit.datamodel.TagName;
|
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains static methods for dealing with Tags in ImageGallery
|
|
||||||
*/
|
|
||||||
public class TagUtils {
|
|
||||||
|
|
||||||
private static final String follow_Up = "Follow Up";
|
|
||||||
|
|
||||||
private static TagName followUpTagName;
|
|
||||||
|
|
||||||
private final static List<TagListener> listeners = new ArrayList<>();
|
|
||||||
|
|
||||||
synchronized public static TagName getFollowUpTagName() throws TskCoreException {
|
|
||||||
if (followUpTagName == null) {
|
|
||||||
followUpTagName = getTagName(follow_Up);
|
|
||||||
}
|
|
||||||
return followUpTagName;
|
|
||||||
}
|
|
||||||
|
|
||||||
static public Collection<TagName> getNonCategoryTagNames() {
|
|
||||||
List<TagName> nonCatTagNames = new ArrayList<>();
|
|
||||||
List<TagName> allTagNames;
|
|
||||||
try {
|
|
||||||
allTagNames = Case.getCurrentCase().getServices().getTagsManager().getAllTagNames();
|
|
||||||
for (TagName tn : allTagNames) {
|
|
||||||
if (tn.getDisplayName().startsWith(Category.CATEGORY_PREFIX) == false) {
|
|
||||||
nonCatTagNames.add(tn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (TskCoreException | IllegalStateException ex) {
|
|
||||||
Logger.getLogger(TagUtils.class.getName()).log(Level.WARNING, "couldn't access case", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nonCatTagNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized static public TagName getTagName(String displayName) throws TskCoreException {
|
|
||||||
try {
|
|
||||||
final TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager();
|
|
||||||
|
|
||||||
for (TagName tn : tagsManager.getAllTagNames()) {
|
|
||||||
if (displayName.equals(tn.getDisplayName())) {
|
|
||||||
return tn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return tagsManager.addTagName(displayName);
|
|
||||||
} catch (TagsManager.TagNameAlreadyExistsException ex) {
|
|
||||||
throw new TskCoreException("tagame exists but wasn't found", ex);
|
|
||||||
}
|
|
||||||
} catch (IllegalStateException ex) {
|
|
||||||
Logger.getLogger(TagUtils.class.getName()).log(Level.SEVERE, "Case was closed out from underneath", ex);
|
|
||||||
throw new TskCoreException("Case was closed out from underneath", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void fireChange(Collection<Long> ids) {
|
|
||||||
Set<TagUtils.TagListener> listenersCopy = new HashSet<TagUtils.TagListener>(listeners);
|
|
||||||
synchronized (listeners) {
|
|
||||||
listenersCopy.addAll(listeners);
|
|
||||||
}
|
|
||||||
for (TagListener list : listenersCopy) {
|
|
||||||
list.handleTagsChanged(ids);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void registerListener(TagListener aThis) {
|
|
||||||
synchronized (listeners) {
|
|
||||||
listeners.add(aThis);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void unregisterListener(TagListener aThis) {
|
|
||||||
synchronized (listeners) {
|
|
||||||
listeners.remove(aThis);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param tn the value of tn
|
|
||||||
*/
|
|
||||||
static public MenuItem createSelTagMenuItem(final TagName tn, final SplitMenuButton tagSelectedMenuButton) {
|
|
||||||
final MenuItem menuItem = new MenuItem(tn.getDisplayName(), new ImageView(DrawableAttribute.TAGS.getIcon()));
|
|
||||||
menuItem.setOnAction(new EventHandler<ActionEvent>() {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent t) {
|
|
||||||
AddDrawableTagAction.getInstance().addTag(tn, "");
|
|
||||||
tagSelectedMenuButton.setText(tn.getDisplayName());
|
|
||||||
tagSelectedMenuButton.setOnAction(this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return menuItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static interface TagListener {
|
|
||||||
public void handleTagsChanged(Collection<Long> ids);
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,23 +18,25 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.imagegallery.actions;
|
package org.sleuthkit.autopsy.imagegallery.actions;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
import javafx.scene.control.Menu;
|
import javafx.scene.control.Menu;
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import org.openide.util.Utilities;
|
import org.openide.util.Utilities;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileUpdateEvent;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableTagsManager;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
@ -48,23 +50,14 @@ public class AddDrawableTagAction extends AddTagAction {
|
|||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(AddDrawableTagAction.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(AddDrawableTagAction.class.getName());
|
||||||
|
|
||||||
// This class is a singleton to support multi-selection of nodes, since
|
private final ImageGalleryController controller;
|
||||||
// org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every
|
|
||||||
// node in the array returns a reference to the same action object from Node.getActions(boolean).
|
|
||||||
private static AddDrawableTagAction instance;
|
|
||||||
|
|
||||||
public static synchronized AddDrawableTagAction getInstance() {
|
public AddDrawableTagAction(ImageGalleryController controller) {
|
||||||
if (null == instance) {
|
this.controller = controller;
|
||||||
instance = new AddDrawableTagAction();
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AddDrawableTagAction() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Menu getPopupMenu() {
|
public Menu getPopupMenu() {
|
||||||
return new TagMenu();
|
return new TagMenu(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -79,29 +72,41 @@ public class AddDrawableTagAction extends AddTagAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTagsToFiles(TagName tagName, String comment, Set<Long> selectedFiles){
|
public void addTagsToFiles(TagName tagName, String comment, Set<Long> selectedFiles) {
|
||||||
new SwingWorker<Void, Void>() {
|
new SwingWorker<Void, Void>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground() throws Exception {
|
protected Void doInBackground() throws Exception {
|
||||||
for (Long fileID : selectedFiles) {
|
for (Long fileID : selectedFiles) {
|
||||||
try {
|
try {
|
||||||
DrawableFile<?> file = ImageGalleryController.getDefault().getFileFromId(fileID);
|
final DrawableFile<?> file = controller.getFileFromId(fileID);
|
||||||
LOGGER.log(Level.INFO, "tagging {0} with {1} and comment {2}", new Object[]{file.getName(), tagName.getDisplayName(), comment});
|
LOGGER.log(Level.INFO, "tagging {0} with {1} and comment {2}", new Object[]{file.getName(), tagName.getDisplayName(), comment});
|
||||||
Case.getCurrentCase().getServices().getTagsManager().addContentTag(file, tagName, comment);
|
|
||||||
} catch (IllegalStateException ex) {
|
// check if the same tag is being added for the same abstract file.
|
||||||
LOGGER.log(Level.SEVERE, "Case was closed out from underneath Updatefile task", ex);
|
DrawableTagsManager tagsManager = controller.getTagsManager();
|
||||||
} catch (TskCoreException ex) {
|
List<ContentTag> contentTags = tagsManager.getContentTagsByContent(file);
|
||||||
LOGGER.log(Level.SEVERE, "Error tagging result", ex);
|
Optional<TagName> duplicateTagName = contentTags.stream()
|
||||||
JOptionPane.showMessageDialog(null, "Unable to tag " + fileID + ".", "Tagging Error", JOptionPane.ERROR_MESSAGE);
|
.map(ContentTag::getName)
|
||||||
|
.filter(tagName::equals)
|
||||||
|
.findAny();
|
||||||
|
|
||||||
|
if (duplicateTagName.isPresent()) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.WARNING, "Unable to tag " + file.getName() + ". It has already been tagged as \"" + tagName.getDisplayName() + ". Cannot reapply the same tag.", ButtonType.OK);
|
||||||
|
alert.setHeaderText("Tag Error");
|
||||||
|
alert.show();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
controller.getTagsManager().addContentTag(file, tagName, comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
//make sure rest of ui hears category change.
|
} catch (TskCoreException tskCoreException) {
|
||||||
ImageGalleryController.getDefault().getGroupManager().handleFileUpdate(FileUpdateEvent.newUpdateEvent(Collections.singleton(fileID), DrawableAttribute.TAGS));
|
LOGGER.log(Level.SEVERE, "Error tagging result", tskCoreException);
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
new Alert(Alert.AlertType.ERROR, "Unable to file " + fileID + ".").show();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshDirectoryTree();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +119,6 @@ public class AddDrawableTagAction extends AddTagAction {
|
|||||||
LOGGER.log(Level.SEVERE, "unexpected exception while tagging files", ex);
|
LOGGER.log(Level.SEVERE, "unexpected exception while tagging files", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,24 +18,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.imagegallery.actions;
|
package org.sleuthkit.autopsy.imagegallery.actions;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.Menu;
|
import javafx.scene.control.Menu;
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog;
|
import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog;
|
||||||
import org.sleuthkit.autopsy.actions.GetTagNameDialog;
|
import org.sleuthkit.autopsy.actions.GetTagNameDialog;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
|
||||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract base class for actions that allow users to tag SleuthKit data
|
* An abstract base class for actions that allow users to tag SleuthKit data
|
||||||
@ -43,20 +36,10 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
*
|
*
|
||||||
* //TODO: this class started as a cut and paste from
|
* //TODO: this class started as a cut and paste from
|
||||||
* org.sleuthkit.autopsy.actions.AddTagAction and needs to be
|
* org.sleuthkit.autopsy.actions.AddTagAction and needs to be
|
||||||
* refactor or reintegrated to the AddTagAction hierarchy of Autopysy.
|
* refactored or reintegrated to the AddTagAction hierarchy of Autopysy.
|
||||||
*/
|
*/
|
||||||
abstract class AddTagAction {
|
abstract class AddTagAction {
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
protected void refreshDirectoryTree() {
|
|
||||||
|
|
||||||
/* Note: this is a hack. In an ideal world, TagsManager would fire
|
|
||||||
* events so that the directory tree would refresh. But, we haven't
|
|
||||||
* had a chance to add that so, we fire these events and the tree
|
|
||||||
* refreshes based on them.
|
|
||||||
*/
|
|
||||||
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent("TagAction", BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE)); //NON-NLS
|
|
||||||
}
|
|
||||||
protected static final String NO_COMMENT = "";
|
protected static final String NO_COMMENT = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,72 +69,59 @@ abstract class AddTagAction {
|
|||||||
// to be reworked.
|
// to be reworked.
|
||||||
protected class TagMenu extends Menu {
|
protected class TagMenu extends Menu {
|
||||||
|
|
||||||
TagMenu() {
|
TagMenu(ImageGalleryController controller) {
|
||||||
super(getActionDisplayName());
|
super(getActionDisplayName());
|
||||||
|
|
||||||
// Get the current set of tag names.
|
|
||||||
TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager();
|
|
||||||
List<TagName> tagNames = null;
|
|
||||||
try {
|
|
||||||
tagNames = tagsManager.getAllTagNames();
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
Logger.getLogger(TagsManager.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a "Quick Tag" sub-menu.
|
// Create a "Quick Tag" sub-menu.
|
||||||
Menu quickTagMenu = new Menu("Quick Tag");
|
Menu quickTagMenu = new Menu("Quick Tag");
|
||||||
getItems().add(quickTagMenu);
|
getItems().add(quickTagMenu);
|
||||||
|
|
||||||
// Each tag name in the current set of tags gets its own menu item in
|
/* Each non-Category tag name in the current set of tags gets its
|
||||||
// the "Quick Tags" sub-menu. Selecting one of these menu items adds
|
* own menu item in the "Quick Tags" sub-menu. Selecting one of
|
||||||
// a tag with the associated tag name.
|
* these menu items adds a tag with the associated tag name. */
|
||||||
if (null != tagNames && !tagNames.isEmpty()) {
|
Collection<TagName> tagNames = controller.getTagsManager().getNonCategoryTagNames();
|
||||||
|
if (tagNames.isEmpty()) {
|
||||||
|
MenuItem empty = new MenuItem("No tags");
|
||||||
|
empty.setDisable(true);
|
||||||
|
quickTagMenu.getItems().add(empty);
|
||||||
|
} else {
|
||||||
for (final TagName tagName : tagNames) {
|
for (final TagName tagName : tagNames) {
|
||||||
if (tagName.getDisplayName().startsWith(Category.CATEGORY_PREFIX) == false) {
|
|
||||||
MenuItem tagNameItem = new MenuItem(tagName.getDisplayName());
|
MenuItem tagNameItem = new MenuItem(tagName.getDisplayName());
|
||||||
tagNameItem.setOnAction((ActionEvent t) -> {
|
tagNameItem.setOnAction((ActionEvent t) -> {
|
||||||
addTag(tagName, NO_COMMENT);
|
addTag(tagName, NO_COMMENT);
|
||||||
refreshDirectoryTree();
|
|
||||||
});
|
});
|
||||||
quickTagMenu.getItems().add(tagNameItem);
|
quickTagMenu.getItems().add(tagNameItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
MenuItem empty = new MenuItem("No tags");
|
|
||||||
empty.setDisable(true);
|
|
||||||
quickTagMenu.getItems().add(empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// quickTagMenu.addSeparator();
|
/* The "Quick Tag" menu also gets an "New Tag..." menu item.
|
||||||
// The "Quick Tag" menu also gets an "Choose Tag..." menu item.
|
* Selecting this item initiates a dialog that can be used to create
|
||||||
// Selecting this item initiates a dialog that can be used to create
|
* or select a tag name and adds a tag with the resulting name. */
|
||||||
// or select a tag name and adds a tag with the resulting name.
|
|
||||||
MenuItem newTagMenuItem = new MenuItem("New Tag...");
|
MenuItem newTagMenuItem = new MenuItem("New Tag...");
|
||||||
newTagMenuItem.setOnAction((ActionEvent t) -> {
|
newTagMenuItem.setOnAction((ActionEvent t) -> {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
TagName tagName = GetTagNameDialog.doDialog();
|
TagName tagName = GetTagNameDialog.doDialog();
|
||||||
if (tagName != null) {
|
if (tagName != null) {
|
||||||
addTag(tagName, NO_COMMENT);
|
addTag(tagName, NO_COMMENT);
|
||||||
refreshDirectoryTree();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
quickTagMenu.getItems().add(newTagMenuItem);
|
quickTagMenu.getItems().add(newTagMenuItem);
|
||||||
|
|
||||||
// Create a "Choose Tag and Comment..." menu item. Selecting this item initiates
|
/* Create a "Tag and Comment..." menu item. Selecting this item
|
||||||
// a dialog that can be used to create or select a tag name with an
|
* initiates a dialog that can be used to create or select a tag
|
||||||
// optional comment and adds a tag with the resulting name.
|
* name with an optional comment and adds a tag with the resulting
|
||||||
|
* name. */
|
||||||
MenuItem tagAndCommentItem = new MenuItem("Tag and Comment...");
|
MenuItem tagAndCommentItem = new MenuItem("Tag and Comment...");
|
||||||
tagAndCommentItem.setOnAction((ActionEvent t) -> {
|
tagAndCommentItem.setOnAction((ActionEvent t) -> {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
GetTagNameAndCommentDialog.TagNameAndComment tagNameAndComment = GetTagNameAndCommentDialog.doDialog();
|
GetTagNameAndCommentDialog.TagNameAndComment tagNameAndComment = GetTagNameAndCommentDialog.doDialog();
|
||||||
if (null != tagNameAndComment) {
|
if (null != tagNameAndComment) {
|
||||||
if (tagNameAndComment.getTagName().getDisplayName().startsWith(Category.CATEGORY_PREFIX)) {
|
if (CategoryManager.isCategoryTagName(tagNameAndComment.getTagName())) {
|
||||||
new CategorizeAction().addTag(tagNameAndComment.getTagName(), tagNameAndComment.getComment());
|
new CategorizeAction(controller).addTag(tagNameAndComment.getTagName(), tagNameAndComment.getComment());
|
||||||
} else {
|
} else {
|
||||||
AddDrawableTagAction.getInstance().addTag(tagNameAndComment.getTagName(), tagNameAndComment.getComment());
|
new AddDrawableTagAction(controller).addTag(tagNameAndComment.getTagName(), tagNameAndComment.getComment());
|
||||||
}
|
}
|
||||||
refreshDirectoryTree();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -18,27 +18,26 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.imagegallery.actions;
|
package org.sleuthkit.autopsy.imagegallery.actions;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
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 java.util.stream.Collectors;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.Menu;
|
import javafx.scene.control.Menu;
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
import javafx.scene.input.KeyCodeCombination;
|
import javafx.scene.input.KeyCodeCombination;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileUpdateEvent;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupKey;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableTagsManager;
|
||||||
import org.sleuthkit.datamodel.ContentTag;
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
|
import org.sleuthkit.datamodel.Tag;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
@ -54,13 +53,13 @@ public class CategorizeAction extends AddTagAction {
|
|||||||
|
|
||||||
private final ImageGalleryController controller;
|
private final ImageGalleryController controller;
|
||||||
|
|
||||||
public CategorizeAction() {
|
public CategorizeAction(ImageGalleryController controller) {
|
||||||
super();
|
super();
|
||||||
this.controller = ImageGalleryController.getDefault();
|
this.controller = controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public Menu getPopupMenu() {
|
public Menu getPopupMenu() {
|
||||||
return new CategoryMenu();
|
return new CategoryMenu(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -84,13 +83,17 @@ public class CategorizeAction extends AddTagAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void enforceOneCat(TagName name, String string) {
|
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instances of this class implement a context menu user interface for
|
* Instances of this class implement a context menu user interface for
|
||||||
* selecting a category
|
* selecting a category
|
||||||
*/
|
*/
|
||||||
static private class CategoryMenu extends Menu {
|
static private class CategoryMenu extends Menu {
|
||||||
|
|
||||||
CategoryMenu() {
|
CategoryMenu(ImageGalleryController controller) {
|
||||||
super("Categorize");
|
super("Categorize");
|
||||||
|
|
||||||
// Each category get an item in the sub-menu. Selecting one of these menu items adds
|
// Each category get an item in the sub-menu. Selecting one of these menu items adds
|
||||||
@ -99,8 +102,8 @@ public class CategorizeAction extends AddTagAction {
|
|||||||
|
|
||||||
MenuItem categoryItem = new MenuItem(cat.getDisplayName());
|
MenuItem categoryItem = new MenuItem(cat.getDisplayName());
|
||||||
categoryItem.setOnAction((ActionEvent t) -> {
|
categoryItem.setOnAction((ActionEvent t) -> {
|
||||||
final CategorizeAction categorizeAction = new CategorizeAction();
|
final CategorizeAction categorizeAction = new CategorizeAction(controller);
|
||||||
categorizeAction.addTag(cat.getTagName(), NO_COMMENT);
|
categorizeAction.addTag(controller.getCategoryManager().getTagName(cat), NO_COMMENT);
|
||||||
});
|
});
|
||||||
categoryItem.setAccelerator(new KeyCodeCombination(KeyCode.getKeyCode(Integer.toString(cat.getCategoryNumber()))));
|
categoryItem.setAccelerator(new KeyCodeCombination(KeyCode.getKeyCode(Integer.toString(cat.getCategoryNumber()))));
|
||||||
getItems().add(categoryItem);
|
getItems().add(categoryItem);
|
||||||
@ -123,36 +126,40 @@ public class CategorizeAction extends AddTagAction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
final CategoryManager categoryManager = controller.getCategoryManager();
|
||||||
|
final DrawableTagsManager tagsManager = controller.getTagsManager();
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DrawableFile<?> file = controller.getFileFromId(fileID); //drawable db
|
DrawableFile<?> file = controller.getFileFromId(fileID); //drawable db
|
||||||
Category oldCat = file.getCategory();
|
final List<ContentTag> fileTags = tagsManager.getContentTagsByContent(file);
|
||||||
// remove file from old category group
|
if (tagName == categoryManager.getTagName(Category.ZERO)) {
|
||||||
controller.getGroupManager().removeFromGroup(new GroupKey<Category>(DrawableAttribute.CATEGORY, oldCat), fileID); //memory
|
// delete all cat tags for cat-0
|
||||||
|
fileTags.stream()
|
||||||
//remove old category tag if necessary
|
.filter(tag -> CategoryManager.isCategoryTagName(tag.getName()))
|
||||||
List<ContentTag> allContentTags = Case.getCurrentCase().getServices().getTagsManager().getContentTagsByContent(file); //tsk db
|
.forEach((ct) -> {
|
||||||
for (ContentTag ct : allContentTags) {
|
try {
|
||||||
//this is bad: treating tags as categories as long as their names start with prefix
|
tagsManager.deleteContentTag(ct);
|
||||||
//TODO: abandon using tags for categories and instead add a new column to DrawableDB
|
} catch (TskCoreException ex) {
|
||||||
if (ct.getName().getDisplayName().startsWith(Category.CATEGORY_PREFIX)) {
|
LOGGER.log(Level.SEVERE, "Error removing old categories result", ex);
|
||||||
Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(ct); //tsk db
|
|
||||||
controller.getCategoryManager().decrementCategoryCount(Category.fromDisplayName(ct.getName().getDisplayName())); //memory/drawable db
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
//add cat tag if no existing cat tag for that cat
|
||||||
|
if (fileTags.stream()
|
||||||
|
.map(Tag::getName)
|
||||||
|
.filter(tagName::equals)
|
||||||
|
.collect(Collectors.toList()).isEmpty()) {
|
||||||
|
tagsManager.addContentTag(file, tagName, comment);
|
||||||
}
|
}
|
||||||
controller.getCategoryManager().incrementCategoryCount(Category.fromDisplayName(tagName.getDisplayName())); //memory/drawable db
|
|
||||||
if (tagName != Category.ZERO.getTagName()) { // no tags for cat-0
|
|
||||||
Case.getCurrentCase().getServices().getTagsManager().addContentTag(file, tagName, comment); //tsk db
|
|
||||||
}
|
}
|
||||||
//make sure rest of ui hears category change.
|
|
||||||
controller.getGroupManager().handleFileUpdate(FileUpdateEvent.newUpdateEvent(Collections.singleton(fileID), DrawableAttribute.CATEGORY)); //memory/ui
|
|
||||||
|
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "Error categorizing result", ex);
|
LOGGER.log(Level.SEVERE, "Error categorizing result", ex);
|
||||||
JOptionPane.showMessageDialog(null, "Unable to categorize " + fileID + ".", "Categorizing Error", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(null, "Unable to categorize " + fileID + ".", "Categorizing Error", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshDirectoryTree();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2015 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.imagegallery.actions;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
|
import org.controlsfx.control.action.Action;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableTagsManager;
|
||||||
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
|
import org.sleuthkit.datamodel.TagName;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action to delete the follow up tag a
|
||||||
|
*/
|
||||||
|
public class DeleteFollowUpTagAction extends Action {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(DeleteFollowUpTagAction.class.getName());
|
||||||
|
|
||||||
|
public DeleteFollowUpTagAction(final ImageGalleryController controller, final DrawableFile<?> file) {
|
||||||
|
super("Delete Follow Up Tag");
|
||||||
|
setEventHandler((ActionEvent t) -> {
|
||||||
|
new SwingWorker<Void, Void>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground() throws Exception {
|
||||||
|
final DrawableTagsManager tagsManager = controller.getTagsManager();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final TagName followUpTagName = tagsManager.getFollowUpTagName();
|
||||||
|
|
||||||
|
List<ContentTag> contentTagsByContent = tagsManager.getContentTagsByContent(file);
|
||||||
|
for (ContentTag ct : contentTagsByContent) {
|
||||||
|
if (ct.getName().getDisplayName().equals(followUpTagName.getDisplayName())) {
|
||||||
|
tagsManager.deleteContentTag(ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Failed to delete follow up tag.", ex);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,7 @@ import javafx.scene.image.ImageView;
|
|||||||
import org.controlsfx.control.action.Action;
|
import org.controlsfx.control.action.Action;
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupViewState;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the currently fisplayed group as "seen" and advances to the next unseen
|
* Marks the currently fisplayed group as "seen" and advances to the next unseen
|
||||||
@ -51,6 +51,7 @@ public class NextUnseenGroup extends Action {
|
|||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
setGraphic(new ImageView(ADVANCE));
|
setGraphic(new ImageView(ADVANCE));
|
||||||
|
|
||||||
|
//TODO: do we need both these listeners?
|
||||||
controller.getGroupManager().getAnalyzedGroups().addListener((Observable observable) -> {
|
controller.getGroupManager().getAnalyzedGroups().addListener((Observable observable) -> {
|
||||||
Platform.runLater(this::updateButton);
|
Platform.runLater(this::updateButton);
|
||||||
|
|
||||||
|
@ -20,52 +20,42 @@ package org.sleuthkit.autopsy.imagegallery.datamodel;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.TagUtils;
|
|
||||||
import org.sleuthkit.datamodel.TagName;
|
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum to represent the six categories in the DHs image categorization scheme.
|
* Enum to represent the six categories in the DHs image categorization scheme.
|
||||||
*/
|
*/
|
||||||
public enum Category implements Comparable<Category> {
|
public enum Category {
|
||||||
|
|
||||||
ZERO(Color.LIGHTGREY, 0, "CAT-0, Uncategorized"),
|
/* This order of declaration is required so that Enum's compareTo method
|
||||||
ONE(Color.RED, 1, "CAT-1, Child Exploitation (Illegal)"),
|
* preserves the fact that lower category numbers are first/most sever,
|
||||||
TWO(Color.ORANGE, 2, "CAT-2, Child Exploitation (Non-Illegal/Age Difficult)"),
|
* except 0 which is last */
|
||||||
THREE(Color.YELLOW, 3, "CAT-3, CGI/Animation (Child Exploitive)"),
|
ONE(Color.RED, 1, "CAT-1: Child Exploitation (Illegal)"),
|
||||||
FOUR(Color.BISQUE, 4, "CAT-4, Exemplar/Comparison (Internal Use Only)"),
|
TWO(Color.ORANGE, 2, "CAT-2: Child Exploitation (Non-Illegal/Age Difficult)"),
|
||||||
FIVE(Color.GREEN, 5, "CAT-5, Non-pertinent");
|
THREE(Color.YELLOW, 3, "CAT-3: CGI/Animation (Child Exploitive)"),
|
||||||
|
FOUR(Color.BISQUE, 4, "CAT-4: Exemplar/Comparison (Internal Use Only)"),
|
||||||
|
FIVE(Color.GREEN, 5, "CAT-5: Non-pertinent"),
|
||||||
|
ZERO(Color.LIGHTGREY, 0, "CAT-0: Uncategorized");
|
||||||
|
|
||||||
/** map from displayName to enum value */
|
/** map from displayName to enum value */
|
||||||
private static final Map<String, Category> nameMap
|
private static final Map<String, Category> nameMap
|
||||||
= Stream.of(values()).collect(Collectors.toMap(Category::getDisplayName,
|
= Stream.of(values()).collect(Collectors.toMap(
|
||||||
|
Category::getDisplayName,
|
||||||
Function.identity()));
|
Function.identity()));
|
||||||
|
|
||||||
public static final String CATEGORY_PREFIX = "CAT-";
|
|
||||||
|
|
||||||
public static Category fromDisplayName(String displayName) {
|
public static Category fromDisplayName(String displayName) {
|
||||||
return nameMap.get(displayName);
|
return nameMap.get(displayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static boolean isCategoryName(String tName) {
|
||||||
* Use when closing a case to make sure everything is re-initialized in the
|
return nameMap.containsKey(tName);
|
||||||
* next case.
|
|
||||||
*/
|
|
||||||
public static void clearTagNames() {
|
|
||||||
Category.ZERO.tagName = null;
|
|
||||||
Category.ONE.tagName = null;
|
|
||||||
Category.TWO.tagName = null;
|
|
||||||
Category.THREE.tagName = null;
|
|
||||||
Category.FOUR.tagName = null;
|
|
||||||
Category.FIVE.tagName = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TagName tagName;
|
public static boolean isNotCategoryName(String tName) {
|
||||||
|
return nameMap.containsKey(tName) == false;
|
||||||
|
}
|
||||||
|
|
||||||
private final Color color;
|
private final Color color;
|
||||||
|
|
||||||
@ -96,19 +86,4 @@ public enum Category implements Comparable<Category> {
|
|||||||
return displayName;
|
return displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* get the TagName used to store this Category in the main autopsy db.
|
|
||||||
*
|
|
||||||
* @return the TagName used for this Category
|
|
||||||
*/
|
|
||||||
public TagName getTagName() {
|
|
||||||
if (tagName == null) {
|
|
||||||
try {
|
|
||||||
tagName = TagUtils.getTagName(displayName);
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
Logger.getLogger(Category.class.getName()).log(Level.SEVERE, "failed to get TagName for " + displayName, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tagName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,23 @@
|
|||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import com.google.common.eventbus.AsyncEventBus;
|
||||||
import com.google.common.eventbus.EventBus;
|
import com.google.common.eventbus.EventBus;
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.atomic.LongAdder;
|
import java.util.concurrent.atomic.LongAdder;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.events.ContentTagAddedEvent;
|
||||||
|
import org.sleuthkit.autopsy.events.ContentTagDeletedEvent;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
|
import org.sleuthkit.datamodel.TagName;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a cached view of the number of files per category, and fires
|
* Provides a cached view of the number of files per category, and fires
|
||||||
@ -43,6 +54,10 @@ public class CategoryManager {
|
|||||||
|
|
||||||
private static final java.util.logging.Logger LOGGER = Logger.getLogger(CategoryManager.class.getName());
|
private static final java.util.logging.Logger LOGGER = Logger.getLogger(CategoryManager.class.getName());
|
||||||
|
|
||||||
|
private final ImageGalleryController controller;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the DrawableDB that backs the category counts cache. The counts are
|
* the DrawableDB that backs the category counts cache. The counts are
|
||||||
* initialized from this, and the counting of CAT-0 is always delegated to
|
* initialized from this, and the counting of CAT-0 is always delegated to
|
||||||
@ -53,7 +68,11 @@ public class CategoryManager {
|
|||||||
/**
|
/**
|
||||||
* Used to distribute {@link CategoryChangeEvent}s
|
* Used to distribute {@link CategoryChangeEvent}s
|
||||||
*/
|
*/
|
||||||
private final EventBus categoryEventBus = new EventBus("Category Event Bus");
|
private final EventBus categoryEventBus = new AsyncEventBus(Executors.newSingleThreadExecutor(
|
||||||
|
new BasicThreadFactory.Builder().namingPattern("Category Event Bus").uncaughtExceptionHandler((Thread t, Throwable e) -> {
|
||||||
|
LOGGER.log(Level.SEVERE, "uncaught exception in event bus handler", e);
|
||||||
|
}).build()
|
||||||
|
));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For performance reasons, keep current category counts in memory. All of
|
* For performance reasons, keep current category counts in memory. All of
|
||||||
@ -62,6 +81,20 @@ public class CategoryManager {
|
|||||||
*/
|
*/
|
||||||
private final LoadingCache<Category, LongAdder> categoryCounts
|
private final LoadingCache<Category, LongAdder> categoryCounts
|
||||||
= CacheBuilder.newBuilder().build(CacheLoader.from(this::getCategoryCountHelper));
|
= CacheBuilder.newBuilder().build(CacheLoader.from(this::getCategoryCountHelper));
|
||||||
|
/**
|
||||||
|
* cached TagNames corresponding to Categories, looked up from
|
||||||
|
* autopsyTagManager at initial request or if invalidated by case change.
|
||||||
|
*/
|
||||||
|
private final LoadingCache<Category, TagName> catTagNameMap = CacheBuilder.newBuilder().build(CacheLoader.from(cat
|
||||||
|
-> getController().getTagsManager().getTagName(cat)));
|
||||||
|
|
||||||
|
public CategoryManager(ImageGalleryController controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageGalleryController getController() {
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* assign a new db. the counts cache is invalidated and all subsequent db
|
* assign a new db. the counts cache is invalidated and all subsequent db
|
||||||
@ -71,10 +104,17 @@ public class CategoryManager {
|
|||||||
*
|
*
|
||||||
* @param db
|
* @param db
|
||||||
*/
|
*/
|
||||||
public void setDb(DrawableDB db) {
|
synchronized public void setDb(DrawableDB db) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
categoryCounts.invalidateAll();
|
categoryCounts.invalidateAll();
|
||||||
Category.clearTagNames();
|
catTagNameMap.invalidateAll();
|
||||||
|
fireChange(Collections.emptyList(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized public void invalidateCaches() {
|
||||||
|
categoryCounts.invalidateAll();
|
||||||
|
catTagNameMap.invalidateAll();
|
||||||
|
fireChange(Collections.emptyList(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,7 +124,7 @@ public class CategoryManager {
|
|||||||
*
|
*
|
||||||
* @return the long the number of files with the given Category
|
* @return the long the number of files with the given Category
|
||||||
*/
|
*/
|
||||||
public long getCategoryCount(Category cat) {
|
synchronized public long getCategoryCount(Category cat) {
|
||||||
if (cat == Category.ZERO) {
|
if (cat == Category.ZERO) {
|
||||||
// Keeping track of the uncategorized files is a bit tricky while ingest
|
// Keeping track of the uncategorized files is a bit tricky while ingest
|
||||||
// is going on, so always use the list of file IDs we already have along with the
|
// is going on, so always use the list of file IDs we already have along with the
|
||||||
@ -102,7 +142,7 @@ public class CategoryManager {
|
|||||||
*
|
*
|
||||||
* @param cat the Category to increment
|
* @param cat the Category to increment
|
||||||
*/
|
*/
|
||||||
public void incrementCategoryCount(Category cat) {
|
synchronized public void incrementCategoryCount(Category cat) {
|
||||||
if (cat != Category.ZERO) {
|
if (cat != Category.ZERO) {
|
||||||
categoryCounts.getUnchecked(cat).increment();
|
categoryCounts.getUnchecked(cat).increment();
|
||||||
}
|
}
|
||||||
@ -114,7 +154,7 @@ public class CategoryManager {
|
|||||||
*
|
*
|
||||||
* @param cat the Category to decrement
|
* @param cat the Category to decrement
|
||||||
*/
|
*/
|
||||||
public void decrementCategoryCount(Category cat) {
|
synchronized public void decrementCategoryCount(Category cat) {
|
||||||
if (cat != Category.ZERO) {
|
if (cat != Category.ZERO) {
|
||||||
categoryCounts.getUnchecked(cat).decrement();
|
categoryCounts.getUnchecked(cat).decrement();
|
||||||
}
|
}
|
||||||
@ -130,7 +170,7 @@ public class CategoryManager {
|
|||||||
* @return a LongAdder whose value is set to the number of file with the
|
* @return a LongAdder whose value is set to the number of file with the
|
||||||
* given Category
|
* given Category
|
||||||
*/
|
*/
|
||||||
private LongAdder getCategoryCountHelper(Category cat) {
|
synchronized private LongAdder getCategoryCountHelper(Category cat) {
|
||||||
LongAdder longAdder = new LongAdder();
|
LongAdder longAdder = new LongAdder();
|
||||||
longAdder.decrement();
|
longAdder.decrement();
|
||||||
try {
|
try {
|
||||||
@ -147,8 +187,8 @@ public class CategoryManager {
|
|||||||
*
|
*
|
||||||
* @param fileIDs
|
* @param fileIDs
|
||||||
*/
|
*/
|
||||||
public void fireChange(Collection<Long> fileIDs) {
|
public void fireChange(Collection<Long> fileIDs, Category newCategory) {
|
||||||
categoryEventBus.post(new CategoryChangeEvent(fileIDs));
|
categoryEventBus.post(new CategoryChangeEvent(fileIDs, newCategory));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,4 +209,97 @@ public class CategoryManager {
|
|||||||
categoryEventBus.unregister(listener);
|
categoryEventBus.unregister(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the TagName used to store this Category in the main autopsy db.
|
||||||
|
*
|
||||||
|
* @return the TagName used for this Category
|
||||||
|
*/
|
||||||
|
synchronized public TagName getTagName(Category cat) {
|
||||||
|
return catTagNameMap.getUnchecked(cat);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Category categoryFromTagName(TagName tagName) {
|
||||||
|
return Category.fromDisplayName(tagName.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isCategoryTagName(TagName tName) {
|
||||||
|
return Category.isCategoryName(tName.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isNotCategoryTagName(TagName tName) {
|
||||||
|
return Category.isNotCategoryName(tName.getDisplayName());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void handleTagAdded(ContentTagAddedEvent event) {
|
||||||
|
final ContentTag addedTag = event.getTag();
|
||||||
|
if (isCategoryTagName(addedTag.getName())) {
|
||||||
|
final DrawableTagsManager tagsManager = controller.getTagsManager();
|
||||||
|
try {
|
||||||
|
//remove old category tag(s) if necessary
|
||||||
|
for (ContentTag ct : tagsManager.getContentTagsByContent(addedTag.getContent())) {
|
||||||
|
if (ct.getId() != addedTag.getId()
|
||||||
|
&& CategoryManager.isCategoryTagName(ct.getName())) {
|
||||||
|
try {
|
||||||
|
tagsManager.deleteContentTag(ct);
|
||||||
|
} catch (TskCoreException tskException) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Failed to delete content tag. Unable to maintain categories in a consistent state.", tskException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (TskCoreException tskException) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Failed to get content tags for content. Unable to maintain category in a consistent state.", tskException);
|
||||||
|
}
|
||||||
|
Category newCat = CategoryManager.categoryFromTagName(addedTag.getName());
|
||||||
|
if (newCat != Category.ZERO) {
|
||||||
|
incrementCategoryCount(newCat);
|
||||||
|
}
|
||||||
|
|
||||||
|
fireChange(Collections.singleton(addedTag.getContent().getId()), newCat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void handleTagDeleted(ContentTagDeletedEvent event) {
|
||||||
|
ContentTag deleted = event.getTag();
|
||||||
|
if (isCategoryTagName(deleted.getName())) {
|
||||||
|
|
||||||
|
Category deletedCat = CategoryManager.categoryFromTagName(deleted.getName());
|
||||||
|
if (deletedCat != Category.ZERO) {
|
||||||
|
decrementCategoryCount(deletedCat);
|
||||||
|
}
|
||||||
|
fireChange(Collections.singleton(deleted.getContent().getId()), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event broadcast to various UI componenets when one or more files'
|
||||||
|
* category
|
||||||
|
* has been changed
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public static class CategoryChangeEvent {
|
||||||
|
|
||||||
|
private final Collection<Long> fileIDs;
|
||||||
|
private final Category newCategory;
|
||||||
|
|
||||||
|
public CategoryChangeEvent(Collection<Long> fileIDs, Category newCategory) {
|
||||||
|
super();
|
||||||
|
this.fileIDs = fileIDs;
|
||||||
|
this.newCategory = newCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Category getNewCategory() {
|
||||||
|
return newCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the fileIDs of the files whose categories have changed
|
||||||
|
*/
|
||||||
|
public Collection<Long> getFileIDs() {
|
||||||
|
return Collections.unmodifiableCollection(fileIDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import java.util.HashMap;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
@ -46,12 +47,12 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import org.openide.util.Exceptions;
|
import org.openide.util.Exceptions;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileUpdateEvent;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryModule;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryModule;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupKey;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupKey;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupManager;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupManager;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupSortBy;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupSortBy;
|
||||||
import static org.sleuthkit.autopsy.imagegallery.grouping.GroupSortBy.GROUP_BY_VALUE;
|
import static org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupSortBy.GROUP_BY_VALUE;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
@ -124,11 +125,6 @@ public final class DrawableDB {
|
|||||||
*/
|
*/
|
||||||
private final Map<DrawableAttribute<?>, PreparedStatement> groupStatementMap = new HashMap<>();
|
private final Map<DrawableAttribute<?>, PreparedStatement> groupStatementMap = new HashMap<>();
|
||||||
|
|
||||||
/**
|
|
||||||
* list of observers to be notified if the database changes
|
|
||||||
*/
|
|
||||||
private final HashSet<FileUpdateEvent.FileUpdateListener> updateListeners = new HashSet<>();
|
|
||||||
|
|
||||||
private GroupManager groupManager;
|
private GroupManager groupManager;
|
||||||
|
|
||||||
private final Path dbPath;
|
private final Path dbPath;
|
||||||
@ -147,6 +143,7 @@ public final class DrawableDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
private final SleuthkitCase tskCase;
|
private final SleuthkitCase tskCase;
|
||||||
|
private final ImageGalleryController controller;
|
||||||
|
|
||||||
//////////////general database logic , mostly borrowed from sleuthkitcase
|
//////////////general database logic , mostly borrowed from sleuthkitcase
|
||||||
/**
|
/**
|
||||||
@ -195,9 +192,11 @@ public final class DrawableDB {
|
|||||||
*
|
*
|
||||||
* @throws SQLException if there is problem creating or configuring the db
|
* @throws SQLException if there is problem creating or configuring the db
|
||||||
*/
|
*/
|
||||||
private DrawableDB(Path dbPath, SleuthkitCase tskCase) throws SQLException, ExceptionInInitializerError, IOException {
|
private DrawableDB(Path dbPath, ImageGalleryController controller) throws SQLException, ExceptionInInitializerError, IOException {
|
||||||
this.dbPath = dbPath;
|
this.dbPath = dbPath;
|
||||||
this.tskCase = tskCase;
|
this.controller = controller;
|
||||||
|
this.tskCase = controller.getSleuthKitCase();
|
||||||
|
this.groupManager = controller.getGroupManager();
|
||||||
Files.createDirectories(dbPath.getParent());
|
Files.createDirectories(dbPath.getParent());
|
||||||
if (initializeDBSchema()) {
|
if (initializeDBSchema()) {
|
||||||
updateFileStmt = prepareStatement(
|
updateFileStmt = prepareStatement(
|
||||||
@ -286,10 +285,10 @@ public final class DrawableDB {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static DrawableDB getDrawableDB(Path dbPath, SleuthkitCase tskCase) {
|
public static DrawableDB getDrawableDB(Path dbPath, ImageGalleryController controller) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new DrawableDB(dbPath.resolve("drawable.db"), tskCase);
|
return new DrawableDB(dbPath.resolve("drawable.db"), controller);
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "sql error creating database connection", ex);
|
LOGGER.log(Level.SEVERE, "sql error creating database connection", ex);
|
||||||
return null;
|
return null;
|
||||||
@ -584,9 +583,8 @@ public final class DrawableDB {
|
|||||||
stmt.setBoolean(8, f.isAnalyzed());
|
stmt.setBoolean(8, f.isAnalyzed());
|
||||||
stmt.executeUpdate();
|
stmt.executeUpdate();
|
||||||
|
|
||||||
final Collection<String> hashSetNames = DrawableAttribute.HASHSET.getValue(f);
|
final Collection<String> hashSetNames = getHashSetsForFileFromAutopsy(f.getId());
|
||||||
|
|
||||||
if (hashSetNames.isEmpty() == false) {
|
|
||||||
for (String name : hashSetNames) {
|
for (String name : hashSetNames) {
|
||||||
|
|
||||||
// "insert or ignore into hash_sets (hash_set_name) values (?)"
|
// "insert or ignore into hash_sets (hash_set_name) values (?)"
|
||||||
@ -607,7 +605,6 @@ public final class DrawableDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//and update all groups this file is in
|
//and update all groups this file is in
|
||||||
for (DrawableAttribute<?> attr : DrawableAttribute.getGroupableAttrs()) {
|
for (DrawableAttribute<?> attr : DrawableAttribute.getGroupableAttrs()) {
|
||||||
@ -641,22 +638,6 @@ public final class DrawableDB {
|
|||||||
tr.commit(notify);
|
tr.commit(notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addUpdatedFileListener(FileUpdateEvent.FileUpdateListener l) {
|
|
||||||
updateListeners.add(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fireUpdatedFiles(Collection<Long> fileIDs) {
|
|
||||||
for (FileUpdateEvent.FileUpdateListener listener : updateListeners) {
|
|
||||||
listener.handleFileUpdate(FileUpdateEvent.newUpdateEvent(fileIDs, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fireRemovedFiles(Collection<Long> fileIDs) {
|
|
||||||
for (FileUpdateEvent.FileUpdateListener listener : updateListeners) {
|
|
||||||
listener.handleFileUpdate(FileUpdateEvent.newRemovedEvent(fileIDs));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean isFileAnalyzed(DrawableFile<?> f) {
|
public Boolean isFileAnalyzed(DrawableFile<?> f) {
|
||||||
return isFileAnalyzed(f.getId());
|
return isFileAnalyzed(f.getId());
|
||||||
}
|
}
|
||||||
@ -698,7 +679,7 @@ public final class DrawableDB {
|
|||||||
public Boolean isGroupAnalyzed(GroupKey<?> gk) {
|
public Boolean isGroupAnalyzed(GroupKey<?> gk) {
|
||||||
dbReadLock();
|
dbReadLock();
|
||||||
try {
|
try {
|
||||||
List<Long> fileIDsInGroup = getFileIDsInGroup(gk);
|
Set<Long> fileIDsInGroup = getFileIDsInGroup(gk);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// In testing, this method appears to be a lot faster than doing one large select statement
|
// In testing, this method appears to be a lot faster than doing one large select statement
|
||||||
@ -747,10 +728,10 @@ public final class DrawableDB {
|
|||||||
*
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
public List<Long> findAllFileIdsWhere(String sqlWhereClause) throws TskCoreException {
|
public Set<Long> findAllFileIdsWhere(String sqlWhereClause) throws TskCoreException {
|
||||||
Statement statement = null;
|
Statement statement = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
List<Long> ret = new ArrayList<>();
|
Set<Long> ret = new HashSet<>();
|
||||||
dbReadLock();
|
dbReadLock();
|
||||||
try {
|
try {
|
||||||
statement = con.createStatement();
|
statement = con.createStatement();
|
||||||
@ -984,7 +965,7 @@ public final class DrawableDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Long> getFileIDsInGroup(GroupKey<?> groupKey) throws TskCoreException {
|
public Set<Long> getFileIDsInGroup(GroupKey<?> groupKey) throws TskCoreException {
|
||||||
|
|
||||||
if (groupKey.getAttribute().isDBColumn) {
|
if (groupKey.getAttribute().isDBColumn) {
|
||||||
switch (groupKey.getAttribute().attrName) {
|
switch (groupKey.getAttribute().attrName) {
|
||||||
@ -994,7 +975,7 @@ public final class DrawableDB {
|
|||||||
return groupManager.getFileIDsWithTag((TagName) groupKey.getValue());
|
return groupManager.getFileIDsWithTag((TagName) groupKey.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<Long> files = new ArrayList<>();
|
Set<Long> files = new HashSet<>();
|
||||||
dbReadLock();
|
dbReadLock();
|
||||||
try {
|
try {
|
||||||
PreparedStatement statement = getGroupStatment(groupKey.getAttribute());
|
PreparedStatement statement = getGroupStatment(groupKey.getAttribute());
|
||||||
@ -1055,7 +1036,7 @@ public final class DrawableDB {
|
|||||||
public List<DrawableFile<?>> getFilesWithCategory(Category cat) throws TskCoreException, IllegalArgumentException {
|
public List<DrawableFile<?>> getFilesWithCategory(Category cat) throws TskCoreException, IllegalArgumentException {
|
||||||
try {
|
try {
|
||||||
List<DrawableFile<?>> files = new ArrayList<>();
|
List<DrawableFile<?>> files = new ArrayList<>();
|
||||||
List<ContentTag> contentTags = Case.getCurrentCase().getServices().getTagsManager().getContentTagsByTagName(cat.getTagName());
|
List<ContentTag> contentTags = controller.getTagsManager().getContentTagsByTagName(controller.getTagsManager().getTagName(cat));
|
||||||
for (ContentTag ct : contentTags) {
|
for (ContentTag ct : contentTags) {
|
||||||
if (ct.getContent() instanceof AbstractFile) {
|
if (ct.getContent() instanceof AbstractFile) {
|
||||||
files.add(DrawableFile.create((AbstractFile) ct.getContent(), isFileAnalyzed(ct.getContent().getId()),
|
files.add(DrawableFile.create((AbstractFile) ct.getContent(), isFileAnalyzed(ct.getContent().getId()),
|
||||||
@ -1113,6 +1094,8 @@ public final class DrawableDB {
|
|||||||
removeFileStmt.setLong(1, id);
|
removeFileStmt.setLong(1, id);
|
||||||
removeFileStmt.executeUpdate();
|
removeFileStmt.executeUpdate();
|
||||||
tr.addRemovedFile(id);
|
tr.addRemovedFile(id);
|
||||||
|
|
||||||
|
//TODO: delete from hash_set_hits table also...
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
LOGGER.log(Level.WARNING, "failed to delete row for obj_id = " + id, ex);
|
LOGGER.log(Level.WARNING, "failed to delete row for obj_id = " + id, ex);
|
||||||
} finally {
|
} finally {
|
||||||
@ -1140,16 +1123,13 @@ public final class DrawableDB {
|
|||||||
*
|
*
|
||||||
* @return a set of names, each of which is a hashset that the given file is
|
* @return a set of names, each of which is a hashset that the given file is
|
||||||
* in.
|
* in.
|
||||||
*
|
|
||||||
*
|
|
||||||
* //TODO: why does this go to the SKC? don't we already have this in =fo
|
|
||||||
* in the drawable db?
|
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Set<String> getHashSetsForFile(long fileID) {
|
public Set<String> getHashSetsForFileFromAutopsy(long fileID) {
|
||||||
try {
|
try {
|
||||||
Set<String> hashNames = new HashSet<>();
|
Set<String> hashNames = new HashSet<>();
|
||||||
List<BlackboardArtifact> arts = tskCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, fileID);
|
List<BlackboardArtifact> arts = tskCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, fileID);
|
||||||
|
|
||||||
for (BlackboardArtifact a : arts) {
|
for (BlackboardArtifact a : arts) {
|
||||||
List<BlackboardAttribute> attrs = a.getAttributes();
|
List<BlackboardAttribute> attrs = a.getAttributes();
|
||||||
for (BlackboardAttribute attr : attrs) {
|
for (BlackboardAttribute attr : attrs) {
|
||||||
@ -1157,8 +1137,8 @@ public final class DrawableDB {
|
|||||||
hashNames.add(attr.getValueString());
|
hashNames.add(attr.getValueString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hashNames;
|
|
||||||
}
|
}
|
||||||
|
return hashNames;
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "failed to get hash sets for file", ex);
|
LOGGER.log(Level.SEVERE, "failed to get hash sets for file", ex);
|
||||||
}
|
}
|
||||||
@ -1217,10 +1197,24 @@ public final class DrawableDB {
|
|||||||
/**
|
/**
|
||||||
* For performance reasons, keep the file type in memory
|
* For performance reasons, keep the file type in memory
|
||||||
*/
|
*/
|
||||||
private final Map<AbstractFile, Boolean> videoFileMap = new ConcurrentHashMap<>();
|
private final Map<Long, Boolean> videoFileMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is this File a video file?
|
||||||
|
*
|
||||||
|
* @param f check if this file is a video. will return false for null file.
|
||||||
|
*
|
||||||
|
* @return returns true if this file is a video as determined by {@link ImageGalleryModule#isVideoFile(org.sleuthkit.datamodel.AbstractFile)
|
||||||
|
* } but caches the result.
|
||||||
|
* returns false if passed a null AbstractFile
|
||||||
|
*/
|
||||||
public boolean isVideoFile(AbstractFile f) {
|
public boolean isVideoFile(AbstractFile f) {
|
||||||
return videoFileMap.computeIfAbsent(f, ImageGalleryModule::isVideoFile);
|
|
||||||
|
if (Objects.isNull(f)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return videoFileMap.computeIfAbsent(f.getId(), (id) -> ImageGalleryModule.isVideoFile(f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1240,7 +1234,7 @@ public final class DrawableDB {
|
|||||||
*/
|
*/
|
||||||
public long getCategoryCount(Category cat) {
|
public long getCategoryCount(Category cat) {
|
||||||
try {
|
try {
|
||||||
return Case.getCurrentCase().getServices().getTagsManager().getContentTagsByTagName(cat.getTagName()).stream()
|
return tskCase.getContentTagsByTagName(controller.getTagsManager().getTagName(cat)).stream()
|
||||||
.map(ContentTag::getContent)
|
.map(ContentTag::getContent)
|
||||||
.map(Content::getId)
|
.map(Content::getId)
|
||||||
.filter(this::isInDB)
|
.filter(this::isInDB)
|
||||||
@ -1309,8 +1303,10 @@ public final class DrawableDB {
|
|||||||
close();
|
close();
|
||||||
|
|
||||||
if (notify) {
|
if (notify) {
|
||||||
fireUpdatedFiles(updatedFiles);
|
if (groupManager != null) {
|
||||||
fireRemovedFiles(removedFiles);
|
groupManager.handleFileUpdate(updatedFiles);
|
||||||
|
groupManager.handleFileRemoved(removedFiles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
if (Case.isCaseOpen()) {
|
if (Case.isCaseOpen()) {
|
||||||
|
@ -46,7 +46,6 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBU
|
|||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING;
|
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.ContentTag;
|
|
||||||
import org.sleuthkit.datamodel.ContentVisitor;
|
import org.sleuthkit.datamodel.ContentVisitor;
|
||||||
import org.sleuthkit.datamodel.SleuthkitItemVisitor;
|
import org.sleuthkit.datamodel.SleuthkitItemVisitor;
|
||||||
import org.sleuthkit.datamodel.Tag;
|
import org.sleuthkit.datamodel.Tag;
|
||||||
@ -111,7 +110,6 @@ public abstract class DrawableFile<T extends AbstractFile> extends AbstractFile
|
|||||||
super(file.getSleuthkitCase(), file.getId(), file.getAttrType(), file.getAttrId(), file.getName(), file.getType(), file.getMetaAddr(), (int) file.getMetaSeq(), file.getDirType(), file.getMetaType(), null, new Integer(0).shortValue(), file.getSize(), file.getCtime(), file.getCrtime(), file.getAtime(), file.getMtime(), new Integer(0).shortValue(), file.getUid(), file.getGid(), file.getMd5Hash(), file.getKnown(), file.getParentPath());
|
super(file.getSleuthkitCase(), file.getId(), file.getAttrType(), file.getAttrId(), file.getName(), file.getType(), file.getMetaAddr(), (int) file.getMetaSeq(), file.getDirType(), file.getMetaType(), null, new Integer(0).shortValue(), file.getSize(), file.getCtime(), file.getCrtime(), file.getAtime(), file.getMtime(), new Integer(0).shortValue(), file.getUid(), file.getGid(), file.getMd5Hash(), file.getKnown(), file.getParentPath());
|
||||||
this.analyzed = new SimpleBooleanProperty(analyzed);
|
this.analyzed = new SimpleBooleanProperty(analyzed);
|
||||||
this.file = file;
|
this.file = file;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean isVideo();
|
public abstract boolean isVideo();
|
||||||
@ -170,19 +168,16 @@ public abstract class DrawableFile<T extends AbstractFile> extends AbstractFile
|
|||||||
|
|
||||||
public Set<TagName> getTagNames() {
|
public Set<TagName> getTagNames() {
|
||||||
try {
|
try {
|
||||||
List<ContentTag> contentTagsByContent = Case.getCurrentCase().getServices().getTagsManager().getContentTagsByContent(this);
|
|
||||||
|
|
||||||
return contentTagsByContent.stream()
|
return getSleuthkitCase().getContentTagsByContent(this).stream()
|
||||||
.map(Tag::getName)
|
.map(Tag::getName)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
Logger.getAnonymousLogger().log(Level.WARNING, "problem looking up " + DrawableAttribute.TAGS.getDisplayName() + " for " + file.getName(), ex);
|
Logger.getAnonymousLogger().log(Level.WARNING, "problem looking up " + DrawableAttribute.TAGS.getDisplayName() + " for " + file.getName(), ex);
|
||||||
return Collections.emptySet();
|
|
||||||
} catch (IllegalStateException ex) {
|
} catch (IllegalStateException ex) {
|
||||||
Logger.getAnonymousLogger().log(Level.WARNING, "there is no case open; failed to look up " + DrawableAttribute.TAGS.getDisplayName() + " for " + file.getName());
|
Logger.getAnonymousLogger().log(Level.WARNING, "there is no case open; failed to look up " + DrawableAttribute.TAGS.getDisplayName() + " for " + file.getName());
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
}
|
||||||
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@ -273,27 +268,21 @@ public abstract class DrawableFile<T extends AbstractFile> extends AbstractFile
|
|||||||
return category;
|
return category;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateCategory() {
|
/** set the category property to the most severe one found */
|
||||||
|
private void updateCategory() {
|
||||||
try {
|
try {
|
||||||
List<ContentTag> contentTagsByContent = Case.getCurrentCase().getServices().getTagsManager().getContentTagsByContent(this);
|
category.set(getSleuthkitCase().getContentTagsByContent(this).stream()
|
||||||
Category cat = null;
|
.map(Tag::getName).filter(CategoryManager::isCategoryTagName)
|
||||||
for (ContentTag ct : contentTagsByContent) {
|
.map(TagName::getDisplayName)
|
||||||
if (ct.getName().getDisplayName().startsWith(Category.CATEGORY_PREFIX)) {
|
.map(Category::fromDisplayName)
|
||||||
cat = Category.fromDisplayName(ct.getName().getDisplayName());
|
.sorted().findFirst() //sort by severity and take the first
|
||||||
break;
|
.orElse(Category.ZERO)
|
||||||
}
|
);
|
||||||
}
|
|
||||||
if (cat == null) {
|
|
||||||
category.set(Category.ZERO);
|
|
||||||
} else {
|
|
||||||
category.set(cat);
|
|
||||||
}
|
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
Logger.getLogger(DrawableFile.class.getName()).log(Level.WARNING, "problem looking up category for file " + this.getName(), ex);
|
Logger.getLogger(DrawableFile.class.getName()).log(Level.WARNING, "problem looking up category for file " + this.getName(), ex);
|
||||||
} catch (IllegalStateException ex) {
|
} catch (IllegalStateException ex) {
|
||||||
// We get here many times if the case is closed during ingest, so don't print out a ton of warnings.
|
// We get here many times if the case is closed during ingest, so don't print out a ton of warnings.
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Image getThumbnail();
|
public abstract Image getThumbnail();
|
||||||
|
@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2013-15 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.imagegallery.datamodel;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.AsyncEventBus;
|
||||||
|
import com.google.common.eventbus.EventBus;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.events.ContentTagAddedEvent;
|
||||||
|
import org.sleuthkit.autopsy.events.ContentTagDeletedEvent;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
|
import org.sleuthkit.datamodel.TagName;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages Tags, Tagging, and the relationship between Categories and Tags in
|
||||||
|
* the autopsy Db. delegates some, work to the backing {@link TagsManager}.
|
||||||
|
*/
|
||||||
|
public class DrawableTagsManager {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(DrawableTagsManager.class.getName());
|
||||||
|
|
||||||
|
private static final String FOLLOW_UP = "Follow Up";
|
||||||
|
|
||||||
|
final private Object autopsyTagsManagerLock = new Object();
|
||||||
|
private TagsManager autopsyTagsManager;
|
||||||
|
|
||||||
|
/** Used to distribute {@link TagsChangeEvent}s */
|
||||||
|
private final EventBus tagsEventBus = new AsyncEventBus(
|
||||||
|
Executors.newSingleThreadExecutor(
|
||||||
|
new BasicThreadFactory.Builder().namingPattern("Tags Event Bus").uncaughtExceptionHandler((Thread t, Throwable e) -> {
|
||||||
|
LOGGER.log(Level.SEVERE, "uncaught exception in event bus handler", e);
|
||||||
|
}).build()
|
||||||
|
));
|
||||||
|
|
||||||
|
/** The tag name corresponding to the "built-in" tag "Follow Up" */
|
||||||
|
private TagName followUpTagName;
|
||||||
|
|
||||||
|
public DrawableTagsManager(TagsManager autopsyTagsManager) {
|
||||||
|
this.autopsyTagsManager = autopsyTagsManager;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* register an object to receive CategoryChangeEvents
|
||||||
|
*
|
||||||
|
* @param listner
|
||||||
|
*/
|
||||||
|
public void registerListener(Object listner) {
|
||||||
|
tagsEventBus.register(listner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unregister an object from receiving CategoryChangeEvents
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
public void unregisterListener(Object listener) {
|
||||||
|
tagsEventBus.unregister(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fireTagAddedEvent(ContentTagAddedEvent event) {
|
||||||
|
tagsEventBus.post(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fireTagDeletedEvent(ContentTagDeletedEvent event) {
|
||||||
|
tagsEventBus.post(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* assign a new TagsManager to back this one, ie when the current case
|
||||||
|
* changes
|
||||||
|
*
|
||||||
|
* @param autopsyTagsManager
|
||||||
|
*/
|
||||||
|
public void setAutopsyTagsManager(TagsManager autopsyTagsManager) {
|
||||||
|
synchronized (autopsyTagsManagerLock) {
|
||||||
|
this.autopsyTagsManager = autopsyTagsManager;
|
||||||
|
clearFollowUpTagName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use when closing a case to make sure everything is re-initialized in the
|
||||||
|
* next case.
|
||||||
|
*/
|
||||||
|
public void clearFollowUpTagName() {
|
||||||
|
synchronized (autopsyTagsManagerLock) {
|
||||||
|
followUpTagName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the (cached) follow up TagName
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
public TagName getFollowUpTagName() throws TskCoreException {
|
||||||
|
synchronized (autopsyTagsManagerLock) {
|
||||||
|
if (Objects.isNull(followUpTagName)) {
|
||||||
|
followUpTagName = getTagName(FOLLOW_UP);
|
||||||
|
}
|
||||||
|
return followUpTagName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all the TagNames that are not categories
|
||||||
|
*
|
||||||
|
* @return all the TagNames that are not categories, in alphabetical order
|
||||||
|
* by displayName, or, an empty set if there was an exception looking them
|
||||||
|
* up from the db.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public Collection<TagName> getNonCategoryTagNames() {
|
||||||
|
synchronized (autopsyTagsManagerLock) {
|
||||||
|
try {
|
||||||
|
return autopsyTagsManager.getAllTagNames().stream()
|
||||||
|
.filter(CategoryManager::isNotCategoryTagName)
|
||||||
|
.distinct().sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
} catch (TskCoreException | IllegalStateException ex) {
|
||||||
|
LOGGER.log(Level.WARNING, "couldn't access case", ex);
|
||||||
|
}
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets content tags count by content.
|
||||||
|
*
|
||||||
|
* @param The content of interest.
|
||||||
|
*
|
||||||
|
* @return A list, possibly empty, of the tags that have been applied to the
|
||||||
|
* artifact.
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
public List<ContentTag> getContentTagsByContent(Content content) throws TskCoreException {
|
||||||
|
synchronized (autopsyTagsManagerLock) {
|
||||||
|
return autopsyTagsManager.getContentTagsByContent(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TagName getTagName(String displayName) throws TskCoreException {
|
||||||
|
synchronized (autopsyTagsManagerLock) {
|
||||||
|
try {
|
||||||
|
for (TagName tn : autopsyTagsManager.getAllTagNames()) {
|
||||||
|
if (displayName.equals(tn.getDisplayName())) {
|
||||||
|
return tn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return autopsyTagsManager.addTagName(displayName);
|
||||||
|
} catch (TagsManager.TagNameAlreadyExistsException ex) {
|
||||||
|
throw new TskCoreException("tagame exists but wasn't found", ex);
|
||||||
|
}
|
||||||
|
} catch (IllegalStateException ex) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Case was closed out from underneath", ex);
|
||||||
|
throw new TskCoreException("Case was closed out from underneath", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TagName getTagName(Category cat) {
|
||||||
|
try {
|
||||||
|
return getTagName(cat.getDisplayName());
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentTag addContentTag(DrawableFile<?> file, TagName tagName, String comment) throws TskCoreException {
|
||||||
|
synchronized (autopsyTagsManagerLock) {
|
||||||
|
return autopsyTagsManager.addContentTag(file.getAbstractFile(), tagName, comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ContentTag> getContentTagsByTagName(TagName t) throws TskCoreException {
|
||||||
|
synchronized (autopsyTagsManagerLock) {
|
||||||
|
return autopsyTagsManager.getContentTagsByTagName(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TagName> getAllTagNames() throws TskCoreException {
|
||||||
|
synchronized (autopsyTagsManagerLock) {
|
||||||
|
return autopsyTagsManager.getAllTagNames();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TagName> getTagNamesInUse() throws TskCoreException {
|
||||||
|
synchronized (autopsyTagsManagerLock) {
|
||||||
|
return autopsyTagsManager.getTagNamesInUse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteContentTag(ContentTag ct) throws TskCoreException {
|
||||||
|
synchronized (autopsyTagsManagerLock) {
|
||||||
|
autopsyTagsManager.deleteContentTag(ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import com.google.common.cache.CacheBuilder;
|
|||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.cache.LoadingCache;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages a cache of hashset hits as a map from fileID to hashset names.
|
* Manages a cache of hashset hits as a map from fileID to hashset names.
|
||||||
@ -35,7 +36,7 @@ public class HashSetManager {
|
|||||||
* @return the names of the hashsets the given fileID is in
|
* @return the names of the hashsets the given fileID is in
|
||||||
*/
|
*/
|
||||||
private Set<String> getHashSetsForFileHelper(long fileID) {
|
private Set<String> getHashSetsForFileHelper(long fileID) {
|
||||||
return db.getHashSetsForFile(fileID);
|
return db.getHashSetsForFileFromAutopsy(fileID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,7 +60,6 @@ public class ImageFile<T extends AbstractFile> extends DrawableFile<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (image == null) {
|
if (image == null) {
|
||||||
|
|
||||||
try (ReadContentInputStream readContentInputStream = new ReadContentInputStream(this.getAbstractFile())) {
|
try (ReadContentInputStream readContentInputStream = new ReadContentInputStream(this.getAbstractFile())) {
|
||||||
BufferedImage read = ImageIO.read(readContentInputStream);
|
BufferedImage read = ImageIO.read(readContentInputStream);
|
||||||
image = SwingFXUtils.toFXImage(read, null);
|
image = SwingFXUtils.toFXImage(read, null);
|
||||||
@ -68,8 +67,8 @@ public class ImageFile<T extends AbstractFile> extends DrawableFile<T> {
|
|||||||
Logger.getLogger(ImageFile.class.getName()).log(Level.WARNING, "unable to read file" + getName());
|
Logger.getLogger(ImageFile.class.getName()).log(Level.WARNING, "unable to read file" + getName());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
imageRef = new SoftReference<>(image);
|
|
||||||
}
|
}
|
||||||
|
imageRef = new SoftReference<>(image);
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
* 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.imagegallery.grouping;
|
package org.sleuthkit.autopsy.imagegallery.datamodel.grouping;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
@ -70,7 +70,7 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
|||||||
return groupKey.getValueDisplayName();
|
return groupKey.getValueDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawableGroup(GroupKey<?> groupKey, List<Long> filesInGroup, boolean seen) {
|
DrawableGroup(GroupKey<?> groupKey, Set<Long> filesInGroup, boolean seen) {
|
||||||
this.groupKey = groupKey;
|
this.groupKey = groupKey;
|
||||||
this.fileIDs.setAll(filesInGroup);
|
this.fileIDs.setAll(filesInGroup);
|
||||||
this.seen.set(seen);
|
this.seen.set(seen);
|
||||||
@ -134,7 +134,7 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
|||||||
((DrawableGroup) obj).groupKey);
|
((DrawableGroup) obj).groupKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized public void addFile(Long f) {
|
synchronized void addFile(Long f) {
|
||||||
invalidateHashSetHitsCount();
|
invalidateHashSetHitsCount();
|
||||||
if (fileIDs.contains(f) == false) {
|
if (fileIDs.contains(f) == false) {
|
||||||
fileIDs.add(f);
|
fileIDs.add(f);
|
||||||
@ -142,7 +142,21 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized public void removeFile(Long f) {
|
synchronized void setFiles(Set<? extends Long> newFileIds) {
|
||||||
|
invalidateHashSetHitsCount();
|
||||||
|
boolean filesRemoved = fileIDs.removeIf((Long t) -> newFileIds.contains(t) == false);
|
||||||
|
if (filesRemoved) {
|
||||||
|
seen.set(false);
|
||||||
|
}
|
||||||
|
for (Long f : newFileIds) {
|
||||||
|
if (fileIDs.contains(f) == false) {
|
||||||
|
fileIDs.add(f);
|
||||||
|
seen.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void removeFile(Long f) {
|
||||||
invalidateHashSetHitsCount();
|
invalidateHashSetHitsCount();
|
||||||
if (fileIDs.removeAll(f)) {
|
if (fileIDs.removeAll(f)) {
|
||||||
seen.set(false);
|
seen.set(false);
|
||||||
@ -166,4 +180,5 @@ public class DrawableGroup implements Comparable<DrawableGroup> {
|
|||||||
public boolean isSeen() {
|
public boolean isSeen() {
|
||||||
return seen.get();
|
return seen.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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.imagegallery.grouping;
|
package org.sleuthkit.autopsy.imagegallery.datamodel.grouping;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -56,7 +56,7 @@ public class GroupKey<T extends Comparable<T>> implements Comparable<GroupKey<T>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "GroupKey: " + getAttribute() + " = " + getValue();
|
return "GroupKey: " + getAttribute().attrName + " = " + getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013-4 Basis Technology Corp.
|
* Copyright 2013-15 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");
|
||||||
@ -16,8 +16,9 @@
|
|||||||
* 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.imagegallery.grouping;
|
package org.sleuthkit.autopsy.imagegallery.datamodel.grouping;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -28,18 +29,31 @@ import java.util.HashMap;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||||
import javafx.beans.property.ReadOnlyDoubleWrapper;
|
import javafx.beans.property.ReadOnlyDoubleWrapper;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
import static javafx.concurrent.Worker.State.CANCELLED;
|
||||||
|
import static javafx.concurrent.Worker.State.FAILED;
|
||||||
|
import static javafx.concurrent.Worker.State.READY;
|
||||||
|
import static javafx.concurrent.Worker.State.RUNNING;
|
||||||
|
import static javafx.concurrent.Worker.State.SCHEDULED;
|
||||||
|
import static javafx.concurrent.Worker.State.SUCCEEDED;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
import javax.swing.SortOrder;
|
import javax.swing.SortOrder;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||||
import org.netbeans.api.progress.ProgressHandle;
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
@ -47,17 +61,18 @@ import org.netbeans.api.progress.ProgressHandleFactory;
|
|||||||
import org.openide.util.Exceptions;
|
import org.openide.util.Exceptions;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.coreutils.LoggedTask;
|
import org.sleuthkit.autopsy.coreutils.LoggedTask;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileUpdateEvent;
|
import org.sleuthkit.autopsy.events.ContentTagAddedEvent;
|
||||||
|
import org.sleuthkit.autopsy.events.ContentTagDeletedEvent;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryModule;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryModule;
|
||||||
import org.sleuthkit.autopsy.imagegallery.TagUtils;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableTagsManager;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
@ -71,18 +86,19 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
* extent {@link SleuthkitCase} ) to facilitate creation, retrieval, updating,
|
* extent {@link SleuthkitCase} ) to facilitate creation, retrieval, updating,
|
||||||
* and sorting of {@link DrawableGroup}s.
|
* and sorting of {@link DrawableGroup}s.
|
||||||
*/
|
*/
|
||||||
public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
public class GroupManager {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(GroupManager.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(GroupManager.class.getName());
|
||||||
|
|
||||||
private DrawableDB db;
|
private DrawableDB db;
|
||||||
|
|
||||||
private final ImageGalleryController controller;
|
private final ImageGalleryController controller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* map from {@link GroupKey}s to {@link DrawableGroup}s. All groups (even
|
* map from {@link GroupKey}s to {@link DrawableGroup}s. All groups (even
|
||||||
* not
|
* not fully analyzed or not visible groups could be in this map
|
||||||
* fully analyzed or not visible groups could be in this map
|
|
||||||
*/
|
*/
|
||||||
|
@GuardedBy("this")
|
||||||
private final Map<GroupKey<?>, DrawableGroup> groupMap = new HashMap<>();
|
private final Map<GroupKey<?>, DrawableGroup> groupMap = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,16 +106,12 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
*/
|
*/
|
||||||
@ThreadConfined(type = ThreadType.JFX)
|
@ThreadConfined(type = ThreadType.JFX)
|
||||||
private final ObservableList<DrawableGroup> analyzedGroups = FXCollections.observableArrayList();
|
private final ObservableList<DrawableGroup> analyzedGroups = FXCollections.observableArrayList();
|
||||||
|
private final ObservableList<DrawableGroup> unmodifiableAnalyzedGroups = FXCollections.unmodifiableObservableList(analyzedGroups);
|
||||||
|
|
||||||
private final ObservableList<DrawableGroup> publicAnalyzedGroupsWrapper = FXCollections.unmodifiableObservableList(analyzedGroups);
|
/** list of unseen groups */
|
||||||
/**
|
|
||||||
* list of unseen groups
|
|
||||||
*/
|
|
||||||
@ThreadConfined(type = ThreadType.JFX)
|
@ThreadConfined(type = ThreadType.JFX)
|
||||||
private final ObservableList<DrawableGroup> unSeenGroups = FXCollections.observableArrayList();
|
private final ObservableList<DrawableGroup> unSeenGroups = FXCollections.observableArrayList();
|
||||||
|
private final ObservableList<DrawableGroup> unmodifiableUnSeenGroups = FXCollections.unmodifiableObservableList(unSeenGroups);
|
||||||
// private final SortedList<Grouping> sortedUnSeenGroups = new SortedList<>(unSeenGroups);
|
|
||||||
private final ObservableList<DrawableGroup> publicSortedUnseenGroupsWrapper = FXCollections.unmodifiableObservableList(unSeenGroups);
|
|
||||||
|
|
||||||
private ReGroupTask<?> groupByTask;
|
private ReGroupTask<?> groupByTask;
|
||||||
|
|
||||||
@ -109,21 +121,22 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
private volatile DrawableAttribute<?> groupBy = DrawableAttribute.PATH;
|
private volatile DrawableAttribute<?> groupBy = DrawableAttribute.PATH;
|
||||||
|
|
||||||
private volatile SortOrder sortOrder = SortOrder.ASCENDING;
|
private volatile SortOrder sortOrder = SortOrder.ASCENDING;
|
||||||
private ReadOnlyDoubleWrapper regroupProgress = new ReadOnlyDoubleWrapper();
|
private final ReadOnlyDoubleWrapper regroupProgress = new ReadOnlyDoubleWrapper();
|
||||||
|
|
||||||
public void setDB(DrawableDB db) {
|
public void setDB(DrawableDB db) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
db.addUpdatedFileListener(this);
|
|
||||||
regroup(groupBy, sortBy, sortOrder, Boolean.TRUE);
|
regroup(groupBy, sortBy, sortOrder, Boolean.TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReturnOfCollectionOrArrayField")
|
||||||
public ObservableList<DrawableGroup> getAnalyzedGroups() {
|
public ObservableList<DrawableGroup> getAnalyzedGroups() {
|
||||||
return publicAnalyzedGroupsWrapper;
|
return unmodifiableAnalyzedGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ThreadConfined(type = ThreadType.JFX)
|
@ThreadConfined(type = ThreadType.JFX)
|
||||||
|
@SuppressWarnings("ReturnOfCollectionOrArrayField")
|
||||||
public ObservableList<DrawableGroup> getUnSeenGroups() {
|
public ObservableList<DrawableGroup> getUnSeenGroups() {
|
||||||
return publicSortedUnseenGroupsWrapper;
|
return unmodifiableUnSeenGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,7 +164,7 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
Set<GroupKey<?>> resultSet = new HashSet<>();
|
Set<GroupKey<?>> resultSet = new HashSet<>();
|
||||||
for (Comparable<?> val : groupBy.getValue(file)) {
|
for (Comparable<?> val : groupBy.getValue(file)) {
|
||||||
if (groupBy == DrawableAttribute.TAGS) {
|
if (groupBy == DrawableAttribute.TAGS) {
|
||||||
if (((TagName) val).getDisplayName().startsWith(Category.CATEGORY_PREFIX) == false) {
|
if (CategoryManager.isNotCategoryTagName((TagName) val)) {
|
||||||
resultSet.add(new GroupKey(groupBy, val));
|
resultSet.add(new GroupKey(groupBy, val));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -187,7 +200,8 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
* or
|
* or
|
||||||
* null if no group exists for that key.
|
* null if no group exists for that key.
|
||||||
*/
|
*/
|
||||||
public DrawableGroup getGroupForKey(GroupKey<?> groupKey) {
|
@Nullable
|
||||||
|
public DrawableGroup getGroupForKey(@Nonnull GroupKey<?> groupKey) {
|
||||||
synchronized (groupMap) {
|
synchronized (groupMap) {
|
||||||
return groupMap.get(groupKey);
|
return groupMap.get(groupKey);
|
||||||
}
|
}
|
||||||
@ -231,46 +245,22 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* make and return a new group with the given key and files. If a group
|
* 'mark' the given group as seen. This removes it from the queue of
|
||||||
* already existed for that key, it will be replaced.
|
* groups to review, and is persisted in the drawable db.
|
||||||
*
|
|
||||||
* NOTE: this is the only API for making a new group.
|
|
||||||
*
|
|
||||||
* @param groupKey the groupKey that uniquely identifies this group
|
|
||||||
* @param files a list of fileids that are members of this group
|
|
||||||
*
|
|
||||||
* @return the new DrawableGroup for the given key
|
|
||||||
*/
|
|
||||||
public DrawableGroup makeGroup(GroupKey<?> groupKey, List<Long> files) {
|
|
||||||
List<Long> newFiles = files == null ? new ArrayList<>() : files;
|
|
||||||
final boolean groupSeen = db.isGroupSeen(groupKey);
|
|
||||||
DrawableGroup g = new DrawableGroup(groupKey, newFiles, groupSeen);
|
|
||||||
|
|
||||||
g.seenProperty().addListener((observable, oldSeen, newSeen) -> {
|
|
||||||
markGroupSeen(g, newSeen);
|
|
||||||
});
|
|
||||||
synchronized (groupMap) {
|
|
||||||
groupMap.put(groupKey, g);
|
|
||||||
}
|
|
||||||
return g;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 'mark' the given group as seen. This removes it from the queue of groups
|
|
||||||
* to review, and is persisted in the drawable db.
|
|
||||||
*
|
*
|
||||||
* @param group the {@link DrawableGroup} to mark as seen
|
* @param group the {@link DrawableGroup} to mark as seen
|
||||||
*/
|
*/
|
||||||
@ThreadConfined(type = ThreadType.JFX)
|
@ThreadConfined(type = ThreadType.JFX)
|
||||||
public void markGroupSeen(DrawableGroup group, boolean seen) {
|
public void markGroupSeen(DrawableGroup group, boolean seen) {
|
||||||
|
|
||||||
db.markGroupSeen(group.getGroupKey(), seen);
|
db.markGroupSeen(group.getGroupKey(), seen);
|
||||||
group.setSeen(seen);
|
group.setSeen(seen);
|
||||||
if (seen) {
|
if (seen) {
|
||||||
unSeenGroups.removeAll(group);
|
unSeenGroups.removeAll(group);
|
||||||
} else if (unSeenGroups.contains(group) == false) {
|
} else if (unSeenGroups.contains(group) == false) {
|
||||||
unSeenGroups.add(group);
|
unSeenGroups.add(group);
|
||||||
FXCollections.sort(unSeenGroups, sortBy.getGrpComparator(sortOrder));
|
|
||||||
}
|
}
|
||||||
|
FXCollections.sort(unSeenGroups, sortBy.getGrpComparator(sortOrder));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -281,91 +271,35 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
* @param groupKey the value of groupKey
|
* @param groupKey the value of groupKey
|
||||||
* @param fileID the value of file
|
* @param fileID the value of file
|
||||||
*/
|
*/
|
||||||
public synchronized void removeFromGroup(GroupKey<?> groupKey, final Long fileID) {
|
public synchronized DrawableGroup removeFromGroup(GroupKey<?> groupKey, final Long fileID) {
|
||||||
//get grouping this file would be in
|
//get grouping this file would be in
|
||||||
final DrawableGroup group = getGroupForKey(groupKey);
|
final DrawableGroup group = getGroupForKey(groupKey);
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
group.removeFile(fileID);
|
group.removeFile(fileID);
|
||||||
|
});
|
||||||
|
|
||||||
// If we're grouping by category, we don't want to remove empty groups.
|
// If we're grouping by category, we don't want to remove empty groups.
|
||||||
if (groupKey.getAttribute() != DrawableAttribute.CATEGORY) {
|
if (groupKey.getAttribute() != DrawableAttribute.CATEGORY) {
|
||||||
if (group.fileIds().isEmpty()) {
|
if (group.fileIds().isEmpty()) {
|
||||||
synchronized (groupMap) {
|
|
||||||
groupMap.remove(groupKey, group);
|
|
||||||
}
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
|
if (analyzedGroups.contains(group)) {
|
||||||
analyzedGroups.remove(group);
|
analyzedGroups.remove(group);
|
||||||
|
FXCollections.sort(analyzedGroups, sortBy.getGrpComparator(sortOrder));
|
||||||
|
}
|
||||||
|
if (unSeenGroups.contains(group)) {
|
||||||
unSeenGroups.remove(group);
|
unSeenGroups.remove(group);
|
||||||
|
FXCollections.sort(unSeenGroups, sortBy.getGrpComparator(sortOrder));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else { //group == null
|
||||||
|
// It may be that this was the last unanalyzed file in the group, so test
|
||||||
|
// whether the group is now fully analyzed.
|
||||||
|
popuplateIfAnalyzed(groupKey, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return group;
|
||||||
|
|
||||||
public synchronized void populateAnalyzedGroup(final GroupKey<?> groupKey, List<Long> filesInGroup) {
|
|
||||||
populateAnalyzedGroup(groupKey, filesInGroup, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create a group with the given GroupKey and file ids and add it to the
|
|
||||||
* analyzed group list.
|
|
||||||
*
|
|
||||||
* @param groupKey
|
|
||||||
* @param filesInGroup
|
|
||||||
*/
|
|
||||||
private synchronized <A extends Comparable<A>> void populateAnalyzedGroup(final GroupKey<A> groupKey, List<Long> filesInGroup, ReGroupTask<A> task) {
|
|
||||||
|
|
||||||
/* if this is not part of a regroup task or it is but the task is not
|
|
||||||
* cancelled...
|
|
||||||
*
|
|
||||||
* this allows us to stop if a regroup task has been cancelled (e.g. the
|
|
||||||
* user picked a different group by attribute, while the current task
|
|
||||||
* was still running) */
|
|
||||||
if (task == null || (task.isCancelled() == false)) {
|
|
||||||
DrawableGroup g = makeGroup(groupKey, filesInGroup);
|
|
||||||
populateAnalyzedGroup(g, task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void populateAnalyzedGroup(final DrawableGroup g, ReGroupTask<?> task) {
|
|
||||||
|
|
||||||
if (task == null || (task.isCancelled() == false)) {
|
|
||||||
final boolean groupSeen = db.isGroupSeen(g.getGroupKey());
|
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
if (analyzedGroups.contains(g) == false) {
|
|
||||||
analyzedGroups.add(g);
|
|
||||||
}
|
|
||||||
markGroupSeen(g, groupSeen);
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* check if the group for the given groupkey is analyzed
|
|
||||||
*
|
|
||||||
* @param groupKey
|
|
||||||
*
|
|
||||||
* @return null if this group is not analyzed or a list of file ids in
|
|
||||||
* this
|
|
||||||
* group if they are all analyzed
|
|
||||||
*/
|
|
||||||
public List<Long> checkAnalyzed(final GroupKey<?> groupKey) {
|
|
||||||
try {
|
|
||||||
/* for attributes other than path we can't be sure a group is fully
|
|
||||||
* analyzed because we don't know all the files that will be a part
|
|
||||||
* of that group */
|
|
||||||
if ((groupKey.getAttribute() != DrawableAttribute.PATH) || db.isGroupAnalyzed(groupKey)) {
|
|
||||||
return getFileIDsInGroup(groupKey);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, "failed to get files for group: " + groupKey.getAttribute().attrName.toString() + " = " + groupKey.getValue(), ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -440,8 +374,8 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
values = (List<A>) Arrays.asList(Category.values());
|
values = (List<A>) Arrays.asList(Category.values());
|
||||||
break;
|
break;
|
||||||
case TAGS:
|
case TAGS:
|
||||||
values = (List<A>) Case.getCurrentCase().getServices().getTagsManager().getTagNamesInUse().stream()
|
values = (List<A>) controller.getTagsManager().getTagNamesInUse().stream()
|
||||||
.filter(t -> t.getDisplayName().startsWith(Category.CATEGORY_PREFIX) == false)
|
.filter(CategoryManager::isNotCategoryTagName)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
break;
|
break;
|
||||||
case ANALYZED:
|
case ANALYZED:
|
||||||
@ -464,7 +398,7 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Long> getFileIDsInGroup(GroupKey<?> groupKey) throws TskCoreException {
|
public Set<Long> getFileIDsInGroup(GroupKey<?> groupKey) throws TskCoreException {
|
||||||
switch (groupKey.getAttribute().attrName) {
|
switch (groupKey.getAttribute().attrName) {
|
||||||
//these cases get special treatment
|
//these cases get special treatment
|
||||||
case CATEGORY:
|
case CATEGORY:
|
||||||
@ -481,34 +415,37 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
|
|
||||||
// @@@ This was kind of slow in the profiler. Maybe we should cache it.
|
// @@@ This was kind of slow in the profiler. Maybe we should cache it.
|
||||||
// Unless the list of file IDs is necessary, use countFilesWithCategory() to get the counts.
|
// Unless the list of file IDs is necessary, use countFilesWithCategory() to get the counts.
|
||||||
public List<Long> getFileIDsWithCategory(Category category) throws TskCoreException {
|
public Set<Long> getFileIDsWithCategory(Category category) throws TskCoreException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
final DrawableTagsManager tagsManager = controller.getTagsManager();
|
||||||
if (category == Category.ZERO) {
|
if (category == Category.ZERO) {
|
||||||
|
List< TagName> tns = Stream.of(Category.ONE, Category.TWO, Category.THREE, Category.FOUR, Category.FIVE)
|
||||||
|
.map(tagsManager::getTagName)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
List<Long> files = new ArrayList<>();
|
Set<Long> files = new HashSet<>();
|
||||||
TagName[] tns = {Category.FOUR.getTagName(), Category.THREE.getTagName(), Category.TWO.getTagName(), Category.ONE.getTagName(), Category.FIVE.getTagName()};
|
|
||||||
for (TagName tn : tns) {
|
for (TagName tn : tns) {
|
||||||
List<ContentTag> contentTags = Case.getCurrentCase().getServices().getTagsManager().getContentTagsByTagName(tn);
|
if (tn != null) {
|
||||||
for (ContentTag ct : contentTags) {
|
List<ContentTag> contentTags = tagsManager.getContentTagsByTagName(tn);
|
||||||
if (ct.getContent() instanceof AbstractFile && db.isInDB(ct.getContent().getId())) {
|
files.addAll(contentTags.stream()
|
||||||
files.add(ct.getContent().getId());
|
.filter(ct -> ct.getContent() instanceof AbstractFile)
|
||||||
}
|
.filter(ct -> db.isInDB(ct.getContent().getId()))
|
||||||
|
.map(ct -> ct.getContent().getId())
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.findAllFileIdsWhere("obj_id NOT IN (" + StringUtils.join(files, ',') + ")");
|
return db.findAllFileIdsWhere("obj_id NOT IN (" + StringUtils.join(files, ',') + ")");
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
List<Long> files = new ArrayList<>();
|
List<ContentTag> contentTags = tagsManager.getContentTagsByTagName(tagsManager.getTagName(category));
|
||||||
List<ContentTag> contentTags = Case.getCurrentCase().getServices().getTagsManager().getContentTagsByTagName(category.getTagName());
|
return contentTags.stream()
|
||||||
for (ContentTag ct : contentTags) {
|
.filter(ct -> ct.getContent() instanceof AbstractFile)
|
||||||
if (ct.getContent() instanceof AbstractFile && db.isInDB(ct.getContent().getId())) {
|
.filter(ct -> db.isInDB(ct.getContent().getId()))
|
||||||
files.add(ct.getContent().getId());
|
.map(ct -> ct.getContent().getId())
|
||||||
}
|
.collect(Collectors.toSet());
|
||||||
}
|
|
||||||
|
|
||||||
return files;
|
|
||||||
}
|
}
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
LOGGER.log(Level.WARNING, "TSK error getting files in Category:" + category.getDisplayName(), ex);
|
LOGGER.log(Level.WARNING, "TSK error getting files in Category:" + category.getDisplayName(), ex);
|
||||||
@ -516,17 +453,15 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Long> getFileIDsWithTag(TagName tagName) throws TskCoreException {
|
public Set<Long> getFileIDsWithTag(TagName tagName) throws TskCoreException {
|
||||||
try {
|
try {
|
||||||
List<Long> files = new ArrayList<>();
|
Set<Long> files = new HashSet<>();
|
||||||
List<ContentTag> contentTags = Case.getCurrentCase().getServices().getTagsManager().getContentTagsByTagName(tagName);
|
List<ContentTag> contentTags = controller.getTagsManager().getContentTagsByTagName(tagName);
|
||||||
for (ContentTag ct : contentTags) {
|
for (ContentTag ct : contentTags) {
|
||||||
if (ct.getContent() instanceof AbstractFile && db.isInDB(ct.getContent().getId())) {
|
if (ct.getContent() instanceof AbstractFile && db.isInDB(ct.getContent().getId())) {
|
||||||
|
|
||||||
files.add(ct.getContent().getId());
|
files.add(ct.getContent().getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return files;
|
return files;
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
LOGGER.log(Level.WARNING, "TSK error getting files with Tag:" + tagName.getDisplayName(), ex);
|
LOGGER.log(Level.WARNING, "TSK error getting files with Tag:" + tagName.getDisplayName(), ex);
|
||||||
@ -567,7 +502,7 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
* @param sortOrder
|
* @param sortOrder
|
||||||
* @param force true to force a full db query regroup
|
* @param force true to force a full db query regroup
|
||||||
*/
|
*/
|
||||||
public <A extends Comparable<A>> void regroup(final DrawableAttribute<A> groupBy, final GroupSortBy sortBy, final SortOrder sortOrder, Boolean force) {
|
public synchronized <A extends Comparable<A>> void regroup(final DrawableAttribute<A> groupBy, final GroupSortBy sortBy, final SortOrder sortOrder, Boolean force) {
|
||||||
|
|
||||||
if (!Case.isCaseOpen()) {
|
if (!Case.isCaseOpen()) {
|
||||||
return;
|
return;
|
||||||
@ -581,9 +516,6 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
if (groupByTask != null) {
|
if (groupByTask != null) {
|
||||||
groupByTask.cancel(true);
|
groupByTask.cancel(true);
|
||||||
}
|
}
|
||||||
Platform.runLater(() -> {
|
|
||||||
FXCollections.sort(unSeenGroups, sortBy.getGrpComparator(sortOrder));
|
|
||||||
});
|
|
||||||
|
|
||||||
groupByTask = new ReGroupTask<A>(groupBy, sortBy, sortOrder);
|
groupByTask = new ReGroupTask<A>(groupBy, sortBy, sortOrder);
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
@ -591,12 +523,12 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
});
|
});
|
||||||
regroupExecutor.submit(groupByTask);
|
regroupExecutor.submit(groupByTask);
|
||||||
} else {
|
} else {
|
||||||
// just resort the list of groups
|
// resort the list of groups
|
||||||
setSortBy(sortBy);
|
setSortBy(sortBy);
|
||||||
setSortOrder(sortOrder);
|
setSortOrder(sortOrder);
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
FXCollections.sort(unSeenGroups, sortBy.getGrpComparator(sortOrder));
|
|
||||||
FXCollections.sort(analyzedGroups, sortBy.getGrpComparator(sortOrder));
|
FXCollections.sort(analyzedGroups, sortBy.getGrpComparator(sortOrder));
|
||||||
|
FXCollections.sort(unSeenGroups, sortBy.getGrpComparator(sortOrder));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -610,86 +542,149 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
return regroupProgress.getReadOnlyProperty();
|
return regroupProgress.getReadOnlyProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Subscribe
|
||||||
* handle {@link FileUpdateEvent} sent from Db when files are
|
public void handleTagAdded(ContentTagAddedEvent evt) {
|
||||||
* inserted/updated
|
GroupKey<?> newGroupKey = null;
|
||||||
*
|
final long fileID = evt.getTag().getContent().getId();
|
||||||
* TODO: why isn't this just two methods!
|
if (groupBy == DrawableAttribute.CATEGORY && CategoryManager.isCategoryTagName(evt.getTag().getName())) {
|
||||||
*
|
newGroupKey = new GroupKey<Category>(DrawableAttribute.CATEGORY, CategoryManager.categoryFromTagName(evt.getTag().getName()));
|
||||||
* @param evt
|
for (GroupKey<?> oldGroupKey : groupMap.keySet()) {
|
||||||
*/
|
if (oldGroupKey.equals(newGroupKey) == false) {
|
||||||
@Override
|
removeFromGroup(oldGroupKey, fileID);
|
||||||
synchronized public void handleFileUpdate(FileUpdateEvent evt) {
|
}
|
||||||
final Collection<Long> fileIDs = evt.getFileIDs();
|
}
|
||||||
switch (evt.getUpdateType()) {
|
} else if (groupBy == DrawableAttribute.TAGS && CategoryManager.isNotCategoryTagName(evt.getTag().getName())) {
|
||||||
case REMOVE:
|
newGroupKey = new GroupKey<TagName>(DrawableAttribute.TAGS, evt.getTag().getName());
|
||||||
for (final long fileId : fileIDs) {
|
}
|
||||||
|
if (newGroupKey != null) {
|
||||||
|
DrawableGroup g = getGroupForKey(newGroupKey);
|
||||||
|
addFileToGroup(g, newGroupKey, fileID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("AssignmentToMethodParameter")
|
||||||
|
private void addFileToGroup(DrawableGroup g, final GroupKey<?> groupKey, final long fileID) {
|
||||||
|
if (g == null) {
|
||||||
|
//if there wasn't already a group check if there should be one now
|
||||||
|
g = popuplateIfAnalyzed(groupKey, null);
|
||||||
|
}
|
||||||
|
DrawableGroup group = g;
|
||||||
|
if (group != null) {
|
||||||
|
//if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it.
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
group.addFile(fileID);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void handleTagDeleted(ContentTagDeletedEvent evt) {
|
||||||
|
GroupKey<?> groupKey = null;
|
||||||
|
if (groupBy == DrawableAttribute.CATEGORY && CategoryManager.isCategoryTagName(evt.getTag().getName())) {
|
||||||
|
groupKey = new GroupKey<Category>(DrawableAttribute.CATEGORY, CategoryManager.categoryFromTagName(evt.getTag().getName()));
|
||||||
|
} else if (groupBy == DrawableAttribute.TAGS && CategoryManager.isNotCategoryTagName(evt.getTag().getName())) {
|
||||||
|
groupKey = new GroupKey<TagName>(DrawableAttribute.TAGS, evt.getTag().getName());
|
||||||
|
}
|
||||||
|
if (groupKey != null) {
|
||||||
|
final long fileID = evt.getTag().getContent().getId();
|
||||||
|
DrawableGroup g = removeFromGroup(groupKey, fileID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
synchronized public void handleFileRemoved(Collection<Long> removedFileIDs) {
|
||||||
|
|
||||||
|
for (final long fileId : removedFileIDs) {
|
||||||
//get grouping(s) this file would be in
|
//get grouping(s) this file would be in
|
||||||
Set<GroupKey<?>> groupsForFile = getGroupKeysForFileID(fileId);
|
Set<GroupKey<?>> groupsForFile = getGroupKeysForFileID(fileId);
|
||||||
|
|
||||||
for (GroupKey<?> gk : groupsForFile) {
|
for (GroupKey<?> gk : groupsForFile) {
|
||||||
removeFromGroup(gk, fileId);
|
removeFromGroup(gk, fileId);
|
||||||
|
|
||||||
DrawableGroup g = getGroupForKey(gk);
|
|
||||||
|
|
||||||
if (g == null) {
|
|
||||||
// It may be that this was the last unanalyzed file in the group, so test
|
|
||||||
// whether the group is now fully analyzed.
|
|
||||||
//TODO: use method in groupmanager ?
|
|
||||||
List<Long> checkAnalyzed = checkAnalyzed(gk);
|
|
||||||
if (checkAnalyzed != null) { // => the group is analyzed, so add it to the ui
|
|
||||||
populateAnalyzedGroup(gk, checkAnalyzed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
/**
|
||||||
case UPDATE:
|
* handle {@link FileUpdateEvent} sent from Db when files are
|
||||||
|
* inserted/updated
|
||||||
|
*
|
||||||
|
* @param evt
|
||||||
|
*/
|
||||||
|
@Subscribe
|
||||||
|
synchronized public void handleFileUpdate(Collection<Long> updatedFileIDs) {
|
||||||
/**
|
/**
|
||||||
* TODO: is there a way to optimize this to avoid quering to db
|
* TODO: is there a way to optimize this to avoid quering to db
|
||||||
* so much. the problem is that as a new files are analyzed they
|
* so much. the problem is that as a new files are analyzed they
|
||||||
* might be in new groups( if we are grouping by say make or
|
* might be in new groups( if we are grouping by say make or
|
||||||
* model)
|
* model) -jm
|
||||||
*
|
|
||||||
* TODO: Should this be a InnerTask so it can be done by the
|
|
||||||
* WorkerThread? Is it already done by worker thread because
|
|
||||||
* handlefileUpdate is invoked through call on db in UpdateTask
|
|
||||||
* innertask? -jm
|
|
||||||
*/
|
*/
|
||||||
for (final long fileId : fileIDs) {
|
for (long fileId : updatedFileIDs) {
|
||||||
|
|
||||||
controller.getHashSetManager().invalidateHashSetsForFile(fileId);
|
controller.getHashSetManager().invalidateHashSetsForFile(fileId);
|
||||||
|
|
||||||
//get grouping(s) this file would be in
|
//get grouping(s) this file would be in
|
||||||
Set<GroupKey<?>> groupsForFile = getGroupKeysForFileID(fileId);
|
Set<GroupKey<?>> groupsForFile = getGroupKeysForFileID(fileId);
|
||||||
|
|
||||||
for (GroupKey<?> gk : groupsForFile) {
|
for (GroupKey<?> gk : groupsForFile) {
|
||||||
DrawableGroup g = getGroupForKey(gk);
|
DrawableGroup g = getGroupForKey(gk);
|
||||||
|
addFileToGroup(g, gk, fileId);
|
||||||
if (g != null) {
|
|
||||||
//if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it.
|
|
||||||
g.addFile(fileId);
|
|
||||||
} else {
|
|
||||||
//if there wasn't already a group check if there should be one now
|
|
||||||
//TODO: use method in groupmanager ?
|
|
||||||
List<Long> checkAnalyzed = checkAnalyzed(gk);
|
|
||||||
if (checkAnalyzed != null) { // => the group is analyzed, so add it to the ui
|
|
||||||
populateAnalyzedGroup(gk, checkAnalyzed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//we fire this event for all files so that the category counts get updated during initial db population
|
//we fire this event for all files so that the category counts get updated during initial db population
|
||||||
controller.getCategoryManager().fireChange(fileIDs);
|
controller.getCategoryManager().fireChange(updatedFileIDs, null);
|
||||||
|
}
|
||||||
|
|
||||||
if (evt.getChangedAttribute() == DrawableAttribute.TAGS) {
|
private DrawableGroup popuplateIfAnalyzed(GroupKey<?> groupKey, ReGroupTask<?> task) {
|
||||||
TagUtils.fireChange(fileIDs);
|
|
||||||
|
if (Objects.nonNull(task) && (task.isCancelled())) {
|
||||||
|
/* if this method call is part of a ReGroupTask and that task is
|
||||||
|
* cancelled, no-op
|
||||||
|
*
|
||||||
|
* this allows us to stop if a regroup task has been cancelled (e.g.
|
||||||
|
* the user picked a different group by attribute, while the
|
||||||
|
* current task was still running) */
|
||||||
|
|
||||||
|
} else { // no task or un-cancelled task
|
||||||
|
if ((groupKey.getAttribute() != DrawableAttribute.PATH) || db.isGroupAnalyzed(groupKey)) {
|
||||||
|
/* for attributes other than path we can't be sure a group is
|
||||||
|
* fully analyzed because we don't know all the files that
|
||||||
|
* will be a part of that group,. just show them no matter what. */
|
||||||
|
|
||||||
|
try {
|
||||||
|
Set<Long> fileIDs = getFileIDsInGroup(groupKey);
|
||||||
|
if (Objects.nonNull(fileIDs)) {
|
||||||
|
DrawableGroup group;
|
||||||
|
final boolean groupSeen = db.isGroupSeen(groupKey);
|
||||||
|
synchronized (groupMap) {
|
||||||
|
if (groupMap.containsKey(groupKey)) {
|
||||||
|
group = groupMap.get(groupKey);
|
||||||
|
group.setFiles(ObjectUtils.defaultIfNull(fileIDs, Collections.emptySet()));
|
||||||
|
} else {
|
||||||
|
group = new DrawableGroup(groupKey, fileIDs, groupSeen);
|
||||||
|
group.seenProperty().addListener((o, oldSeen, newSeen) -> {
|
||||||
|
markGroupSeen(group, newSeen);
|
||||||
|
});
|
||||||
|
groupMap.put(groupKey, group);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (analyzedGroups.contains(group) == false) {
|
||||||
|
analyzedGroups.add(group);
|
||||||
|
if (Objects.isNull(task)) {
|
||||||
|
FXCollections.sort(analyzedGroups, sortBy.getGrpComparator(sortOrder));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
markGroupSeen(group, groupSeen);
|
||||||
|
});
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
LOGGER.log(Level.SEVERE, "failed to get files for group: " + groupKey.getAttribute().attrName.toString() + " = " + groupKey.getValue(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -730,20 +725,12 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
groupProgress = ProgressHandleFactory.createHandle("regrouping files by " + groupBy.attrName.toString() + " sorted by " + sortBy.name() + " in " + sortOrder.toString() + " order", this);
|
groupProgress = ProgressHandleFactory.createHandle("regrouping files by " + groupBy.attrName.toString() + " sorted by " + sortBy.name() + " in " + sortOrder.toString() + " order", this);
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
analyzedGroups.clear();
|
analyzedGroups.clear();
|
||||||
synchronized (unSeenGroups) {
|
|
||||||
unSeenGroups.clear();
|
unSeenGroups.clear();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
synchronized (groupMap) {
|
|
||||||
groupMap.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the list of group keys
|
// Get the list of group keys
|
||||||
final List<A> vals = findValuesForAttribute(groupBy);
|
final List<A> vals = findValuesForAttribute(groupBy);
|
||||||
|
|
||||||
// Make a list of each group
|
|
||||||
final List<DrawableGroup> groups = new ArrayList<>();
|
|
||||||
|
|
||||||
groupProgress.start(vals.size());
|
groupProgress.start(vals.size());
|
||||||
|
|
||||||
int p = 0;
|
int p = 0;
|
||||||
@ -756,27 +743,9 @@ public class GroupManager implements FileUpdateEvent.FileUpdateListener {
|
|||||||
updateMessage("regrouping files by " + groupBy.attrName.toString() + " : " + val);
|
updateMessage("regrouping files by " + groupBy.attrName.toString() + " : " + val);
|
||||||
updateProgress(p, vals.size());
|
updateProgress(p, vals.size());
|
||||||
groupProgress.progress("regrouping files by " + groupBy.attrName.toString() + " : " + val, p);
|
groupProgress.progress("regrouping files by " + groupBy.attrName.toString() + " : " + val, p);
|
||||||
//check if this group is analyzed
|
popuplateIfAnalyzed(new GroupKey<A>(groupBy, val), this);
|
||||||
final GroupKey<A> groupKey = new GroupKey<>(groupBy, val);
|
|
||||||
|
|
||||||
List<Long> checkAnalyzed = checkAnalyzed(groupKey);
|
|
||||||
if (checkAnalyzed != null) { // != null => the group is analyzed, so add it to the ui
|
|
||||||
|
|
||||||
// makeGroup will create the group and add it to the map groupMap, but does not
|
|
||||||
// update anything else
|
|
||||||
DrawableGroup g = makeGroup(groupKey, checkAnalyzed);
|
|
||||||
groups.add(g);
|
|
||||||
}
|
}
|
||||||
}
|
FXCollections.sort(analyzedGroups, sortBy.getGrpComparator(sortOrder));
|
||||||
|
|
||||||
// Sort the group list
|
|
||||||
Collections.sort(groups, sortBy.getGrpComparator(sortOrder));
|
|
||||||
|
|
||||||
// Officially add all groups in order
|
|
||||||
for (DrawableGroup g : groups) {
|
|
||||||
populateAnalyzedGroup(g, ReGroupTask.this);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProgress(1, 1);
|
updateProgress(1, 1);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
@ -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.imagegallery.grouping;
|
package org.sleuthkit.autopsy.imagegallery.datamodel.grouping;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
@ -0,0 +1,7 @@
|
|||||||
|
package org.sleuthkit.autopsy.imagegallery.datamodel.grouping;
|
||||||
|
|
||||||
|
public enum GroupViewMode {
|
||||||
|
|
||||||
|
TILE, SLIDE_SHOW
|
||||||
|
|
||||||
|
}
|
@ -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.imagegallery.grouping;
|
package org.sleuthkit.autopsy.imagegallery.datamodel.grouping;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
@ -1,7 +0,0 @@
|
|||||||
package org.sleuthkit.autopsy.imagegallery.grouping;
|
|
||||||
|
|
||||||
public enum GroupViewMode {
|
|
||||||
|
|
||||||
TILE, SLIDE_SHOW
|
|
||||||
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<?import java.lang.*?>
|
|
||||||
<?import javafx.geometry.*?>
|
|
||||||
<?import javafx.scene.control.*?>
|
|
||||||
<?import javafx.scene.image.*?>
|
|
||||||
<?import javafx.scene.layout.*?>
|
|
||||||
|
|
||||||
<fx:root maxHeight="-1.0" maxWidth="-1.0" minHeight="-Infinity" minWidth="-Infinity" opacity="1.0" prefHeight="-1.0" prefWidth="-1.0" style="-fx-background-color: linear-gradient(to bottom, derive(-fx-base,-30%), derive(-fx-base,-60%)), linear-gradient(to bottom, derive(-fx-base,65%) 2%, derive(-fx-base,-20%) 95%); -fx-background-radius: 2;" type="javafx.scene.layout.AnchorPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
|
|
||||||
<children>
|
|
||||||
<BorderPane maxHeight="-1.0" maxWidth="-1.0" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" snapToPixel="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
|
||||||
<bottom>
|
|
||||||
<BorderPane maxHeight="-Infinity" maxWidth="-1.0" minHeight="-Infinity" minWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0" BorderPane.alignment="CENTER">
|
|
||||||
<center>
|
|
||||||
<Label id="pathLabel" fx:id="nameLabel" alignment="CENTER" contentDisplay="TEXT_ONLY" maxHeight="16.0" minHeight="16.0" minWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0" text="file name" textAlignment="CENTER">
|
|
||||||
<labelFor>
|
|
||||||
<ImageView fx:id="imageView" fitHeight="200.0" fitWidth="200.0" opacity="1.0" pickOnBounds="true" preserveRatio="true" style="-fx-border-radius : 5; -fx-border-width : 5; -fx-border-color : blue;" BorderPane.alignment="CENTER" />
|
|
||||||
</labelFor>
|
|
||||||
</Label>
|
|
||||||
</center>
|
|
||||||
<left>
|
|
||||||
<HBox maxHeight="-Infinity" prefHeight="-1.0" prefWidth="-1.0" spacing="2.0" BorderPane.alignment="CENTER_LEFT">
|
|
||||||
<children>
|
|
||||||
<ImageView fx:id="fileTypeImageView" fitHeight="16.0" fitWidth="16.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="true" scaleX="1.0" scaleY="1.0">
|
|
||||||
<image>
|
|
||||||
<Image url="@../images/video-file.png" />
|
|
||||||
</image>
|
|
||||||
</ImageView>
|
|
||||||
<ImageView fx:id="hashHitImageView" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true" style="">
|
|
||||||
<image>
|
|
||||||
<Image url="@../images/hashset_hits.png" />
|
|
||||||
</image>
|
|
||||||
<HBox.margin>
|
|
||||||
<Insets bottom="1.0" left="1.0" right="1.0" top="1.0" fx:id="x1" />
|
|
||||||
</HBox.margin>
|
|
||||||
</ImageView>
|
|
||||||
</children>
|
|
||||||
<padding>
|
|
||||||
<Insets bottom="2.0" right="2.0" top="2.0" />
|
|
||||||
</padding>
|
|
||||||
</HBox>
|
|
||||||
</left>
|
|
||||||
<right>
|
|
||||||
<ToggleButton fx:id="followUpToggle" minWidth="24.0" mnemonicParsing="false" prefWidth="24.0" selected="false" text="">
|
|
||||||
<graphic>
|
|
||||||
<ImageView id="followUpImageview" fx:id="followUpImageView" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
|
||||||
<image>
|
|
||||||
<Image url="@../images/flag_gray.png" />
|
|
||||||
</image>
|
|
||||||
</ImageView>
|
|
||||||
</graphic>
|
|
||||||
</ToggleButton>
|
|
||||||
</right>
|
|
||||||
</BorderPane>
|
|
||||||
</bottom>
|
|
||||||
<center>
|
|
||||||
<BorderPane fx:id="imageBorder" center="$imageView" maxHeight="-1.0" maxWidth="-1.0" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" BorderPane.alignment="CENTER">
|
|
||||||
<center><ImageView fx:id="imageView" fitHeight="100.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER" />
|
|
||||||
</center></BorderPane>
|
|
||||||
</center>
|
|
||||||
</BorderPane>
|
|
||||||
</children>
|
|
||||||
<padding>
|
|
||||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
|
||||||
</padding>
|
|
||||||
</fx:root>
|
|
@ -1,8 +0,0 @@
|
|||||||
|
|
||||||
package org.sleuthkit.autopsy.imagegallery.gui;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public interface GroupView {
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2015 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.imagegallery.gui;
|
||||||
|
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
import javafx.scene.control.SplitMenuButton;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.actions.AddDrawableTagAction;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
|
import org.sleuthkit.datamodel.TagName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static utility methods for working with GUI components
|
||||||
|
*/
|
||||||
|
public class GuiUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make a new menu item that when clicked, tags the selected files with the
|
||||||
|
* given tagname
|
||||||
|
*
|
||||||
|
* @param tagName
|
||||||
|
* @param tagSelectedMenuButton
|
||||||
|
* @param controller
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static MenuItem createSelTagMenuItem(final TagName tagName, final SplitMenuButton tagSelectedMenuButton, ImageGalleryController controller) {
|
||||||
|
final MenuItem menuItem = new MenuItem(tagName.getDisplayName(), new ImageView(DrawableAttribute.TAGS.getIcon()));
|
||||||
|
menuItem.setOnAction(new EventHandler<ActionEvent>() {
|
||||||
|
@Override
|
||||||
|
public void handle(ActionEvent t) {
|
||||||
|
new AddDrawableTagAction(controller).addTag(tagName, "");
|
||||||
|
tagSelectedMenuButton.setText(tagName.getDisplayName());
|
||||||
|
tagSelectedMenuButton.setOnAction(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return menuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GuiUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -20,7 +20,7 @@ package org.sleuthkit.autopsy.imagegallery.gui;
|
|||||||
|
|
||||||
import javafx.scene.control.ListCell;
|
import javafx.scene.control.ListCell;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupSortBy;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupSortBy;
|
||||||
|
|
||||||
public class SortByListCell extends ListCell<GroupSortBy> {
|
public class SortByListCell extends ListCell<GroupSortBy> {
|
||||||
|
|
||||||
|
@ -36,15 +36,12 @@ import org.sleuthkit.autopsy.casemodule.Case;
|
|||||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryChangeEvent;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays summary statistics (counts) for each group
|
* Displays summary statistics (counts) for each group
|
||||||
*/
|
*/
|
||||||
public class SummaryTablePane extends AnchorPane {
|
public class SummaryTablePane extends AnchorPane {
|
||||||
|
|
||||||
private static SummaryTablePane instance;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TableColumn<Pair<Category, Long>, String> catColumn;
|
private TableColumn<Pair<Category, Long>, String> catColumn;
|
||||||
|
|
||||||
@ -53,6 +50,7 @@ public class SummaryTablePane extends AnchorPane {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TableView<Pair<Category, Long>> tableView;
|
private TableView<Pair<Category, Long>> tableView;
|
||||||
|
private final ImageGalleryController controller;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void initialize() {
|
void initialize() {
|
||||||
@ -74,34 +72,26 @@ public class SummaryTablePane extends AnchorPane {
|
|||||||
|
|
||||||
tableView.getColumns().setAll(Arrays.asList(catColumn, countColumn));
|
tableView.getColumns().setAll(Arrays.asList(catColumn, countColumn));
|
||||||
|
|
||||||
// //register for category events
|
//register for category events
|
||||||
ImageGalleryController.getDefault().getCategoryManager().registerListener(this);
|
controller.getCategoryManager().registerListener(this);
|
||||||
|
handleCategoryChanged(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SummaryTablePane() {
|
public SummaryTablePane(ImageGalleryController controller) {
|
||||||
|
this.controller = controller;
|
||||||
FXMLConstructor.construct(this, "SummaryTablePane.fxml");
|
FXMLConstructor.construct(this, "SummaryTablePane.fxml");
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized SummaryTablePane getDefault() {
|
|
||||||
if (instance == null) {
|
|
||||||
instance = new SummaryTablePane();
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* listen to Category updates and rebuild the table
|
* listen to Category updates and rebuild the table
|
||||||
*/
|
*/
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void handleCategoryChanged(CategoryChangeEvent evt) {
|
public void handleCategoryChanged(org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager.CategoryChangeEvent evt) {
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refresh() {
|
|
||||||
final ObservableList<Pair<Category, Long>> data = FXCollections.observableArrayList();
|
final ObservableList<Pair<Category, Long>> data = FXCollections.observableArrayList();
|
||||||
if (Case.isCaseOpen()) {
|
if (Case.isCaseOpen()) {
|
||||||
for (Category cat : Category.values()) {
|
for (Category cat : Category.values()) {
|
||||||
data.add(new Pair<>(cat, ImageGalleryController.getDefault().getCategoryManager().getCategoryCount(cat)));
|
data.add(new Pair<>(cat, controller.getCategoryManager().getCategoryCount(cat)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
|
@ -44,12 +44,11 @@ import org.openide.util.Exceptions;
|
|||||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.TagUtils;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.ThumbnailCache;
|
import org.sleuthkit.autopsy.imagegallery.ThumbnailCache;
|
||||||
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
|
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupSortBy;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupSortBy;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
@ -105,6 +104,7 @@ public class Toolbar extends ToolBar {
|
|||||||
|
|
||||||
ImageGalleryController.getDefault().getGroupManager().regroup(groupByBox.getSelectionModel().getSelectedItem(), sortByBox.getSelectionModel().getSelectedItem(), getSortOrder(), false);
|
ImageGalleryController.getDefault().getGroupManager().regroup(groupByBox.getSelectionModel().getSelectedItem(), sortByBox.getSelectionModel().getSelectedItem(), getSortOrder(), false);
|
||||||
};
|
};
|
||||||
|
private ImageGalleryController controller;
|
||||||
|
|
||||||
synchronized public SortOrder getSortOrder() {
|
synchronized public SortOrder getSortOrder() {
|
||||||
return orderProperty.get();
|
return orderProperty.get();
|
||||||
@ -117,9 +117,9 @@ public class Toolbar extends ToolBar {
|
|||||||
return sizeSlider.valueProperty();
|
return sizeSlider.valueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
static synchronized public Toolbar getDefault() {
|
static synchronized public Toolbar getDefault(ImageGalleryController controller) {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new Toolbar();
|
instance = new Toolbar(controller);
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
@ -151,7 +151,7 @@ public class Toolbar extends ToolBar {
|
|||||||
|
|
||||||
tagSelectedMenuButton.setOnAction((ActionEvent t) -> {
|
tagSelectedMenuButton.setOnAction((ActionEvent t) -> {
|
||||||
try {
|
try {
|
||||||
TagUtils.createSelTagMenuItem(TagUtils.getFollowUpTagName(), tagSelectedMenuButton).getOnAction().handle(t);
|
GuiUtils.createSelTagMenuItem(getController().getTagsManager().getFollowUpTagName(), tagSelectedMenuButton, getController()).getOnAction().handle(t);
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
Exceptions.printStackTrace(ex);
|
Exceptions.printStackTrace(ex);
|
||||||
}
|
}
|
||||||
@ -161,22 +161,22 @@ public class Toolbar extends ToolBar {
|
|||||||
tagSelectedMenuButton.showingProperty().addListener((ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) -> {
|
tagSelectedMenuButton.showingProperty().addListener((ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) -> {
|
||||||
if (t1) {
|
if (t1) {
|
||||||
ArrayList<MenuItem> selTagMenues = new ArrayList<>();
|
ArrayList<MenuItem> selTagMenues = new ArrayList<>();
|
||||||
for (final TagName tn : TagUtils.getNonCategoryTagNames()) {
|
for (final TagName tn : getController().getTagsManager().getNonCategoryTagNames()) {
|
||||||
MenuItem menuItem = TagUtils.createSelTagMenuItem(tn, tagSelectedMenuButton);
|
MenuItem menuItem = GuiUtils.createSelTagMenuItem(tn, tagSelectedMenuButton, getController());
|
||||||
selTagMenues.add(menuItem);
|
selTagMenues.add(menuItem);
|
||||||
}
|
}
|
||||||
tagSelectedMenuButton.getItems().setAll(selTagMenues);
|
tagSelectedMenuButton.getItems().setAll(selTagMenues);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
catSelectedMenuButton.setOnAction(createSelCatMenuItem(Category.FIVE, catSelectedMenuButton).getOnAction());
|
catSelectedMenuButton.setOnAction(createSelCatMenuItem(Category.FIVE, catSelectedMenuButton, getController()).getOnAction());
|
||||||
catSelectedMenuButton.setText(Category.FIVE.getDisplayName());
|
catSelectedMenuButton.setText(Category.FIVE.getDisplayName());
|
||||||
catSelectedMenuButton.setGraphic(new ImageView(DrawableAttribute.CATEGORY.getIcon()));
|
catSelectedMenuButton.setGraphic(new ImageView(DrawableAttribute.CATEGORY.getIcon()));
|
||||||
catSelectedMenuButton.showingProperty().addListener((ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) -> {
|
catSelectedMenuButton.showingProperty().addListener((ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) -> {
|
||||||
if (t1) {
|
if (t1) {
|
||||||
ArrayList<MenuItem> categoryMenues = new ArrayList<>();
|
ArrayList<MenuItem> categoryMenues = new ArrayList<>();
|
||||||
for (final Category cat : Category.values()) {
|
for (final Category cat : Category.values()) {
|
||||||
MenuItem menuItem = createSelCatMenuItem(cat, catSelectedMenuButton);
|
MenuItem menuItem = createSelCatMenuItem(cat, catSelectedMenuButton, getController());
|
||||||
categoryMenues.add(menuItem);
|
categoryMenues.add(menuItem);
|
||||||
}
|
}
|
||||||
catSelectedMenuButton.getItems().setAll(categoryMenues);
|
catSelectedMenuButton.getItems().setAll(categoryMenues);
|
||||||
@ -221,20 +221,25 @@ public class Toolbar extends ToolBar {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Toolbar() {
|
private Toolbar(ImageGalleryController controller) {
|
||||||
|
this.controller = controller;
|
||||||
FXMLConstructor.construct(this, "Toolbar.fxml");
|
FXMLConstructor.construct(this, "Toolbar.fxml");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MenuItem createSelCatMenuItem(Category cat, final SplitMenuButton catSelectedMenuButton) {
|
private static MenuItem createSelCatMenuItem(Category cat, final SplitMenuButton catSelectedMenuButton, ImageGalleryController controller) {
|
||||||
final MenuItem menuItem = new MenuItem(cat.getDisplayName(), new ImageView(DrawableAttribute.CATEGORY.getIcon()));
|
final MenuItem menuItem = new MenuItem(cat.getDisplayName(), new ImageView(DrawableAttribute.CATEGORY.getIcon()));
|
||||||
menuItem.setOnAction(new EventHandler<ActionEvent>() {
|
menuItem.setOnAction(new EventHandler<ActionEvent>() {
|
||||||
@Override
|
@Override
|
||||||
public void handle(ActionEvent t) {
|
public void handle(ActionEvent t) {
|
||||||
new CategorizeAction().addTag(cat.getTagName(), "");
|
new CategorizeAction(controller).addTag(controller.getTagsManager().getTagName(cat), "");
|
||||||
catSelectedMenuButton.setText(cat.getDisplayName());
|
catSelectedMenuButton.setText(cat.getDisplayName());
|
||||||
catSelectedMenuButton.setOnAction(this);
|
catSelectedMenuButton.setOnAction(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return menuItem;
|
return menuItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ImageGalleryController getController() {
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import javafx.geometry.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.image.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<fx:root maxHeight="-1.0" maxWidth="-1.0" minHeight="-Infinity" minWidth="-Infinity" opacity="1.0" prefHeight="-1.0" prefWidth="-1.0" style="-fx-background-color: linear-gradient(to bottom, derive(-fx-base,-30%), derive(-fx-base,-60%)), linear-gradient(to bottom, derive(-fx-base,65%) 2%, derive(-fx-base,-20%) 95%); -fx-background-radius: 2;" type="javafx.scene.layout.AnchorPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
|
<children>
|
||||||
|
<BorderPane maxHeight="-1.0" maxWidth="-1.0" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" snapToPixel="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<bottom>
|
||||||
|
<BorderPane maxHeight="-Infinity" maxWidth="-1.0" minHeight="-Infinity" minWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0" BorderPane.alignment="CENTER">
|
||||||
|
<center>
|
||||||
|
<Label id="pathLabel" fx:id="nameLabel" alignment="CENTER" contentDisplay="TEXT_ONLY" maxHeight="16.0" minHeight="16.0" minWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0" text="file name" textAlignment="CENTER">
|
||||||
|
<labelFor>
|
||||||
|
<ImageView fx:id="imageView" fitHeight="200.0" fitWidth="200.0" opacity="1.0" pickOnBounds="true" preserveRatio="true" style="-fx-border-radius : 5; -fx-border-width : 5; -fx-border-color : blue;" BorderPane.alignment="CENTER" />
|
||||||
|
</labelFor>
|
||||||
|
</Label>
|
||||||
|
</center>
|
||||||
|
<left>
|
||||||
|
<HBox maxHeight="-Infinity" prefHeight="-1.0" prefWidth="-1.0" spacing="2.0" BorderPane.alignment="CENTER_LEFT">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="fileTypeImageView" fitHeight="16.0" fitWidth="16.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="true" scaleX="1.0" scaleY="1.0">
|
||||||
|
<image>
|
||||||
|
<Image url="@../../images/video-file.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
<ImageView fx:id="hashHitImageView" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true" style="">
|
||||||
|
<image>
|
||||||
|
<Image url="@../../images/hashset_hits.png" />
|
||||||
|
</image>
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets bottom="1.0" left="1.0" right="1.0" top="1.0" fx:id="x1" />
|
||||||
|
</HBox.margin>
|
||||||
|
</ImageView>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="2.0" right="2.0" top="2.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
||||||
|
</left>
|
||||||
|
<right>
|
||||||
|
<ToggleButton fx:id="followUpToggle" minWidth="24.0" mnemonicParsing="false" prefWidth="24.0" selected="false" text="">
|
||||||
|
<graphic>
|
||||||
|
<ImageView id="followUpImageview" fx:id="followUpImageView" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../../images/flag_gray.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</ToggleButton>
|
||||||
|
</right>
|
||||||
|
</BorderPane>
|
||||||
|
</bottom>
|
||||||
|
<center>
|
||||||
|
<BorderPane fx:id="imageBorder" center="$imageView" maxHeight="-1.0" maxWidth="-1.0" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" BorderPane.alignment="CENTER">
|
||||||
|
<center>
|
||||||
|
<ImageView fx:id="imageView" fitHeight="100.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER" />
|
||||||
|
</center>
|
||||||
|
</BorderPane>
|
||||||
|
</center>
|
||||||
|
</BorderPane>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||||
|
</padding>
|
||||||
|
</fx:root>
|
@ -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.imagegallery.gui;
|
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@ -32,8 +32,9 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
|||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||||
import org.sleuthkit.autopsy.imagegallery.TagUtils;
|
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
|
||||||
import static org.sleuthkit.autopsy.imagegallery.gui.DrawableViewBase.globalSelectionModel;
|
import static org.sleuthkit.autopsy.imagegallery.gui.drawableviews.DrawableTileBase.globalSelectionModel;
|
||||||
|
import org.sleuthkit.datamodel.AbstractContent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GUI component that represents a single image as a tile with an icon, a label,
|
* GUI component that represents a single image as a tile with an icon, a label,
|
||||||
@ -43,7 +44,7 @@ import static org.sleuthkit.autopsy.imagegallery.gui.DrawableViewBase.globalSele
|
|||||||
*
|
*
|
||||||
* TODO: refactor this to extend from {@link Control}? -jm
|
* TODO: refactor this to extend from {@link Control}? -jm
|
||||||
*/
|
*/
|
||||||
public class DrawableTile extends DrawableViewBase implements TagUtils.TagListener {
|
public class DrawableTile extends DrawableTileBase {
|
||||||
|
|
||||||
private static final DropShadow LAST_SELECTED_EFFECT = new DropShadow(10, Color.BLUE);
|
private static final DropShadow LAST_SELECTED_EFFECT = new DropShadow(10, Color.BLUE);
|
||||||
|
|
||||||
@ -67,14 +68,13 @@ public class DrawableTile extends DrawableViewBase implements TagUtils.TagListen
|
|||||||
assert imageBorder != null : "fx:id=\"imageAnchor\" was not injected: check your FXML file 'DrawableTile.fxml'.";
|
assert imageBorder != null : "fx:id=\"imageAnchor\" was not injected: check your FXML file 'DrawableTile.fxml'.";
|
||||||
assert imageView != null : "fx:id=\"imageView\" was not injected: check your FXML file 'DrawableTile.fxml'.";
|
assert imageView != null : "fx:id=\"imageView\" was not injected: check your FXML file 'DrawableTile.fxml'.";
|
||||||
assert nameLabel != null : "fx:id=\"nameLabel\" was not injected: check your FXML file 'DrawableTile.fxml'.";
|
assert nameLabel != null : "fx:id=\"nameLabel\" was not injected: check your FXML file 'DrawableTile.fxml'.";
|
||||||
|
|
||||||
//set up properties and binding
|
//set up properties and binding
|
||||||
setCache(true);
|
setCache(true);
|
||||||
setCacheHint(CacheHint.SPEED);
|
setCacheHint(CacheHint.SPEED);
|
||||||
nameLabel.prefWidthProperty().bind(imageView.fitWidthProperty());
|
nameLabel.prefWidthProperty().bind(imageView.fitWidthProperty());
|
||||||
|
|
||||||
imageView.fitHeightProperty().bind(Toolbar.getDefault().sizeSliderValue());
|
imageView.fitHeightProperty().bind(Toolbar.getDefault(getController()).sizeSliderValue());
|
||||||
imageView.fitWidthProperty().bind(Toolbar.getDefault().sizeSliderValue());
|
imageView.fitWidthProperty().bind(Toolbar.getDefault(getController()).sizeSliderValue());
|
||||||
|
|
||||||
globalSelectionModel.lastSelectedProperty().addListener((observable, oldValue, newValue) -> {
|
globalSelectionModel.lastSelectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
try {
|
try {
|
||||||
@ -87,9 +87,11 @@ public class DrawableTile extends DrawableViewBase implements TagUtils.TagListen
|
|||||||
|
|
||||||
public DrawableTile(GroupPane gp) {
|
public DrawableTile(GroupPane gp) {
|
||||||
super(gp);
|
super(gp);
|
||||||
|
|
||||||
FXMLConstructor.construct(this, "DrawableTile.fxml");
|
FXMLConstructor.construct(this, "DrawableTile.fxml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ThreadConfined(type = ThreadType.JFX)
|
@ThreadConfined(type = ThreadType.JFX)
|
||||||
protected void clearContent() {
|
protected void clearContent() {
|
||||||
@ -110,15 +112,20 @@ public class DrawableTile extends DrawableViewBase implements TagUtils.TagListen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Runnable getContentUpdateRunnable() {
|
protected Runnable getContentUpdateRunnable() {
|
||||||
Image image = getFile().getThumbnail();
|
if (getFile().isPresent()) {
|
||||||
|
Image image = getFile().get().getThumbnail();
|
||||||
|
|
||||||
return () -> {
|
return () -> {
|
||||||
imageView.setImage(image);
|
imageView.setImage(image);
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
return () -> { //no-op
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getTextForLabel() {
|
protected String getTextForLabel() {
|
||||||
return getFile().getName();
|
return getFile().map(AbstractContent::getName).orElse("");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013-14 Basis Technology Corp.
|
* Copyright 2013-15 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");
|
||||||
@ -17,14 +17,12 @@
|
|||||||
* 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.imagegallery.gui;
|
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Optional;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
@ -39,7 +37,6 @@ import javafx.scene.control.Tooltip;
|
|||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.input.MouseEvent;
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.scene.layout.AnchorPane;
|
|
||||||
import javafx.scene.layout.Border;
|
import javafx.scene.layout.Border;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.BorderStroke;
|
import javafx.scene.layout.BorderStroke;
|
||||||
@ -63,21 +60,17 @@ import org.sleuthkit.autopsy.datamodel.FileNode;
|
|||||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||||
|
import org.sleuthkit.autopsy.events.ContentTagAddedEvent;
|
||||||
|
import org.sleuthkit.autopsy.events.ContentTagDeletedEvent;
|
||||||
|
import org.sleuthkit.autopsy.events.TagEvent;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileUpdateEvent;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryTopComponent;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryTopComponent;
|
||||||
import org.sleuthkit.autopsy.imagegallery.TagUtils;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.actions.AddDrawableTagAction;
|
import org.sleuthkit.autopsy.imagegallery.actions.AddDrawableTagAction;
|
||||||
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
|
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.actions.DeleteFollowUpTagAction;
|
||||||
import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter;
|
import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryChangeEvent;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupKey;
|
|
||||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
|
||||||
import org.sleuthkit.datamodel.ContentTag;
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
@ -88,9 +81,9 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
* of {@link DrawableView}s should implement the interface directly
|
* of {@link DrawableView}s should implement the interface directly
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class DrawableViewBase extends AnchorPane implements DrawableView {
|
public abstract class DrawableTileBase extends DrawableUIBase {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(DrawableViewBase.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(DrawableTileBase.class.getName());
|
||||||
|
|
||||||
private static final Border UNSELECTED_BORDER = new Border(new BorderStroke(Color.GRAY, BorderStrokeStyle.SOLID, new CornerRadii(2), new BorderWidths(3)));
|
private static final Border UNSELECTED_BORDER = new Border(new BorderStroke(Color.GRAY, BorderStrokeStyle.SOLID, new CornerRadii(2), new BorderWidths(3)));
|
||||||
|
|
||||||
@ -103,6 +96,7 @@ public abstract class DrawableViewBase extends AnchorPane implements DrawableVie
|
|||||||
protected static final Image followUpGray = new Image("org/sleuthkit/autopsy/imagegallery/images/flag_gray.png");
|
protected static final Image followUpGray = new Image("org/sleuthkit/autopsy/imagegallery/images/flag_gray.png");
|
||||||
|
|
||||||
protected static final FileIDSelectionModel globalSelectionModel = FileIDSelectionModel.getInstance();
|
protected static final FileIDSelectionModel globalSelectionModel = FileIDSelectionModel.getInstance();
|
||||||
|
private static ContextMenu contextMenu;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* displays the icon representing video files
|
* displays the icon representing video files
|
||||||
@ -134,23 +128,15 @@ public abstract class DrawableViewBase extends AnchorPane implements DrawableVie
|
|||||||
@FXML
|
@FXML
|
||||||
protected BorderPane imageBorder;
|
protected BorderPane imageBorder;
|
||||||
|
|
||||||
static private ContextMenu contextMenu;
|
|
||||||
|
|
||||||
private DrawableFile<?> file;
|
|
||||||
|
|
||||||
private Long fileID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the groupPane this {@link DrawableViewBase} is embedded in
|
* the groupPane this {@link DrawableTileBase} is embedded in
|
||||||
*/
|
*/
|
||||||
final private GroupPane groupPane;
|
final private GroupPane groupPane;
|
||||||
private boolean registered = false;
|
volatile private boolean registered = false;
|
||||||
|
|
||||||
GroupPane getGroupPane() {
|
protected DrawableTileBase(GroupPane groupPane) {
|
||||||
return groupPane;
|
super(groupPane.getController());
|
||||||
}
|
|
||||||
|
|
||||||
protected DrawableViewBase(GroupPane groupPane) {
|
|
||||||
this.groupPane = groupPane;
|
this.groupPane = groupPane;
|
||||||
globalSelectionModel.getSelected().addListener((Observable observable) -> {
|
globalSelectionModel.getSelected().addListener((Observable observable) -> {
|
||||||
updateSelectionState();
|
updateSelectionState();
|
||||||
@ -162,11 +148,13 @@ public abstract class DrawableViewBase extends AnchorPane implements DrawableVie
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(MouseEvent t) {
|
public void handle(MouseEvent t) {
|
||||||
|
getFile().ifPresent(file -> {
|
||||||
|
final long fileID = file.getId();
|
||||||
switch (t.getButton()) {
|
switch (t.getButton()) {
|
||||||
case PRIMARY:
|
case PRIMARY:
|
||||||
if (t.getClickCount() == 1) {
|
if (t.getClickCount() == 1) {
|
||||||
if (t.isControlDown()) {
|
if (t.isControlDown()) {
|
||||||
|
|
||||||
globalSelectionModel.toggleSelection(fileID);
|
globalSelectionModel.toggleSelection(fileID);
|
||||||
} else {
|
} else {
|
||||||
groupPane.makeSelection(t.isShiftDown(), fileID);
|
groupPane.makeSelection(t.isShiftDown(), fileID);
|
||||||
@ -176,13 +164,11 @@ public abstract class DrawableViewBase extends AnchorPane implements DrawableVie
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SECONDARY:
|
case SECONDARY:
|
||||||
|
|
||||||
if (t.getClickCount() == 1) {
|
if (t.getClickCount() == 1) {
|
||||||
if (globalSelectionModel.isSelected(fileID) == false) {
|
if (globalSelectionModel.isSelected(fileID) == false) {
|
||||||
groupPane.makeSelection(false, fileID);
|
groupPane.makeSelection(false, fileID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contextMenu != null) {
|
if (contextMenu != null) {
|
||||||
contextMenu.hide();
|
contextMenu.hide();
|
||||||
}
|
}
|
||||||
@ -190,20 +176,21 @@ public abstract class DrawableViewBase extends AnchorPane implements DrawableVie
|
|||||||
if (groupContextMenu != null) {
|
if (groupContextMenu != null) {
|
||||||
groupContextMenu.hide();
|
groupContextMenu.hide();
|
||||||
}
|
}
|
||||||
contextMenu = buildContextMenu();
|
contextMenu = buildContextMenu(file);
|
||||||
contextMenu.show(DrawableViewBase.this, t.getScreenX(), t.getScreenY());
|
contextMenu.show(DrawableTileBase.this, t.getScreenX(), t.getScreenY());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
t.consume();
|
t.consume();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContextMenu buildContextMenu() {
|
private ContextMenu buildContextMenu(DrawableFile<?> file) {
|
||||||
final ArrayList<MenuItem> menuItems = new ArrayList<>();
|
final ArrayList<MenuItem> menuItems = new ArrayList<>();
|
||||||
|
|
||||||
menuItems.add(CategorizeAction.getPopupMenu());
|
menuItems.add(new CategorizeAction(getController()).getPopupMenu());
|
||||||
|
|
||||||
menuItems.add(AddDrawableTagAction.getInstance().getPopupMenu());
|
menuItems.add(new AddDrawableTagAction(getController()).getPopupMenu());
|
||||||
|
|
||||||
final MenuItem extractMenuItem = new MenuItem("Extract File(s)");
|
final MenuItem extractMenuItem = new MenuItem("Extract File(s)");
|
||||||
extractMenuItem.setOnAction((ActionEvent t) -> {
|
extractMenuItem.setOnAction((ActionEvent t) -> {
|
||||||
@ -217,13 +204,13 @@ public abstract class DrawableViewBase extends AnchorPane implements DrawableVie
|
|||||||
MenuItem contentViewer = new MenuItem("Show Content Viewer");
|
MenuItem contentViewer = new MenuItem("Show Content Viewer");
|
||||||
contentViewer.setOnAction((ActionEvent t) -> {
|
contentViewer.setOnAction((ActionEvent t) -> {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
new NewWindowViewAction("Show Content Viewer", new FileNode(getFile().getAbstractFile())).actionPerformed(null);
|
new NewWindowViewAction("Show Content Viewer", new FileNode(file.getAbstractFile())).actionPerformed(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
menuItems.add(contentViewer);
|
menuItems.add(contentViewer);
|
||||||
|
|
||||||
MenuItem externalViewer = new MenuItem("Open in External Viewer");
|
MenuItem externalViewer = new MenuItem("Open in External Viewer");
|
||||||
final ExternalViewerAction externalViewerAction = new ExternalViewerAction("Open in External Viewer", new FileNode(getFile().getAbstractFile()));
|
final ExternalViewerAction externalViewerAction = new ExternalViewerAction("Open in External Viewer", new FileNode(file.getAbstractFile()));
|
||||||
|
|
||||||
externalViewer.setDisable(externalViewerAction.isEnabled() == false);
|
externalViewer.setDisable(externalViewerAction.isEnabled() == false);
|
||||||
externalViewer.setOnAction((ActionEvent t) -> {
|
externalViewer.setOnAction((ActionEvent t) -> {
|
||||||
@ -251,6 +238,10 @@ public abstract class DrawableViewBase extends AnchorPane implements DrawableVie
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GroupPane getGroupPane() {
|
||||||
|
return groupPane;
|
||||||
|
}
|
||||||
|
|
||||||
@ThreadConfined(type = ThreadType.UI)
|
@ThreadConfined(type = ThreadType.UI)
|
||||||
protected abstract void clearContent();
|
protected abstract void clearContent();
|
||||||
|
|
||||||
@ -260,124 +251,72 @@ public abstract class DrawableViewBase extends AnchorPane implements DrawableVie
|
|||||||
|
|
||||||
protected abstract String getTextForLabel();
|
protected abstract String getTextForLabel();
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
protected void initialize() {
|
protected void initialize() {
|
||||||
followUpToggle.setOnAction((ActionEvent t) -> {
|
followUpToggle.setOnAction((ActionEvent event) -> {
|
||||||
|
getFile().ifPresent(file -> {
|
||||||
if (followUpToggle.isSelected() == true) {
|
if (followUpToggle.isSelected() == true) {
|
||||||
globalSelectionModel.clearAndSelect(fileID);
|
|
||||||
try {
|
try {
|
||||||
AddDrawableTagAction.getInstance().addTag(TagUtils.getFollowUpTagName(), "");
|
globalSelectionModel.clearAndSelect(file.getId());
|
||||||
|
new AddDrawableTagAction(getController()).addTag(getController().getTagsManager().getFollowUpTagName(), "");
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "Failed to add follow up tag. Could not load TagName.", ex);
|
LOGGER.log(Level.SEVERE, "Failed to add Follow Up tag. Could not load TagName.", ex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//TODO: convert this to an action!
|
new DeleteFollowUpTagAction(getController(), file).handle(event);
|
||||||
final ImageGalleryController controller = ImageGalleryController.getDefault();
|
|
||||||
try {
|
|
||||||
// remove file from old category group
|
|
||||||
controller.getGroupManager().removeFromGroup(new GroupKey<TagName>(DrawableAttribute.TAGS, TagUtils.getFollowUpTagName()), fileID);
|
|
||||||
|
|
||||||
List<ContentTag> contentTagsByContent = Case.getCurrentCase().getServices().getTagsManager().getContentTagsByContent(file);
|
|
||||||
for (ContentTag ct : contentTagsByContent) {
|
|
||||||
if (ct.getName().getDisplayName().equals(TagUtils.getFollowUpTagName().getDisplayName())) {
|
|
||||||
Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(ct);
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent("TagAction", BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE)); //NON-NLS
|
|
||||||
controller.getGroupManager().handleFileUpdate(FileUpdateEvent.newUpdateEvent(Collections.singleton(fileID), DrawableAttribute.TAGS));
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Failed to delete follow up tag.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected boolean hasFollowUp() {
|
||||||
public DrawableFile<?> getFile() {
|
if (getFileID().isPresent()) {
|
||||||
if (fileID != null) {
|
|
||||||
if (file == null || file.getId() != fileID) {
|
|
||||||
try {
|
try {
|
||||||
file = ImageGalleryController.getDefault().getFileFromId(fileID);
|
TagName followUpTagName = getController().getTagsManager().getFollowUpTagName();
|
||||||
|
return DrawableAttribute.TAGS.getValue(getFile().get()).stream()
|
||||||
|
.anyMatch(followUpTagName::equals);
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
LOGGER.log(Level.WARNING, "failed to get DrawableFile for obj_id" + fileID, ex);
|
LOGGER.log(Level.WARNING, "failed to get follow up tag name ", ex);
|
||||||
file = null;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return file;
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return false;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean hasFollowUp() throws TskCoreException {
|
|
||||||
String followUpTagName = TagUtils.getFollowUpTagName().getDisplayName();
|
|
||||||
Collection<TagName> tagNames = DrawableAttribute.TAGS.getValue(getFile());
|
|
||||||
return tagNames.stream().anyMatch((tn) -> tn.getDisplayName().equals(followUpTagName));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
synchronized public Long getFileID() {
|
|
||||||
return fileID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
synchronized public void handleTagsChanged(Collection<Long> ids) {
|
|
||||||
if (fileID != null && ids.contains(fileID)) {
|
|
||||||
updateFollowUpIcon();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized protected void updateFollowUpIcon() {
|
|
||||||
if (file != null) {
|
|
||||||
try {
|
|
||||||
boolean hasFollowUp = hasFollowUp();
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
followUpImageView.setImage(hasFollowUp ? followUpIcon : followUpGray);
|
|
||||||
followUpToggle.setSelected(hasFollowUp);
|
|
||||||
});
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Failed to get follow up status for file.", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
synchronized public void setFile(final Long fileID) {
|
synchronized protected void setFileHelper(final Long newFileID) {
|
||||||
if (Objects.equals(fileID, this.fileID) == false) {
|
setFileIDOpt(Optional.ofNullable(newFileID));
|
||||||
this.fileID = fileID;
|
|
||||||
disposeContent();
|
disposeContent();
|
||||||
|
|
||||||
if (this.fileID == null || Case.isCaseOpen() == false) {
|
if (getFileID().isPresent() == false || Case.isCaseOpen() == false) {
|
||||||
if (registered == true) {
|
if (registered == true) {
|
||||||
ImageGalleryController.getDefault().getCategoryManager().unregisterListener(this);
|
getController().getCategoryManager().unregisterListener(this);
|
||||||
TagUtils.unregisterListener(this);
|
getController().getTagsManager().unregisterListener(this);
|
||||||
registered = false;
|
registered = false;
|
||||||
}
|
}
|
||||||
file = null;
|
setFileOpt(Optional.empty());
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
clearContent();
|
clearContent();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (registered == false) {
|
if (registered == false) {
|
||||||
ImageGalleryController.getDefault().getCategoryManager().registerListener(this);
|
getController().getCategoryManager().registerListener(this);
|
||||||
TagUtils.registerListener(this);
|
getController().getTagsManager().registerListener(this);
|
||||||
registered = true;
|
registered = true;
|
||||||
}
|
}
|
||||||
file = null;
|
setFileOpt(Optional.empty());
|
||||||
getFile();
|
|
||||||
updateSelectionState();
|
updateSelectionState();
|
||||||
updateCategoryBorder();
|
updateCategory();
|
||||||
updateFollowUpIcon();
|
updateFollowUpIcon();
|
||||||
updateUI();
|
updateUI();
|
||||||
Platform.runLater(getContentUpdateRunnable());
|
Platform.runLater(getContentUpdateRunnable());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void updateUI() {
|
private void updateUI() {
|
||||||
final boolean isVideo = getFile().isVideo();
|
getFile().ifPresent(file -> {
|
||||||
|
final boolean isVideo = file.isVideo();
|
||||||
final boolean hasHashSetHits = hasHashHit();
|
final boolean hasHashSetHits = hasHashHit();
|
||||||
final String text = getTextForLabel();
|
final String text = getTextForLabel();
|
||||||
|
|
||||||
@ -387,17 +326,21 @@ public abstract class DrawableViewBase extends AnchorPane implements DrawableVie
|
|||||||
nameLabel.setText(text);
|
nameLabel.setText(text);
|
||||||
nameLabel.setTooltip(new Tooltip(text));
|
nameLabel.setTooltip(new Tooltip(text));
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* update the visual representation of the selection state of this
|
* update the visual representation of the selection state of this
|
||||||
* DrawableView
|
* DrawableView
|
||||||
*/
|
*/
|
||||||
synchronized protected void updateSelectionState() {
|
protected void updateSelectionState() {
|
||||||
final boolean selected = globalSelectionModel.isSelected(getFileID());
|
getFile().ifPresent(file -> {
|
||||||
|
final boolean selected = globalSelectionModel.isSelected(file.getId());
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
setBorder(selected ? SELECTED_BORDER : UNSELECTED_BORDER);
|
setBorder(selected ? SELECTED_BORDER : UNSELECTED_BORDER);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -407,9 +350,43 @@ public abstract class DrawableViewBase extends AnchorPane implements DrawableVie
|
|||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
@Override
|
@Override
|
||||||
synchronized public void handleCategoryChanged(CategoryChangeEvent evt) {
|
public void handleTagAdded(ContentTagAddedEvent evt) {
|
||||||
if (evt.getIds().contains(getFileID())) {
|
handleTagEvent(evt, () -> {
|
||||||
updateCategoryBorder();
|
Platform.runLater(() -> {
|
||||||
|
followUpImageView.setImage(followUpIcon);
|
||||||
|
followUpToggle.setSelected(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
@Override
|
||||||
|
public void handleTagDeleted(ContentTagDeletedEvent evt) {
|
||||||
|
handleTagEvent(evt, this::updateFollowUpIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleTagEvent(TagEvent<ContentTag> evt, Runnable runnable) {
|
||||||
|
getFileID().ifPresent(fileID -> {
|
||||||
|
try {
|
||||||
|
final TagName followUpTagName = getController().getTagsManager().getFollowUpTagName();
|
||||||
|
final ContentTag deletedTag = evt.getTag();
|
||||||
|
|
||||||
|
if (fileID == deletedTag.getContent().getId()
|
||||||
|
&& deletedTag.getName().equals(followUpTagName)) {
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Failed to get followup tag name. Unable to update follow up status for file. ", ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFollowUpIcon() {
|
||||||
|
boolean hasFollowUp = hasFollowUp();
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
followUpImageView.setImage(hasFollowUp ? followUpIcon : followUpGray);
|
||||||
|
followUpToggle.setSelected(hasFollowUp);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2015 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.imagegallery.gui.drawableviews;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import static java.util.Objects.nonNull;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
abstract public class DrawableUIBase extends AnchorPane implements DrawableView {
|
||||||
|
|
||||||
|
private final ImageGalleryController controller;
|
||||||
|
|
||||||
|
private Optional<DrawableFile<?>> fileOpt = Optional.empty();
|
||||||
|
|
||||||
|
private Optional<Long> fileIDOpt = Optional.empty();
|
||||||
|
|
||||||
|
public DrawableUIBase(ImageGalleryController controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageGalleryController getController() {
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Long> getFileID() {
|
||||||
|
return fileIDOpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void setFileIDOpt(Optional<Long> fileIDOpt) {
|
||||||
|
this.fileIDOpt = fileIDOpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void setFileOpt(Optional<DrawableFile<?>> fileOpt) {
|
||||||
|
this.fileOpt = fileOpt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
synchronized public Optional<DrawableFile<?>> getFile() {
|
||||||
|
if (fileIDOpt.isPresent()) {
|
||||||
|
if (fileOpt.isPresent() && fileOpt.get().getId() == fileIDOpt.get()) {
|
||||||
|
return fileOpt;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
fileOpt = Optional.of(getController().getFileFromId(fileIDOpt.get()));
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
Logger.getAnonymousLogger().log(Level.WARNING, "failed to get DrawableFile for obj_id" + fileIDOpt.get(), ex);
|
||||||
|
fileOpt = Optional.empty();
|
||||||
|
}
|
||||||
|
return fileOpt;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void setFileHelper(Long newFileID);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
synchronized public void setFile(Long newFileID) {
|
||||||
|
if (getFileID().isPresent()) {
|
||||||
|
if (Objects.equals(newFileID, getFileID().get()) == false) {
|
||||||
|
setFileHelper(newFileID);
|
||||||
|
}
|
||||||
|
} else if (nonNull(newFileID)) {
|
||||||
|
setFileHelper(newFileID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
package org.sleuthkit.autopsy.imagegallery.gui;
|
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.layout.Border;
|
import javafx.scene.layout.Border;
|
||||||
@ -13,9 +14,10 @@ import javafx.scene.layout.Region;
|
|||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||||
import org.sleuthkit.autopsy.imagegallery.TagUtils;
|
import org.sleuthkit.autopsy.events.ContentTagAddedEvent;
|
||||||
|
import org.sleuthkit.autopsy.events.ContentTagDeletedEvent;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryChangeEvent;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
|
|
||||||
@ -25,7 +27,7 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
|||||||
* } to have there {@link DrawableView#handleCategoryChanged(org.sleuthkit.autopsy.imagegallery.datamodel.CategoryChangeEvent)
|
* } to have there {@link DrawableView#handleCategoryChanged(org.sleuthkit.autopsy.imagegallery.datamodel.CategoryChangeEvent)
|
||||||
* } method invoked
|
* } method invoked
|
||||||
*/
|
*/
|
||||||
public interface DrawableView extends TagUtils.TagListener {
|
public interface DrawableView {
|
||||||
|
|
||||||
//TODO: do this all in css? -jm
|
//TODO: do this all in css? -jm
|
||||||
static final int CAT_BORDER_WIDTH = 10;
|
static final int CAT_BORDER_WIDTH = 10;
|
||||||
@ -50,11 +52,11 @@ public interface DrawableView extends TagUtils.TagListener {
|
|||||||
|
|
||||||
Region getCategoryBorderRegion();
|
Region getCategoryBorderRegion();
|
||||||
|
|
||||||
DrawableFile<?> getFile();
|
Optional<DrawableFile<?>> getFile();
|
||||||
|
|
||||||
void setFile(final Long fileID);
|
void setFile(final Long fileID);
|
||||||
|
|
||||||
Long getFileID();
|
Optional<Long> getFileID();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* update the visual representation of the category of the assigned file.
|
* update the visual representation of the category of the assigned file.
|
||||||
@ -65,14 +67,28 @@ public interface DrawableView extends TagUtils.TagListener {
|
|||||||
* @param evt the CategoryChangeEvent to handle
|
* @param evt the CategoryChangeEvent to handle
|
||||||
*/
|
*/
|
||||||
@Subscribe
|
@Subscribe
|
||||||
void handleCategoryChanged(CategoryChangeEvent evt);
|
default void handleCategoryChanged(org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager.CategoryChangeEvent evt) {
|
||||||
|
getFileID().ifPresent(fileID -> {
|
||||||
|
if (evt.getFileIDs().contains(fileID)) {
|
||||||
|
updateCategory();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Subscribe
|
||||||
void handleTagsChanged(Collection<Long> ids);
|
void handleTagAdded(ContentTagAddedEvent evt);
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
void handleTagDeleted(ContentTagDeletedEvent evt);
|
||||||
|
|
||||||
|
ImageGalleryController getController();
|
||||||
|
|
||||||
default boolean hasHashHit() {
|
default boolean hasHashHit() {
|
||||||
try {
|
try {
|
||||||
return getFile().getHashHitSetNames().isEmpty() == false;
|
return getFile().map(DrawableFile::getHashHitSetNames)
|
||||||
|
.map((Collection<String> t) -> t.isEmpty() == false)
|
||||||
|
.orElse(false);
|
||||||
|
|
||||||
} catch (NullPointerException ex) {
|
} catch (NullPointerException ex) {
|
||||||
// I think this happens when we're in the process of removing images from the view while
|
// I think this happens when we're in the process of removing images from the view while
|
||||||
// also trying to update it?
|
// also trying to update it?
|
||||||
@ -82,6 +98,7 @@ public interface DrawableView extends TagUtils.TagListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Border getCategoryBorder(Category category) {
|
static Border getCategoryBorder(Category category) {
|
||||||
|
if (category != null) {
|
||||||
switch (category) {
|
switch (category) {
|
||||||
case ONE:
|
case ONE:
|
||||||
return CAT1_BORDER;
|
return CAT1_BORDER;
|
||||||
@ -98,19 +115,22 @@ public interface DrawableView extends TagUtils.TagListener {
|
|||||||
return CAT0_BORDER;
|
return CAT0_BORDER;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return CAT0_BORDER;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.ANY)
|
@ThreadConfined(type = ThreadConfined.ThreadType.ANY)
|
||||||
default Category updateCategoryBorder() {
|
default Category updateCategory() {
|
||||||
final Category category = getFile().getCategory();
|
if (getFile().isPresent()) {
|
||||||
final Border border = hasHashHit() && (category == Category.ZERO)
|
final Category category = getFile().map(DrawableFile::getCategory).orElse(Category.ZERO);
|
||||||
? HASH_BORDER
|
final Border border = hasHashHit() && (category == Category.ZERO) ? HASH_BORDER : getCategoryBorder(category);
|
||||||
: getCategoryBorder(category);
|
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
getCategoryBorderRegion().setBorder(border);
|
getCategoryBorderRegion().setBorder(border);
|
||||||
getCategoryBorderRegion().requestLayout();
|
|
||||||
});
|
});
|
||||||
return category;
|
return category;
|
||||||
|
} else {
|
||||||
|
return Category.ZERO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -23,7 +23,7 @@
|
|||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../images/arrow-180.png" />
|
<Image url="@../../images/arrow-180.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
@ -32,7 +32,7 @@
|
|||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../images/arrow.png" />
|
<Image url="@../../images/arrow.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
@ -53,7 +53,7 @@
|
|||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../images/control-double.png" />
|
<Image url="@../../images/control-double.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
@ -108,7 +108,7 @@
|
|||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitHeight="16.0" fitWidth="16.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="true" translateY="1.0">
|
<ImageView fitHeight="16.0" fitWidth="16.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="true" translateY="1.0">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../images/application_view_tile.png" />
|
<Image url="@../../images/application_view_tile.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
@ -117,7 +117,7 @@
|
|||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitHeight="16.0" fitWidth="16.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="true" rotate="90.0" translateY="1.0">
|
<ImageView fitHeight="16.0" fitWidth="16.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="true" rotate="90.0" translateY="1.0">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../images/film.png" />
|
<Image url="@../../images/film.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
@ -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.imagegallery.gui;
|
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -104,7 +104,6 @@ import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
|||||||
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryTopComponent;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryTopComponent;
|
||||||
import org.sleuthkit.autopsy.imagegallery.TagUtils;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.actions.AddDrawableTagAction;
|
import org.sleuthkit.autopsy.imagegallery.actions.AddDrawableTagAction;
|
||||||
import org.sleuthkit.autopsy.imagegallery.actions.Back;
|
import org.sleuthkit.autopsy.imagegallery.actions.Back;
|
||||||
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
|
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
|
||||||
@ -113,9 +112,10 @@ import org.sleuthkit.autopsy.imagegallery.actions.NextUnseenGroup;
|
|||||||
import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter;
|
import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.DrawableGroup;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupViewMode;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewMode;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupViewState;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
@ -126,13 +126,13 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* TODO: Extract the The GridView instance to a separate class analogous to the
|
* TODO: Extract the The GridView instance to a separate class analogous to the
|
||||||
* SlideShow. Move selection model into controlsfx GridView and submit pull
|
* SlideShow.
|
||||||
|
*
|
||||||
|
* TODO: Move selection model into controlsfx GridView and submit pull
|
||||||
* request to them.
|
* request to them.
|
||||||
* https://bitbucket.org/controlsfx/controlsfx/issue/4/add-a-multipleselectionmodel-to-gridview
|
* https://bitbucket.org/controlsfx/controlsfx/issue/4/add-a-multipleselectionmodel-to-gridview
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class GroupPane extends BorderPane implements GroupView {
|
public class GroupPane extends BorderPane {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(GroupPane.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(GroupPane.class.getName());
|
||||||
|
|
||||||
@ -277,7 +277,7 @@ public class GroupPane extends BorderPane implements GroupView {
|
|||||||
@Override
|
@Override
|
||||||
public void handle(ActionEvent t) {
|
public void handle(ActionEvent t) {
|
||||||
Set<Long> fileIdSet = new HashSet<>(getGrouping().fileIds());
|
Set<Long> fileIdSet = new HashSet<>(getGrouping().fileIds());
|
||||||
new CategorizeAction().addTagsToFiles(cat.getTagName(), "", fileIdSet);
|
new CategorizeAction(controller).addTagsToFiles(controller.getTagsManager().getTagName(cat), "", fileIdSet);
|
||||||
|
|
||||||
grpCatSplitMenu.setText(cat.getDisplayName());
|
grpCatSplitMenu.setText(cat.getDisplayName());
|
||||||
grpCatSplitMenu.setOnAction(this);
|
grpCatSplitMenu.setOnAction(this);
|
||||||
@ -292,7 +292,7 @@ public class GroupPane extends BorderPane implements GroupView {
|
|||||||
@Override
|
@Override
|
||||||
public void handle(ActionEvent t) {
|
public void handle(ActionEvent t) {
|
||||||
Set<Long> fileIdSet = new HashSet<>(getGrouping().fileIds());
|
Set<Long> fileIdSet = new HashSet<>(getGrouping().fileIds());
|
||||||
AddDrawableTagAction.getInstance().addTagsToFiles(tn, "", fileIdSet);
|
new AddDrawableTagAction(controller).addTagsToFiles(tn, "", fileIdSet);
|
||||||
|
|
||||||
grpTagSplitMenu.setText(tn.getDisplayName());
|
grpTagSplitMenu.setText(tn.getDisplayName());
|
||||||
grpTagSplitMenu.setOnAction(this);
|
grpTagSplitMenu.setOnAction(this);
|
||||||
@ -340,8 +340,8 @@ public class GroupPane extends BorderPane implements GroupView {
|
|||||||
flashAnimation.setAutoReverse(true);
|
flashAnimation.setAutoReverse(true);
|
||||||
|
|
||||||
//configure gridView cell properties
|
//configure gridView cell properties
|
||||||
gridView.cellHeightProperty().bind(Toolbar.getDefault().sizeSliderValue().add(75));
|
gridView.cellHeightProperty().bind(Toolbar.getDefault(controller).sizeSliderValue().add(75));
|
||||||
gridView.cellWidthProperty().bind(Toolbar.getDefault().sizeSliderValue().add(75));
|
gridView.cellWidthProperty().bind(Toolbar.getDefault(controller).sizeSliderValue().add(75));
|
||||||
gridView.setCellFactory((GridView<Long> param) -> new DrawableCell());
|
gridView.setCellFactory((GridView<Long> param) -> new DrawableCell());
|
||||||
|
|
||||||
//configure toolbar properties
|
//configure toolbar properties
|
||||||
@ -349,8 +349,8 @@ public class GroupPane extends BorderPane implements GroupView {
|
|||||||
spacer.setMinWidth(Region.USE_PREF_SIZE);
|
spacer.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
grpTagSplitMenu.setText(TagUtils.getFollowUpTagName().getDisplayName());
|
grpTagSplitMenu.setText(getController().getTagsManager().getFollowUpTagName().getDisplayName());
|
||||||
grpTagSplitMenu.setOnAction(createGrpTagMenuItem(TagUtils.getFollowUpTagName()).getOnAction());
|
grpTagSplitMenu.setOnAction(createGrpTagMenuItem(getController().getTagsManager().getFollowUpTagName()).getOnAction());
|
||||||
} catch (TskCoreException tskCoreException) {
|
} catch (TskCoreException tskCoreException) {
|
||||||
LOGGER.log(Level.WARNING, "failed to load FollowUpTagName", tskCoreException);
|
LOGGER.log(Level.WARNING, "failed to load FollowUpTagName", tskCoreException);
|
||||||
}
|
}
|
||||||
@ -358,8 +358,8 @@ public class GroupPane extends BorderPane implements GroupView {
|
|||||||
grpTagSplitMenu.showingProperty().addListener((ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) -> {
|
grpTagSplitMenu.showingProperty().addListener((ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) -> {
|
||||||
if (t1) {
|
if (t1) {
|
||||||
ArrayList<MenuItem> selTagMenues = new ArrayList<>();
|
ArrayList<MenuItem> selTagMenues = new ArrayList<>();
|
||||||
for (final TagName tn : TagUtils.getNonCategoryTagNames()) {
|
for (final TagName tn : getController().getTagsManager().getNonCategoryTagNames()) {
|
||||||
MenuItem menuItem = TagUtils.createSelTagMenuItem(tn, grpTagSplitMenu);
|
MenuItem menuItem = createGrpTagMenuItem(tn);
|
||||||
selTagMenues.add(menuItem);
|
selTagMenues.add(menuItem);
|
||||||
}
|
}
|
||||||
grpTagSplitMenu.getItems().setAll(selTagMenues);
|
grpTagSplitMenu.getItems().setAll(selTagMenues);
|
||||||
@ -419,9 +419,8 @@ public class GroupPane extends BorderPane implements GroupView {
|
|||||||
private ContextMenu buildContextMenu() {
|
private ContextMenu buildContextMenu() {
|
||||||
ArrayList<MenuItem> menuItems = new ArrayList<>();
|
ArrayList<MenuItem> menuItems = new ArrayList<>();
|
||||||
|
|
||||||
menuItems.add(CategorizeAction.getPopupMenu());
|
menuItems.add(new CategorizeAction(controller).getPopupMenu());
|
||||||
|
menuItems.add(new AddDrawableTagAction(controller).getPopupMenu());
|
||||||
menuItems.add(AddDrawableTagAction.getInstance().getPopupMenu());
|
|
||||||
|
|
||||||
Collection<? extends ContextMenuActionsProvider> menuProviders = Lookup.getDefault().lookupAll(ContextMenuActionsProvider.class);
|
Collection<? extends ContextMenuActionsProvider> menuProviders = Lookup.getDefault().lookupAll(ContextMenuActionsProvider.class);
|
||||||
|
|
||||||
@ -590,6 +589,7 @@ public class GroupPane extends BorderPane implements GroupView {
|
|||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
gridView.getItems().setAll(Collections.emptyList());
|
gridView.getItems().setAll(Collections.emptyList());
|
||||||
setCenter(null);
|
setCenter(null);
|
||||||
|
slideShowToggle.setDisable(true);
|
||||||
groupLabel.setText("");
|
groupLabel.setText("");
|
||||||
resetScrollBar();
|
resetScrollBar();
|
||||||
if (false == Case.isCaseOpen()) {
|
if (false == Case.isCaseOpen()) {
|
||||||
@ -653,6 +653,10 @@ public class GroupPane extends BorderPane implements GroupView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImageGalleryController getController() {
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
private class DrawableCell extends GridCell<Long> {
|
private class DrawableCell extends GridCell<Long> {
|
||||||
|
|
||||||
private final DrawableTile tile = new DrawableTile(GroupPane.this);
|
private final DrawableTile tile = new DrawableTile(GroupPane.this);
|
||||||
@ -751,27 +755,27 @@ public class GroupPane extends BorderPane implements GroupView {
|
|||||||
switch (t.getCode()) {
|
switch (t.getCode()) {
|
||||||
case NUMPAD0:
|
case NUMPAD0:
|
||||||
case DIGIT0:
|
case DIGIT0:
|
||||||
new CategorizeAction().addTag(Category.ZERO.getTagName(), "");
|
new CategorizeAction(controller).addTag(controller.getTagsManager().getTagName(Category.ZERO), "");
|
||||||
break;
|
break;
|
||||||
case NUMPAD1:
|
case NUMPAD1:
|
||||||
case DIGIT1:
|
case DIGIT1:
|
||||||
new CategorizeAction().addTag(Category.ONE.getTagName(), "");
|
new CategorizeAction(controller).addTag(controller.getTagsManager().getTagName(Category.ONE), "");
|
||||||
break;
|
break;
|
||||||
case NUMPAD2:
|
case NUMPAD2:
|
||||||
case DIGIT2:
|
case DIGIT2:
|
||||||
new CategorizeAction().addTag(Category.TWO.getTagName(), "");
|
new CategorizeAction(controller).addTag(controller.getTagsManager().getTagName(Category.TWO), "");
|
||||||
break;
|
break;
|
||||||
case NUMPAD3:
|
case NUMPAD3:
|
||||||
case DIGIT3:
|
case DIGIT3:
|
||||||
new CategorizeAction().addTag(Category.THREE.getTagName(), "");
|
new CategorizeAction(controller).addTag(controller.getTagsManager().getTagName(Category.THREE), "");
|
||||||
break;
|
break;
|
||||||
case NUMPAD4:
|
case NUMPAD4:
|
||||||
case DIGIT4:
|
case DIGIT4:
|
||||||
new CategorizeAction().addTag(Category.FOUR.getTagName(), "");
|
new CategorizeAction(controller).addTag(controller.getTagsManager().getTagName(Category.FOUR), "");
|
||||||
break;
|
break;
|
||||||
case NUMPAD5:
|
case NUMPAD5:
|
||||||
case DIGIT5:
|
case DIGIT5:
|
||||||
new CategorizeAction().addTag(Category.FIVE.getTagName(), "");
|
new CategorizeAction(controller).addTag(controller.getTagsManager().getTagName(Category.FIVE), "");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,13 +16,15 @@
|
|||||||
* 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.imagegallery.gui;
|
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.logging.Level;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
@ -36,34 +38,28 @@ import javafx.scene.control.TableColumn;
|
|||||||
import javafx.scene.control.TableView;
|
import javafx.scene.control.TableView;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.AnchorPane;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import static javafx.scene.layout.Region.USE_COMPUTED_SIZE;
|
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.util.Pair;
|
import javafx.util.Pair;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.events.ContentTagAddedEvent;
|
||||||
|
import org.sleuthkit.autopsy.events.ContentTagDeletedEvent;
|
||||||
|
import org.sleuthkit.autopsy.events.TagEvent;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.TagUtils;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryChangeEvent;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows details of the selected file.
|
* Shows details of the selected file.
|
||||||
*/
|
*/
|
||||||
public class MetaDataPane extends AnchorPane implements TagUtils.TagListener, DrawableView {
|
public class MetaDataPane extends DrawableUIBase {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(MetaDataPane.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(MetaDataPane.class.getName());
|
||||||
|
|
||||||
private final ImageGalleryController controller;
|
|
||||||
|
|
||||||
private Long fileID;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private ImageView imageView;
|
private ImageView imageView;
|
||||||
|
|
||||||
@ -79,21 +75,29 @@ public class MetaDataPane extends AnchorPane implements TagUtils.TagListener, Dr
|
|||||||
@FXML
|
@FXML
|
||||||
private BorderPane imageBorder;
|
private BorderPane imageBorder;
|
||||||
|
|
||||||
private DrawableFile<?> file;
|
public MetaDataPane(ImageGalleryController controller) {
|
||||||
|
super(controller);
|
||||||
|
|
||||||
@Override
|
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("MetaDataPane.fxml"));
|
||||||
public Long getFileID() {
|
fxmlLoader.setRoot(this);
|
||||||
return fileID;
|
fxmlLoader.setController(this);
|
||||||
|
|
||||||
|
try {
|
||||||
|
fxmlLoader.load();
|
||||||
|
} catch (IOException exception) {
|
||||||
|
throw new RuntimeException(exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
void initialize() {
|
void initialize() {
|
||||||
assert attributeColumn != null : "fx:id=\"attributeColumn\" was not injected: check your FXML file 'MetaDataPane.fxml'.";
|
assert attributeColumn != null : "fx:id=\"attributeColumn\" was not injected: check your FXML file 'MetaDataPane.fxml'.";
|
||||||
assert imageView != null : "fx:id=\"imageView\" was not injected: check your FXML file 'MetaDataPane.fxml'.";
|
assert imageView != null : "fx:id=\"imageView\" was not injected: check your FXML file 'MetaDataPane.fxml'.";
|
||||||
assert tableView != null : "fx:id=\"tableView\" was not injected: check your FXML file 'MetaDataPane.fxml'.";
|
assert tableView != null : "fx:id=\"tableView\" was not injected: check your FXML file 'MetaDataPane.fxml'.";
|
||||||
assert valueColumn != null : "fx:id=\"valueColumn\" was not injected: check your FXML file 'MetaDataPane.fxml'.";
|
assert valueColumn != null : "fx:id=\"valueColumn\" was not injected: check your FXML file 'MetaDataPane.fxml'.";
|
||||||
TagUtils.registerListener(this);
|
getController().getTagsManager().registerListener(this);
|
||||||
ImageGalleryController.getDefault().getCategoryManager().registerListener(this);
|
getController().getCategoryManager().registerListener(this);
|
||||||
|
|
||||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
tableView.setPlaceholder(new Label("Select a file to show its details here."));
|
tableView.setPlaceholder(new Label("Select a file to show its details here."));
|
||||||
@ -116,14 +120,12 @@ public class MetaDataPane extends AnchorPane implements TagUtils.TagListener, Dr
|
|||||||
attributeColumn.setPrefWidth(USE_COMPUTED_SIZE);
|
attributeColumn.setPrefWidth(USE_COMPUTED_SIZE);
|
||||||
|
|
||||||
valueColumn.setCellValueFactory((p) -> {
|
valueColumn.setCellValueFactory((p) -> {
|
||||||
if (p.getValue().getKey() == DrawableAttribute.TAGS) {
|
return (p.getValue().getKey() == DrawableAttribute.TAGS)
|
||||||
return new SimpleStringProperty(((Collection<TagName>) p.getValue().getValue()).stream()
|
? new SimpleStringProperty(((Collection<TagName>) p.getValue().getValue()).stream()
|
||||||
.map(TagName::getDisplayName)
|
.map(TagName::getDisplayName)
|
||||||
.filter((String t) -> t.startsWith(Category.CATEGORY_PREFIX) == false)
|
.filter(Category::isNotCategoryName)
|
||||||
.collect(Collectors.joining(" ; ", "", "")));
|
.collect(Collectors.joining(" ; ")))
|
||||||
} else {
|
: new SimpleStringProperty(StringUtils.join((Iterable<?>) p.getValue().getValue(), " ; "));
|
||||||
return new SimpleStringProperty(StringUtils.join((Iterable<?>) p.getValue().getValue(), " ; "));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
valueColumn.setPrefWidth(USE_COMPUTED_SIZE);
|
valueColumn.setPrefWidth(USE_COMPUTED_SIZE);
|
||||||
valueColumn.setCellFactory((p) -> new TableCell<Pair<DrawableAttribute<?>, ? extends Object>, String>() {
|
valueColumn.setCellFactory((p) -> new TableCell<Pair<DrawableAttribute<?>, ? extends Object>, String>() {
|
||||||
@ -142,37 +144,15 @@ public class MetaDataPane extends AnchorPane implements TagUtils.TagListener, Dr
|
|||||||
tableView.getColumns().setAll(Arrays.asList(attributeColumn, valueColumn));
|
tableView.getColumns().setAll(Arrays.asList(attributeColumn, valueColumn));
|
||||||
|
|
||||||
//listen for selection change
|
//listen for selection change
|
||||||
controller.getSelectionModel().lastSelectedProperty().addListener((observable, oldFileID, newFileID) -> {
|
getController().getSelectionModel().lastSelectedProperty().addListener((observable, oldFileID, newFileID) -> {
|
||||||
setFile(newFileID);
|
setFile(newFileID);
|
||||||
});
|
});
|
||||||
|
|
||||||
// MetaDataPane.this.visibleProperty().bind(controller.getMetaDataCollapsed().not());
|
|
||||||
// MetaDataPane.this.managedProperty().bind(controller.getMetaDataCollapsed().not());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DrawableFile<?> getFile() {
|
synchronized protected void setFileHelper(Long newFileID) {
|
||||||
if (fileID != null) {
|
setFileIDOpt(Optional.ofNullable(newFileID));
|
||||||
if (file == null || file.getId() != fileID) {
|
if (newFileID == null) {
|
||||||
try {
|
|
||||||
file = controller.getFileFromId(fileID);
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
LOGGER.log(Level.WARNING, "failed to get DrawableFile for obj_id" + fileID, ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFile(Long fileID) {
|
|
||||||
this.fileID = fileID;
|
|
||||||
|
|
||||||
if (fileID == null) {
|
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
imageView.setImage(null);
|
imageView.setImage(null);
|
||||||
tableView.getItems().clear();
|
tableView.getItems().clear();
|
||||||
@ -180,39 +160,23 @@ public class MetaDataPane extends AnchorPane implements TagUtils.TagListener, Dr
|
|||||||
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
try {
|
|
||||||
file = controller.getFileFromId(fileID);
|
|
||||||
updateUI();
|
updateUI();
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
LOGGER.log(Level.WARNING, "Failed to get drawable file from ID", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MetaDataPane(ImageGalleryController controller) {
|
|
||||||
this.controller = controller;
|
|
||||||
|
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("MetaDataPane.fxml"));
|
|
||||||
fxmlLoader.setRoot(this);
|
|
||||||
fxmlLoader.setController(this);
|
|
||||||
|
|
||||||
try {
|
|
||||||
fxmlLoader.load();
|
|
||||||
} catch (IOException exception) {
|
|
||||||
throw new RuntimeException(exception);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateUI() {
|
public void updateUI() {
|
||||||
final Image icon = getFile().getThumbnail();
|
getFile().ifPresent(file -> {
|
||||||
final ObservableList<Pair<DrawableAttribute<?>, ? extends Object>> attributesList = getFile().getAttributesList();
|
final Image icon = file.getThumbnail();
|
||||||
|
final ObservableList<Pair<DrawableAttribute<?>, ? extends Object>> attributesList = file.getAttributesList();
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
imageView.setImage(icon);
|
imageView.setImage(icon);
|
||||||
|
tableView.getItems().clear();
|
||||||
tableView.getItems().setAll(attributesList);
|
tableView.getItems().setAll(attributesList);
|
||||||
});
|
});
|
||||||
|
|
||||||
updateCategoryBorder();
|
updateCategory();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -223,16 +187,35 @@ public class MetaDataPane extends AnchorPane implements TagUtils.TagListener, Dr
|
|||||||
/** {@inheritDoc } */
|
/** {@inheritDoc } */
|
||||||
@Subscribe
|
@Subscribe
|
||||||
@Override
|
@Override
|
||||||
public void handleCategoryChanged(CategoryChangeEvent evt) {
|
public void handleCategoryChanged(CategoryManager.CategoryChangeEvent evt) {
|
||||||
if (getFile() != null && evt.getIds().contains(getFileID())) {
|
getFileID().ifPresent(fileID -> {
|
||||||
|
if (evt.getFileIDs().contains(fileID)) {
|
||||||
updateUI();
|
updateUI();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
@Override
|
||||||
|
public void handleTagAdded(ContentTagAddedEvent evt) {
|
||||||
|
handleTagEvent(evt, this::updateUI);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleTagsChanged(Collection<Long> ids) {
|
public void handleTagDeleted(ContentTagDeletedEvent evt) {
|
||||||
if (getFile() != null && ids.contains(getFileID())) {
|
handleTagEvent(evt, this::updateUI);
|
||||||
updateUI();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param tagFileID the value of tagEvent
|
||||||
|
* @param runnable the value of runnable
|
||||||
|
*/
|
||||||
|
void handleTagEvent(TagEvent<ContentTag> tagEvent, final Runnable runnable) {
|
||||||
|
getFileID().ifPresent(fileID -> {
|
||||||
|
if (Objects.equals(tagEvent.getTag().getContent().getId(), fileID)) {
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,7 +18,7 @@
|
|||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitHeight="128.0" fitWidth="32.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="false" rotate="180.0" scaleX="1.0" translateX="0.0">
|
<ImageView fitHeight="128.0" fitWidth="32.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="false" rotate="180.0" scaleX="1.0" translateX="0.0">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../images/right_arrow_128.png" />
|
<Image url="@../../images/right_arrow_128.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
@ -42,7 +42,7 @@
|
|||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitHeight="16.0" fitWidth="16.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="true">
|
<ImageView fitHeight="16.0" fitWidth="16.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="true">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../images/category-icon.png" />
|
<Image url="@../../images/category-icon.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
@ -88,12 +88,12 @@
|
|||||||
<children>
|
<children>
|
||||||
<ImageView fx:id="fileTypeImageView" fitHeight="16.0" fitWidth="16.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="true" scaleX="1.0" scaleY="1.0">
|
<ImageView fx:id="fileTypeImageView" fitHeight="16.0" fitWidth="16.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="true" scaleX="1.0" scaleY="1.0">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../images/video-file.png" />
|
<Image url="@../../images/video-file.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
<ImageView fx:id="hashHitImageView" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true" style="">
|
<ImageView fx:id="hashHitImageView" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true" style="">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../images/hashset_hits.png" />
|
<Image url="@../../images/hashset_hits.png" />
|
||||||
</image>
|
</image>
|
||||||
<HBox.margin>
|
<HBox.margin>
|
||||||
<Insets bottom="1.0" left="1.0" right="1.0" top="1.0" />
|
<Insets bottom="1.0" left="1.0" right="1.0" top="1.0" />
|
||||||
@ -110,7 +110,7 @@
|
|||||||
<graphic>
|
<graphic>
|
||||||
<ImageView id="followUpImageview" fx:id="followUpImageView" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
<ImageView id="followUpImageview" fx:id="followUpImageView" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../images/flag_gray.png" />
|
<Image url="@../../images/flag_gray.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
||||||
@ -135,7 +135,7 @@
|
|||||||
<graphic>
|
<graphic>
|
||||||
<ImageView fitHeight="128.0" fitWidth="32.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="false" scaleX="1.0" smooth="true" translateX="0.0">
|
<ImageView fitHeight="128.0" fitWidth="32.0" mouseTransparent="true" pickOnBounds="true" preserveRatio="false" scaleX="1.0" smooth="true" translateX="0.0">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../images/right_arrow_128.png" />
|
<Image url="@../../images/right_arrow_128.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</graphic>
|
</graphic>
|
@ -16,14 +16,16 @@
|
|||||||
* 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.imagegallery.gui;
|
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
@ -50,62 +52,60 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
|||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
||||||
import org.sleuthkit.autopsy.imagegallery.TagUtils;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
|
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.ImageFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.ImageFile;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.VideoFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.VideoFile;
|
||||||
import static org.sleuthkit.autopsy.imagegallery.gui.DrawableView.HASH_BORDER;
|
import org.sleuthkit.autopsy.imagegallery.gui.GuiUtils;
|
||||||
import static org.sleuthkit.autopsy.imagegallery.gui.DrawableView.getCategoryBorder;
|
import org.sleuthkit.autopsy.imagegallery.gui.MediaControl;
|
||||||
|
import static org.sleuthkit.autopsy.imagegallery.gui.drawableviews.DrawableView.CAT_BORDER_WIDTH;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the files of a group one at a time. Designed to be embedded in a
|
* Displays the files of a group one at a time. Designed to be embedded in a
|
||||||
* GroupPane. TODO: Extract a subclass for video files in slideshow mode-jm
|
* GroupPane. TODO: Extract a subclass for video files in slideshow mode-jm
|
||||||
|
*
|
||||||
* TODO: reduce coupling to GroupPane
|
* TODO: reduce coupling to GroupPane
|
||||||
*/
|
*/
|
||||||
public class SlideShowView extends DrawableViewBase implements TagUtils.TagListener {
|
public class SlideShowView extends DrawableTileBase {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(SlideShowView.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(SlideShowView.class.getName());
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private ToggleButton cat0Toggle;
|
private ToggleButton cat0Toggle;
|
||||||
|
@FXML
|
||||||
|
private ToggleButton cat1Toggle;
|
||||||
@FXML
|
@FXML
|
||||||
private ToggleButton cat2Toggle;
|
private ToggleButton cat2Toggle;
|
||||||
|
@FXML
|
||||||
|
private ToggleButton cat3Toggle;
|
||||||
|
@FXML
|
||||||
|
private ToggleButton cat4Toggle;
|
||||||
|
@FXML
|
||||||
|
private ToggleButton cat5Toggle;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private SplitMenuButton tagSplitButton;
|
private SplitMenuButton tagSplitButton;
|
||||||
|
|
||||||
@FXML
|
|
||||||
private ToggleButton cat3Toggle;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Region spring;
|
private Region spring;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Button leftButton;
|
private Button leftButton;
|
||||||
|
|
||||||
@FXML
|
|
||||||
private ToggleButton cat4Toggle;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private ToggleButton cat5Toggle;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private ToggleButton cat1Toggle;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Button rightButton;
|
private Button rightButton;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private ToolBar toolBar;
|
private ToolBar toolBar;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private BorderPane footer;
|
private BorderPane footer;
|
||||||
|
|
||||||
|
SlideShowView(GroupPane gp) {
|
||||||
|
super(gp);
|
||||||
|
FXMLConstructor.construct(this, "SlideShow.fxml");
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@Override
|
@Override
|
||||||
protected void initialize() {
|
protected void initialize() {
|
||||||
@ -127,7 +127,7 @@ public class SlideShowView extends DrawableViewBase implements TagUtils.TagListe
|
|||||||
|
|
||||||
tagSplitButton.setOnAction((ActionEvent t) -> {
|
tagSplitButton.setOnAction((ActionEvent t) -> {
|
||||||
try {
|
try {
|
||||||
TagUtils.createSelTagMenuItem(TagUtils.getFollowUpTagName(), tagSplitButton).getOnAction().handle(t);
|
GuiUtils.createSelTagMenuItem(getController().getTagsManager().getFollowUpTagName(), tagSplitButton, getController()).getOnAction().handle(t);
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
Exceptions.printStackTrace(ex);
|
Exceptions.printStackTrace(ex);
|
||||||
}
|
}
|
||||||
@ -137,13 +137,15 @@ public class SlideShowView extends DrawableViewBase implements TagUtils.TagListe
|
|||||||
tagSplitButton.showingProperty().addListener((ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) -> {
|
tagSplitButton.showingProperty().addListener((ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) -> {
|
||||||
if (t1) {
|
if (t1) {
|
||||||
ArrayList<MenuItem> selTagMenues = new ArrayList<>();
|
ArrayList<MenuItem> selTagMenues = new ArrayList<>();
|
||||||
for (final TagName tn : TagUtils.getNonCategoryTagNames()) {
|
for (final TagName tn : getController().getTagsManager().getNonCategoryTagNames()) {
|
||||||
MenuItem menuItem = TagUtils.createSelTagMenuItem(tn, tagSplitButton);
|
MenuItem menuItem = GuiUtils.createSelTagMenuItem(tn, tagSplitButton, getController());
|
||||||
selTagMenues.add(menuItem);
|
selTagMenues.add(menuItem);
|
||||||
}
|
}
|
||||||
tagSplitButton.getItems().setAll(selTagMenues);
|
tagSplitButton.getItems().setAll(selTagMenues);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//configure category toggles
|
||||||
cat0Toggle.setBorder(new Border(new BorderStroke(Category.ZERO.getColor(), BorderStrokeStyle.SOLID, new CornerRadii(1), new BorderWidths(1))));
|
cat0Toggle.setBorder(new Border(new BorderStroke(Category.ZERO.getColor(), BorderStrokeStyle.SOLID, new CornerRadii(1), new BorderWidths(1))));
|
||||||
cat1Toggle.setBorder(new Border(new BorderStroke(Category.ONE.getColor(), BorderStrokeStyle.SOLID, new CornerRadii(1), new BorderWidths(1))));
|
cat1Toggle.setBorder(new Border(new BorderStroke(Category.ONE.getColor(), BorderStrokeStyle.SOLID, new CornerRadii(1), new BorderWidths(1))));
|
||||||
cat2Toggle.setBorder(new Border(new BorderStroke(Category.TWO.getColor(), BorderStrokeStyle.SOLID, new CornerRadii(1), new BorderWidths(1))));
|
cat2Toggle.setBorder(new Border(new BorderStroke(Category.TWO.getColor(), BorderStrokeStyle.SOLID, new CornerRadii(1), new BorderWidths(1))));
|
||||||
@ -175,9 +177,7 @@ public class SlideShowView extends DrawableViewBase implements TagUtils.TagListe
|
|||||||
|
|
||||||
//set up key listener equivalents of buttons
|
//set up key listener equivalents of buttons
|
||||||
addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent t) -> {
|
addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent t) -> {
|
||||||
|
|
||||||
if (t.getEventType() == KeyEvent.KEY_PRESSED) {
|
if (t.getEventType() == KeyEvent.KEY_PRESSED) {
|
||||||
|
|
||||||
switch (t.getCode()) {
|
switch (t.getCode()) {
|
||||||
case LEFT:
|
case LEFT:
|
||||||
cycleSlideShowImage(-1);
|
cycleSlideShowImage(-1);
|
||||||
@ -219,25 +219,21 @@ public class SlideShowView extends DrawableViewBase implements TagUtils.TagListe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SlideShowView(GroupPane gp) {
|
@ThreadConfined(type = ThreadType.JFX)
|
||||||
super(gp);
|
|
||||||
FXMLConstructor.construct(this, "SlideShow.fxml");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@ThreadConfined(type = ThreadType.UI)
|
|
||||||
public void stopVideo() {
|
public void stopVideo() {
|
||||||
if (imageBorder.getCenter() instanceof MediaControl) {
|
if (imageBorder.getCenter() instanceof MediaControl) {
|
||||||
((MediaControl) imageBorder.getCenter()).stopVideo();
|
((MediaControl) imageBorder.getCenter()).stopVideo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc } */
|
||||||
@Override
|
@Override
|
||||||
synchronized public void setFile(final Long fileID) {
|
synchronized public void setFile(final Long fileID) {
|
||||||
super.setFile(fileID);
|
super.setFile(fileID);
|
||||||
if (this.getFileID() != null) {
|
|
||||||
getGroupPane().makeSelection(false, this.getFileID());
|
getFileID().ifPresent((Long id) -> {
|
||||||
}
|
getGroupPane().makeSelection(false, id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -252,43 +248,57 @@ public class SlideShowView extends DrawableViewBase implements TagUtils.TagListe
|
|||||||
imageBorder.setCenter(null);
|
imageBorder.setCenter(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc } */
|
||||||
@Override
|
@Override
|
||||||
protected Runnable getContentUpdateRunnable() {
|
protected Runnable getContentUpdateRunnable() {
|
||||||
if (getFile().isVideo()) {
|
|
||||||
|
return getFile().map(new Function<DrawableFile<?>, Runnable>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Runnable apply(DrawableFile<?> file) {
|
||||||
|
|
||||||
|
if (file.isVideo()) {
|
||||||
return () -> {
|
return () -> {
|
||||||
imageBorder.setCenter(MediaControl.create((VideoFile<?>) getFile()));
|
imageBorder.setCenter(MediaControl.create((VideoFile<?>) file));
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
ImageView imageView = new ImageView(((ImageFile<?>) getFile()).getFullSizeImage());
|
ImageView imageView = new ImageView(((ImageFile<?>) file).getFullSizeImage());
|
||||||
imageView.setPreserveRatio(true);
|
imageView.setPreserveRatio(true);
|
||||||
imageView.fitWidthProperty().bind(imageBorder.widthProperty().subtract(CAT_BORDER_WIDTH * 2));
|
imageView.fitWidthProperty().bind(imageBorder.widthProperty().subtract(CAT_BORDER_WIDTH * 2));
|
||||||
imageView.fitHeightProperty().bind(this.heightProperty().subtract(CAT_BORDER_WIDTH * 4).subtract(footer.heightProperty()).subtract(toolBar.heightProperty()));
|
imageView.fitHeightProperty().bind(heightProperty().subtract(CAT_BORDER_WIDTH * 4).subtract(footer.heightProperty()).subtract(toolBar.heightProperty()));
|
||||||
return () -> {
|
return () -> {
|
||||||
imageBorder.setCenter(imageView);
|
imageBorder.setCenter(imageView);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}).orElse(() -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc } */
|
||||||
@Override
|
@Override
|
||||||
protected String getTextForLabel() {
|
protected String getTextForLabel() {
|
||||||
return getFile().getName() + " " + getSupplementalText();
|
return getFile().map(file -> file.getName()).orElse("") + " " + getSupplementalText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cycle the image displayed in thes SlideShowview, to the next/previous one
|
||||||
|
* in the group.
|
||||||
|
*
|
||||||
|
* @param direction the direction to cycle:
|
||||||
|
* -1 => left / back
|
||||||
|
* 1 => right / forward
|
||||||
|
*/
|
||||||
@ThreadConfined(type = ThreadType.JFX)
|
@ThreadConfined(type = ThreadType.JFX)
|
||||||
private void cycleSlideShowImage(int d) {
|
synchronized private void cycleSlideShowImage(int direction) {
|
||||||
stopVideo();
|
stopVideo();
|
||||||
if (getFileID() != null) {
|
final int groupSize = getGroupPane().getGrouping().fileIds().size();
|
||||||
int index = getGroupPane().getGrouping().fileIds().indexOf(getFileID());
|
final Integer nextIndex = getFileID().map(fileID -> {
|
||||||
final int size = getGroupPane().getGrouping().fileIds().size();
|
final int currentIndex = getGroupPane().getGrouping().fileIds().indexOf(fileID);
|
||||||
index = (index + d) % size;
|
return (currentIndex + direction + groupSize) % groupSize;
|
||||||
if (index < 0) {
|
}).orElse(0);
|
||||||
index += size;
|
setFile(getGroupPane().getGrouping().fileIds().get(nextIndex)
|
||||||
}
|
);
|
||||||
setFile(getGroupPane().getGrouping().fileIds().get(index));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
setFile(getGroupPane().getGrouping().fileIds().get(0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -296,24 +306,26 @@ public class SlideShowView extends DrawableViewBase implements TagUtils.TagListe
|
|||||||
* of y"
|
* of y"
|
||||||
*/
|
*/
|
||||||
private String getSupplementalText() {
|
private String getSupplementalText() {
|
||||||
return " ( " + (getGroupPane().getGrouping().fileIds().indexOf(getFileID()) + 1) + " of " + getGroupPane().getGrouping().fileIds().size() + " in group )";
|
final ObservableList<Long> fileIds = getGroupPane().getGrouping().fileIds();
|
||||||
|
return getFileID().map(fileID -> " ( " + (fileIds.indexOf(fileID) + 1) + " of " + fileIds.size() + " in group )")
|
||||||
|
.orElse("");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc } */
|
||||||
@Override
|
@Override
|
||||||
@ThreadConfined(type = ThreadType.ANY)
|
@ThreadConfined(type = ThreadType.ANY)
|
||||||
public Category updateCategoryBorder() {
|
public Category updateCategory() {
|
||||||
final Category category = getFile().getCategory();
|
if (getFile().isPresent()) {
|
||||||
final Border border = hasHashHit() && (category == Category.ZERO)
|
final Category category = super.updateCategory();
|
||||||
? HASH_BORDER
|
|
||||||
: getCategoryBorder(category);
|
|
||||||
ToggleButton toggleForCategory = getToggleForCategory(category);
|
ToggleButton toggleForCategory = getToggleForCategory(category);
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
getCategoryBorderRegion().setBorder(border);
|
|
||||||
toggleForCategory.setSelected(true);
|
toggleForCategory.setSelected(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
return category;
|
return category;
|
||||||
|
} else {
|
||||||
|
return Category.ZERO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ToggleButton getToggleForCategory(Category category) {
|
private ToggleButton getToggleForCategory(Category category) {
|
||||||
@ -345,10 +357,12 @@ public class SlideShowView extends DrawableViewBase implements TagUtils.TagListe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
|
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
|
||||||
|
getFileID().ifPresent(fileID -> {
|
||||||
if (t1) {
|
if (t1) {
|
||||||
FileIDSelectionModel.getInstance().clearAndSelect(getFileID());
|
FileIDSelectionModel.getInstance().clearAndSelect(fileID);
|
||||||
new CategorizeAction().addTag(cat.getTagName(), "");
|
new CategorizeAction(getController()).addTag(getController().getTagsManager().getTagName(cat), "");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -31,7 +31,7 @@ import javafx.scene.image.ImageView;
|
|||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.DrawableGroup;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cell in the NavPanel tree that listens to its associated group's fileids
|
* A cell in the NavPanel tree that listens to its associated group's fileids
|
||||||
@ -107,7 +107,6 @@ class GroupTreeCell extends TreeCell<TreeNode> {
|
|||||||
setStyle("");
|
setStyle("");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (isNull(treeNode.getGroup())) {
|
if (isNull(treeNode.getGroup())) {
|
||||||
final String groupName = getGroupName();
|
final String groupName = getGroupName();
|
||||||
//"dummy" group in file system tree <=> a folder with no drawables
|
//"dummy" group in file system tree <=> a folder with no drawables
|
||||||
@ -141,7 +140,7 @@ class GroupTreeCell extends TreeCell<TreeNode> {
|
|||||||
|
|
||||||
private String getGroupName() {
|
private String getGroupName() {
|
||||||
return Optional.ofNullable(getItem())
|
return Optional.ofNullable(getItem())
|
||||||
.map((TreeNode t) -> StringUtils.defaultIfBlank(t.getPath(), DrawableGroup.getBlankGroupName()))
|
.map(treeNode -> StringUtils.defaultIfBlank(treeNode.getPath(), DrawableGroup.getBlankGroupName()))
|
||||||
.orElse("");
|
.orElse("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ import javafx.application.Platform;
|
|||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.scene.control.TreeItem;
|
import javafx.scene.control.TreeItem;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.DrawableGroup;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A node in the nav/hash tree. Manages inserts and removals. Has parents and
|
* A node in the nav/hash tree. Manages inserts and removals. Has parents and
|
||||||
|
@ -28,7 +28,6 @@ import javafx.beans.property.SimpleObjectProperty;
|
|||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.ComboBox;
|
import javafx.scene.control.ComboBox;
|
||||||
import javafx.scene.control.ListView;
|
import javafx.scene.control.ListView;
|
||||||
@ -45,8 +44,8 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
|||||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.DrawableGroup;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.GroupViewState;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display two trees. one shows all folders (groups) and calls out folders with
|
* Display two trees. one shows all folders (groups) and calls out folders with
|
||||||
@ -151,12 +150,15 @@ public class NavPanel extends TabPane {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
initHashTree();
|
|
||||||
initNavTree();
|
|
||||||
|
|
||||||
controller.getGroupManager().getAnalyzedGroups().addListener((ListChangeListener.Change<? extends DrawableGroup> change) -> {
|
controller.getGroupManager().getAnalyzedGroups().addListener((ListChangeListener.Change<? extends DrawableGroup> change) -> {
|
||||||
|
TreeItem<TreeNode> selectedItem = activeTreeProperty.get().getSelectionModel().getSelectedItem();
|
||||||
boolean wasPermuted = false;
|
boolean wasPermuted = false;
|
||||||
while (change.next()) {
|
while (change.next()) {
|
||||||
|
if (change.wasPermutated()) {
|
||||||
|
// Handle this afterward
|
||||||
|
wasPermuted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
for (DrawableGroup g : change.getAddedSubList()) {
|
for (DrawableGroup g : change.getAddedSubList()) {
|
||||||
insertIntoNavTree(g);
|
insertIntoNavTree(g);
|
||||||
if (g.getHashSetHitsCount() > 0) {
|
if (g.getHashSetHitsCount() > 0) {
|
||||||
@ -167,24 +169,19 @@ public class NavPanel extends TabPane {
|
|||||||
removeFromNavTree(g);
|
removeFromNavTree(g);
|
||||||
removeFromHashTree(g);
|
removeFromHashTree(g);
|
||||||
}
|
}
|
||||||
if (change.wasPermutated()) {
|
|
||||||
// Handle this afterward
|
|
||||||
wasPermuted = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wasPermuted) {
|
if (wasPermuted) {
|
||||||
rebuildTrees();
|
rebuildTrees();
|
||||||
|
}
|
||||||
|
if (selectedItem != null && selectedItem.getValue().getGroup() != null) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
setFocusedGroup(selectedItem.getValue().getGroup());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (DrawableGroup g : controller.getGroupManager().getAnalyzedGroups()) {
|
rebuildTrees();
|
||||||
insertIntoNavTree(g);
|
|
||||||
if (g.getHashSetHitsCount() > 0) {
|
|
||||||
insertIntoHashTree(g);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
controller.viewState().addListener((ObservableValue<? extends GroupViewState> observable, GroupViewState oldValue, GroupViewState newValue) -> {
|
controller.viewState().addListener((ObservableValue<? extends GroupViewState> observable, GroupViewState oldValue, GroupViewState newValue) -> {
|
||||||
if (newValue != null && newValue.getGroup() != null) {
|
if (newValue != null && newValue.getGroup() != null) {
|
||||||
@ -197,9 +194,7 @@ public class NavPanel extends TabPane {
|
|||||||
navTreeRoot = new GroupTreeItem("", null, sortByBox.getSelectionModel().selectedItemProperty().get());
|
navTreeRoot = new GroupTreeItem("", null, sortByBox.getSelectionModel().selectedItemProperty().get());
|
||||||
hashTreeRoot = new GroupTreeItem("", null, sortByBox.getSelectionModel().selectedItemProperty().get());
|
hashTreeRoot = new GroupTreeItem("", null, sortByBox.getSelectionModel().selectedItemProperty().get());
|
||||||
|
|
||||||
ObservableList<DrawableGroup> groups = controller.getGroupManager().getAnalyzedGroups();
|
for (DrawableGroup g : controller.getGroupManager().getAnalyzedGroups()) {
|
||||||
|
|
||||||
for (DrawableGroup g : groups) {
|
|
||||||
insertIntoNavTree(g);
|
insertIntoNavTree(g);
|
||||||
if (g.getHashSetHitsCount() > 0) {
|
if (g.getHashSetHitsCount() > 0) {
|
||||||
insertIntoHashTree(g);
|
insertIntoHashTree(g);
|
||||||
@ -262,7 +257,6 @@ public class NavPanel extends TabPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static List<String> groupingToPath(DrawableGroup g) {
|
private static List<String> groupingToPath(DrawableGroup g) {
|
||||||
|
|
||||||
if (g.groupKey.getAttribute() == DrawableAttribute.PATH) {
|
if (g.groupKey.getAttribute() == DrawableAttribute.PATH) {
|
||||||
String path = g.groupKey.getValueDisplayName();
|
String path = g.groupKey.getValueDisplayName();
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.imagegallery.gui.navpanel;
|
package org.sleuthkit.autopsy.imagegallery.gui.navpanel;
|
||||||
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.grouping.DrawableGroup;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Manifest-Version: 1.0
|
Manifest-Version: 1.0
|
||||||
AutoUpdate-Show-In-Client: true
|
AutoUpdate-Show-In-Client: true
|
||||||
OpenIDE-Module: org.sleuthkit.autopsy.keywordsearch/6
|
OpenIDE-Module: org.sleuthkit.autopsy.keywordsearch/6
|
||||||
OpenIDE-Module-Implementation-Version: 13
|
OpenIDE-Module-Implementation-Version: 14
|
||||||
OpenIDE-Module-Install: org/sleuthkit/autopsy/keywordsearch/Installer.class
|
OpenIDE-Module-Install: org/sleuthkit/autopsy/keywordsearch/Installer.class
|
||||||
OpenIDE-Module-Layer: org/sleuthkit/autopsy/keywordsearch/layer.xml
|
OpenIDE-Module-Layer: org/sleuthkit/autopsy/keywordsearch/layer.xml
|
||||||
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/keywordsearch/Bundle.properties
|
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/keywordsearch/Bundle.properties
|
||||||
|
10
NEWS.txt
10
NEWS.txt
@ -1,3 +1,13 @@
|
|||||||
|
---------------- VERSION 3.1.3 --------------
|
||||||
|
Improvements:
|
||||||
|
- New Embedded File Extractor module that incorporates ZIP file module and extracts images from Office documents
|
||||||
|
- Views area counts updates when ZIP files and such are found
|
||||||
|
- Updates to python scripting for new version of Python, scripts are reloaded each time ingest is run, and errors are better shown.
|
||||||
|
- Updated right click actions to be consistent accross all file types
|
||||||
|
- Changed logic of Interesting Files module to look for substrings of parent path.
|
||||||
|
- Lots of minor fixes and enhancements
|
||||||
|
|
||||||
|
|
||||||
---------------- VERSION 3.1.2 --------------
|
---------------- VERSION 3.1.2 --------------
|
||||||
Improvements:
|
Improvements:
|
||||||
- New PhotoRec carving ingest module
|
- New PhotoRec carving ingest module
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
Manifest-Version: 1.0
|
Manifest-Version: 1.0
|
||||||
OpenIDE-Module: org.sleuthkit.autopsy.recentactivity/6
|
OpenIDE-Module: org.sleuthkit.autopsy.recentactivity/6
|
||||||
OpenIDE-Module-Implementation-Version: 12
|
OpenIDE-Module-Implementation-Version: 13
|
||||||
OpenIDE-Module-Layer: org/sleuthkit/autopsy/recentactivity/layer.xml
|
OpenIDE-Module-Layer: org/sleuthkit/autopsy/recentactivity/layer.xml
|
||||||
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/recentactivity/Bundle.properties
|
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/recentactivity/Bundle.properties
|
||||||
OpenIDE-Module-Requires:
|
OpenIDE-Module-Requires:
|
||||||
|
@ -7,7 +7,7 @@ nbplatform.active.dir=${suite.dir}/netbeans-plat/${netbeans-plat-version}
|
|||||||
harness.dir=${nbplatform.active.dir}/harness
|
harness.dir=${nbplatform.active.dir}/harness
|
||||||
bootstrap.url=http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastStableBuild/artifact/nbbuild/netbeans/harness/tasks.jar
|
bootstrap.url=http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastStableBuild/artifact/nbbuild/netbeans/harness/tasks.jar
|
||||||
# Where we get the platform from. To see what versions are available, open URL in browser up to the .../updates part of the URL
|
# Where we get the platform from. To see what versions are available, open URL in browser up to the .../updates part of the URL
|
||||||
autoupdate.catalog.url=http://dlc.sun.com.edgesuite.net/netbeans/updates/${netbeans-plat-version}/uc/final/distribution/catalog.xml.gz
|
autoupdate.catalog.url=http://updates.netbeans.org/netbeans/updates/${netbeans-plat-version}/uc/final/distribution/catalog.xml.gz
|
||||||
cluster.path=\
|
cluster.path=\
|
||||||
${nbplatform.active.dir}/harness:\
|
${nbplatform.active.dir}/harness:\
|
||||||
${nbplatform.active.dir}/java:\
|
${nbplatform.active.dir}/java:\
|
||||||
|
@ -29,4 +29,13 @@ The results of carving show up on the tree under the appropriate data source wit
|
|||||||
\image html photorec_output.PNG
|
\image html photorec_output.PNG
|
||||||
|
|
||||||
Applicable types also show up in the "Views", "File Types" portion of the the tree, depending upon the file type.
|
Applicable types also show up in the "Views", "File Types" portion of the the tree, depending upon the file type.
|
||||||
|
|
||||||
|
Custom File Signatures
|
||||||
|
======
|
||||||
|
To add custom file signatures, create a file (if it does not exist) photorec.sig in the user home directory (for example - /home/john/photorec.sig, or C:\\Users\john\photorec.sig). The photorec.sig file should contain one expression per line.
|
||||||
|
For example, to detect a file foo.bar which has header signature - 0x4141414141414141, add an expression
|
||||||
|
|
||||||
|
bar 0 0x4141414141414141
|
||||||
|
in photorec.sig where *bar* is the file extension, *0* is the signature offset, and *0x4141414141414141* is the signature.
|
||||||
|
Add another expression on a new line to detect another custom file based on its signature.
|
||||||
*/
|
*/
|
@ -7,14 +7,18 @@ go into the page for that module type. -->
|
|||||||
This page describes the basic concepts and setup that are needed for all types of Python modules. It is not needed for Java module development.
|
This page describes the basic concepts and setup that are needed for all types of Python modules. It is not needed for Java module development.
|
||||||
|
|
||||||
Autopsy uses Jython (http://www.jython.org) to enable Python scripting. Jython looks like Python and gets converted into Java byte code and run on the JVM. Its biggest limitations are:
|
Autopsy uses Jython (http://www.jython.org) to enable Python scripting. Jython looks like Python and gets converted into Java byte code and run on the JVM. Its biggest limitations are:
|
||||||
- Limited to Python 2.5
|
- Limited to Python 2.7 (as of Autopsy 3.1.3)
|
||||||
- Can't use Python libraries that have native code
|
- Can't use Python libraries that have native code
|
||||||
- You can't make any UIs. This means that you can't make content viewer modules or have configuration settings for your ingest modules.
|
- You can't easily make UIs. This means that you can't make content viewer modules or easily have configuration settings for your ingest modules. We have done it, but it is tedious compared to using a Java tool to place UI widgets in various places.
|
||||||
|
|
||||||
Using it is very easy though in Autopsy and it allows you to access all of the Java services and classes that you need.
|
Using it is very easy though in Autopsy and it allows you to access all of the Java services and classes that you need.
|
||||||
|
|
||||||
To develop a module, you should follow this section to get your environment setup and then read the later sections on the different types of modules.
|
To develop a module, you should follow this section to get your environment setup and then read the later sections on the different types of modules.
|
||||||
|
|
||||||
|
There are also a set of tutorials that Basis Technology published on their blog:
|
||||||
|
|
||||||
|
* The File Ingest Module: http://www.basistech.com/python-autopsy-module-tutorial-1-the-file-ingest-module/
|
||||||
|
|
||||||
|
|
||||||
\section mod_dev_py_setup Basic Setup
|
\section mod_dev_py_setup Basic Setup
|
||||||
|
|
||||||
@ -29,10 +33,11 @@ Autopsy requires that you have a self-contained folder for each Python module.
|
|||||||
|
|
||||||
You will need to copy this folder into Autopsy's Python script folder. It will scan this folder each time it looks for modules. You can find the location of this folder from the "Tools -> Python Scripts" menu item.
|
You will need to copy this folder into Autopsy's Python script folder. It will scan this folder each time it looks for modules. You can find the location of this folder from the "Tools -> Python Scripts" menu item.
|
||||||
|
|
||||||
|
|
||||||
\subsection mod_dev_py_create_create Module Creation
|
\subsection mod_dev_py_create_create Module Creation
|
||||||
|
|
||||||
-# Create a folder
|
-# Create a folder
|
||||||
-# Add a .py file to it (see later sections for details on its contents)
|
-# Copy one of the sample modules from the github repository (listed below) or make one from scratch
|
||||||
-# Copy the folder to the previously mentioned folder to make updates during development.
|
-# Copy the folder to the previously mentioned folder to make updates during development.
|
||||||
|
|
||||||
That's it. Autopsy will find the module each time it needs it and you can make updates without having to restart Autopsy each time.
|
That's it. Autopsy will find the module each time it needs it and you can make updates without having to restart Autopsy each time.
|
||||||
@ -52,6 +57,11 @@ from neededLib.mylib import neededClass
|
|||||||
Jython will look in the module's folder to resolve these libraries.
|
Jython will look in the module's folder to resolve these libraries.
|
||||||
|
|
||||||
|
|
||||||
|
\subsection mod_dev_py_misc Minor Gotchas
|
||||||
|
This section lists some helpful tips that we have found. These are all now in the sample modules, so refer to those for examples and a place to copy and paste from.
|
||||||
|
- We haven't found a good way to debug while running inside of Autopsy. So, logging becomes critical. You need to go through a bunch of steps to get the logger to display your module name. See the sample module for a log() method that does all of this for you.
|
||||||
|
- When you name the file with your Python module in it, restrict its name to letters, numbers, and underscore (_).
|
||||||
|
|
||||||
\section mod_dev_py_distribute Distribution
|
\section mod_dev_py_distribute Distribution
|
||||||
To distribute and share your Python module, ZIP up the folder and send it around. Other users of the module should expand the ZIP file and drop the folder into their Autopsy Python folder.
|
To distribute and share your Python module, ZIP up the folder and send it around. Other users of the module should expand the ZIP file and drop the folder into their Autopsy Python folder.
|
||||||
|
|
||||||
@ -63,5 +73,7 @@ There are only two types of modules that you can make with Python. Those (along
|
|||||||
- Ingest Modules (both file-level and data source-level): https://github.com/sleuthkit/autopsy/blob/develop/pythonExamples/
|
- Ingest Modules (both file-level and data source-level): https://github.com/sleuthkit/autopsy/blob/develop/pythonExamples/
|
||||||
- Report Modules: https://github.com/sleuthkit/autopsy/blob/develop/pythonExamples/reportmodule.py
|
- Report Modules: https://github.com/sleuthkit/autopsy/blob/develop/pythonExamples/reportmodule.py
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ nbplatform.active.dir=${suite.dir}/netbeans-plat/${netbeans-plat-version}
|
|||||||
harness.dir=${nbplatform.active.dir}/harness
|
harness.dir=${nbplatform.active.dir}/harness
|
||||||
bootstrap.url=http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastStableBuild/artifact/nbbuild/netbeans/harness/tasks.jar
|
bootstrap.url=http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastStableBuild/artifact/nbbuild/netbeans/harness/tasks.jar
|
||||||
# Where we get the platform from. To see what versions are available, open URL in browser up to the .../updates part of the URL
|
# Where we get the platform from. To see what versions are available, open URL in browser up to the .../updates part of the URL
|
||||||
autoupdate.catalog.url=http://dlc.sun.com.edgesuite.net/netbeans/updates/${netbeans-plat-version}/uc/final/distribution/catalog.xml.gz
|
autoupdate.catalog.url=http://updates.netbeans.org/netbeans/updates/${netbeans-plat-version}/uc/final/distribution/catalog.xml.gz
|
||||||
cluster.path=\
|
cluster.path=\
|
||||||
${nbplatform.active.dir}/harness:\
|
${nbplatform.active.dir}/harness:\
|
||||||
${nbplatform.active.dir}/java:\
|
${nbplatform.active.dir}/java:\
|
||||||
|
@ -4,7 +4,7 @@ app.title=Autopsy
|
|||||||
### lowercase version of above
|
### lowercase version of above
|
||||||
app.name=${branding.token}
|
app.name=${branding.token}
|
||||||
### if left unset, version will default to today's date
|
### if left unset, version will default to today's date
|
||||||
app.version=3.1.2
|
app.version=3.1.3
|
||||||
### Build type isn't used at this point, but it may be useful
|
### Build type isn't used at this point, but it may be useful
|
||||||
### Must be one of: DEVELOPMENT, RELEASE
|
### Must be one of: DEVELOPMENT, RELEASE
|
||||||
#build.type=RELEASE
|
#build.type=RELEASE
|
||||||
|
@ -48,6 +48,7 @@ from org.sleuthkit.autopsy.ingest import FileIngestModule
|
|||||||
from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter
|
from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter
|
||||||
from org.sleuthkit.autopsy.ingest import IngestMessage
|
from org.sleuthkit.autopsy.ingest import IngestMessage
|
||||||
from org.sleuthkit.autopsy.ingest import IngestServices
|
from org.sleuthkit.autopsy.ingest import IngestServices
|
||||||
|
from org.sleuthkit.autopsy.ingest import ModuleDataEvent
|
||||||
from org.sleuthkit.autopsy.coreutils import Logger
|
from org.sleuthkit.autopsy.coreutils import Logger
|
||||||
from org.sleuthkit.autopsy.casemodule import Case
|
from org.sleuthkit.autopsy.casemodule import Case
|
||||||
from org.sleuthkit.autopsy.casemodule.services import Services
|
from org.sleuthkit.autopsy.casemodule.services import Services
|
||||||
@ -107,21 +108,29 @@ class SampleJythonFileIngestModule(FileIngestModule):
|
|||||||
# TODO: Add your analysis code in here.
|
# TODO: Add your analysis code in here.
|
||||||
def process(self, file):
|
def process(self, file):
|
||||||
# Skip non-files
|
# Skip non-files
|
||||||
if ((file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) or (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) or (file.isFile() == False)):
|
if ((file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) or
|
||||||
|
(file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) or
|
||||||
|
(file.isFile() == False)):
|
||||||
return IngestModule.ProcessResult.OK
|
return IngestModule.ProcessResult.OK
|
||||||
|
|
||||||
# For an example, we will flag files with .txt in the name and make a blackboard artifact.
|
# For an example, we will flag files with .txt in the name and make a blackboard artifact.
|
||||||
if file.getName().find(".txt") != -1:
|
if file.getName().lower().endswith(".txt"):
|
||||||
|
|
||||||
self.log(Level.INFO, "Found a text file: " + file.getName())
|
self.log(Level.INFO, "Found a text file: " + file.getName())
|
||||||
self.filesFound+=1
|
self.filesFound+=1
|
||||||
|
|
||||||
# Make an artifact on the blackboard. TSK_INTERESTING_FILE_HIT is a generic type of
|
# Make an artifact on the blackboard. TSK_INTERESTING_FILE_HIT is a generic type of
|
||||||
# artfiact. Refer to the developer docs for other examples.
|
# artifact. Refer to the developer docs for other examples.
|
||||||
art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT)
|
art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT)
|
||||||
att = BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), SampleJythonFileIngestModuleFactory.moduleName, "Text Files")
|
att = BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(),
|
||||||
|
SampleJythonFileIngestModuleFactory.moduleName, "Text Files")
|
||||||
art.addAttribute(att)
|
art.addAttribute(att)
|
||||||
|
|
||||||
|
# Fire an event to notify the UI and others that there is a new artifact
|
||||||
|
IngestServices.getInstance().fireModuleDataEvent(
|
||||||
|
ModuleDataEvent(SampleJythonFileIngestModuleFactory.moduleName,
|
||||||
|
BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, None));
|
||||||
|
|
||||||
# For the example (this wouldn't be needed normally), we'll query the blackboard for data that was added
|
# For the example (this wouldn't be needed normally), we'll query the blackboard for data that was added
|
||||||
# by other modules. We then iterate over its attributes. We'll just print them, but you would probably
|
# by other modules. We then iterate over its attributes. We'll just print them, but you would probably
|
||||||
# want to do something with them.
|
# want to do something with them.
|
||||||
@ -146,5 +155,7 @@ class SampleJythonFileIngestModule(FileIngestModule):
|
|||||||
# TODO: Add any shutdown code that you need here.
|
# TODO: Add any shutdown code that you need here.
|
||||||
def shutDown(self):
|
def shutDown(self):
|
||||||
# As a final part of this example, we'll send a message to the ingest inbox with the number of files found (in this thread)
|
# As a final part of this example, we'll send a message to the ingest inbox with the number of files found (in this thread)
|
||||||
message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, SampleJythonFileIngestModuleFactory.moduleName, str(self.filesFound) + " files found")
|
message = IngestMessage.createMessage(
|
||||||
|
IngestMessage.MessageType.DATA, SampleJythonFileIngestModuleFactory.moduleName,
|
||||||
|
str(self.filesFound) + " files found")
|
||||||
ingestServices = IngestServices.getInstance().postMessage(message)
|
ingestServices = IngestServices.getInstance().postMessage(message)
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
from java.lang import System
|
from java.lang import System
|
||||||
from org.sleuthkit.autopsy.casemodule import Case
|
from org.sleuthkit.autopsy.casemodule import Case
|
||||||
from org.sleuthkit.autopsy.report import GeneralReportModuleAdapter
|
from org.sleuthkit.autopsy.report import GeneralReportModuleAdapter
|
||||||
|
import os
|
||||||
|
|
||||||
# TODO: Rename this to something more specific
|
# TODO: Rename this to something more specific
|
||||||
class SampleGeneralReportModule(GeneralReportModuleAdapter):
|
class SampleGeneralReportModule(GeneralReportModuleAdapter):
|
||||||
@ -73,7 +74,7 @@ class SampleGeneralReportModule(GeneralReportModuleAdapter):
|
|||||||
progressBar.increment()
|
progressBar.increment()
|
||||||
|
|
||||||
# Write the result to the report file.
|
# Write the result to the report file.
|
||||||
report = open(baseReportDir + '\\' + self.getRelativeFilePath(), 'w')
|
report = open(os.path.join(baseReportDir, self.getRelativeFilePath()), 'w')
|
||||||
report.write("file count = %d" % fileCount)
|
report.write("file count = %d" % fileCount)
|
||||||
Case.getCurrentCase().addReport(report.name, "SampleGeneralReportModule", "Sample Python Report");
|
Case.getCurrentCase().addReport(report.name, "SampleGeneralReportModule", "Sample Python Report");
|
||||||
report.close()
|
report.close()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Manifest-Version: 1.0
|
Manifest-Version: 1.0
|
||||||
AutoUpdate-Show-In-Client: true
|
AutoUpdate-Show-In-Client: true
|
||||||
OpenIDE-Module: org.sleuthkit.autopsy.thunderbirdparser/4
|
OpenIDE-Module: org.sleuthkit.autopsy.thunderbirdparser/4
|
||||||
OpenIDE-Module-Implementation-Version: 13
|
OpenIDE-Module-Implementation-Version: 14
|
||||||
OpenIDE-Module-Layer: org/sleuthkit/autopsy/thunderbirdparser/layer.xml
|
OpenIDE-Module-Layer: org/sleuthkit/autopsy/thunderbirdparser/layer.xml
|
||||||
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/thunderbirdparser/Bundle.properties
|
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/thunderbirdparser/Bundle.properties
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ import org.sleuthkit.autopsy.ingest.IngestServices;
|
|||||||
try {
|
try {
|
||||||
Message msg = messageBuilder.parseMessage(message.asInputStream(theEncoder.charset()));
|
Message msg = messageBuilder.parseMessage(message.asInputStream(theEncoder.charset()));
|
||||||
emails.add(extractEmail(msg));
|
emails.add(extractEmail(msg));
|
||||||
} catch (IOException ex) {
|
} catch (RuntimeException | IOException ex) {
|
||||||
logger.log(Level.WARNING, "Failed to get message from mbox: {0}", ex.getMessage()); //NON-NLS
|
logger.log(Level.WARNING, "Failed to get message from mbox: {0}", ex.getMessage()); //NON-NLS
|
||||||
failCount++;
|
failCount++;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user