Merge remote-tracking branch 'upstream/develop' into 3973_DisableDataSourceSearch
@ -64,13 +64,14 @@ public final class ReplaceBlackboardArtifactTagAction extends ReplaceTagAction<B
|
||||
*
|
||||
* @param oldArtifactTag tag to be replaced
|
||||
* @param newTagName name of the tag to replace with
|
||||
* @param comment the comment for the tag use an empty string for no comment
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"# {0} - old tag name",
|
||||
"# {1} - artifactID",
|
||||
"ReplaceBlackboardArtifactTagAction.replaceTag.alert=Unable to replace tag {0} for artifact {1}."})
|
||||
@Override
|
||||
protected void replaceTag( BlackboardArtifactTag oldArtifactTag, TagName newTagName) {
|
||||
protected void replaceTag( BlackboardArtifactTag oldArtifactTag, TagName newTagName, String comment) {
|
||||
new SwingWorker<Void, Void>() {
|
||||
|
||||
@Override
|
||||
@ -90,7 +91,7 @@ public final class ReplaceBlackboardArtifactTagAction extends ReplaceTagAction<B
|
||||
logger.log(Level.INFO, "Replacing tag {0} with tag {1} for artifact {2}", new Object[]{oldArtifactTag.getName().getDisplayName(), newTagName.getDisplayName(), oldArtifactTag.getContent().getName()}); //NON-NLS
|
||||
|
||||
tagsManager.deleteBlackboardArtifactTag(oldArtifactTag);
|
||||
tagsManager.addBlackboardArtifactTag(oldArtifactTag.getArtifact(), newTagName);
|
||||
tagsManager.addBlackboardArtifactTag(oldArtifactTag.getArtifact(), newTagName, comment);
|
||||
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.SEVERE, "Error replacing artifact tag", tskCoreException); //NON-NLS
|
||||
|
@ -64,7 +64,7 @@ public final class ReplaceContentTagAction extends ReplaceTagAction<ContentTag>
|
||||
"# {1} - content obj id",
|
||||
"ReplaceContentTagAction.replaceTag.alert=Unable to replace tag {0} for {1}."})
|
||||
@Override
|
||||
protected void replaceTag(ContentTag oldTag, TagName newTagName) {
|
||||
protected void replaceTag(ContentTag oldTag, TagName newTagName, String comment) {
|
||||
new SwingWorker<Void, Void>() {
|
||||
|
||||
@Override
|
||||
@ -84,7 +84,7 @@ public final class ReplaceContentTagAction extends ReplaceTagAction<ContentTag>
|
||||
logger.log(Level.INFO, "Replacing tag {0} with tag {1} for artifact {2}", new Object[]{oldTag.getName().getDisplayName(), newTagName.getDisplayName(), oldTag.getContent().getName()}); //NON-NLS
|
||||
|
||||
tagsManager.deleteContentTag(oldTag);
|
||||
tagsManager.addContentTag(oldTag.getContent(), newTagName);
|
||||
tagsManager.addContentTag(oldTag.getContent(), newTagName, comment);
|
||||
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.SEVERE, "Error replacing artifact tag", tskCoreException); //NON-NLS
|
||||
|
@ -77,10 +77,11 @@ abstract class ReplaceTagAction<T extends Tag> extends AbstractAction implements
|
||||
/**
|
||||
* Method to actually replace the selected tag with the given new tag
|
||||
*
|
||||
* @param oldTag
|
||||
* @param newTagName
|
||||
* @param oldTag - the TagName which is being removed from the item
|
||||
* @param newTagName - the TagName which is being added to the itme
|
||||
* @param comment the comment associated with the tag, empty string for no comment
|
||||
*/
|
||||
abstract protected void replaceTag(T oldTag, TagName newTagName);
|
||||
abstract protected void replaceTag(T oldTag, TagName newTagName, String comment);
|
||||
|
||||
/**
|
||||
* Returns elected tags which are to be replaced
|
||||
@ -140,7 +141,7 @@ abstract class ReplaceTagAction<T extends Tag> extends AbstractAction implements
|
||||
// Add action to replace the tag
|
||||
tagNameItem.addActionListener((ActionEvent event) -> {
|
||||
selectedTags.forEach((oldtag) -> {
|
||||
replaceTag(oldtag, entry.getValue());
|
||||
replaceTag(oldtag, entry.getValue(), "");
|
||||
});
|
||||
});
|
||||
|
||||
@ -177,12 +178,24 @@ abstract class ReplaceTagAction<T extends Tag> extends AbstractAction implements
|
||||
TagName newTagName = GetTagNameDialog.doDialog();
|
||||
if (null != newTagName) {
|
||||
selectedTags.forEach((oldtag) -> {
|
||||
replaceTag(oldtag, newTagName);
|
||||
replaceTag(oldtag, newTagName, "");
|
||||
});
|
||||
}
|
||||
});
|
||||
add(newTagMenuItem);
|
||||
|
||||
// Create a "Choose Tag and Comment..." menu item. Selecting this item initiates
|
||||
// a dialog that can be used to create or select a tag name with an
|
||||
// optional comment and adds a tag with the resulting name.
|
||||
JMenuItem tagAndCommentItem = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.tagAndComment"));
|
||||
tagAndCommentItem.addActionListener((ActionEvent event) -> {
|
||||
GetTagNameAndCommentDialog.TagNameAndComment tagNameAndComment = GetTagNameAndCommentDialog.doDialog();
|
||||
if (null != tagNameAndComment) {
|
||||
selectedTags.forEach((oldtag) -> {
|
||||
replaceTag(oldtag, tagNameAndComment.getTagName(), tagNameAndComment.getComment());
|
||||
});
|
||||
}
|
||||
});
|
||||
add(tagAndCommentItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import javax.swing.Box.Filler;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||
@ -67,7 +68,7 @@ final class AddImageWizardSelectDspVisual extends JPanel {
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex);
|
||||
}
|
||||
|
||||
|
||||
//add actionlistner to listen for change
|
||||
}
|
||||
|
||||
@ -132,7 +133,7 @@ final class AddImageWizardSelectDspVisual extends JPanel {
|
||||
//Add the button
|
||||
JToggleButton dspButton = createDspButton(dspType);
|
||||
dspButton.addActionListener(cbActionListener);
|
||||
if ((Case.getCurrentCaseThrows().getCaseType() == Case.CaseType.MULTI_USER_CASE) && dspType.equals(LocalDiskDSProcessor.getType())){
|
||||
if ((Case.getCurrentCaseThrows().getCaseType() == Case.CaseType.MULTI_USER_CASE) && dspType.equals(LocalDiskDSProcessor.getType())) {
|
||||
dspButton.setEnabled(false); //disable the button for local disk DSP when this is a multi user case
|
||||
dspButton.setSelected(false);
|
||||
shouldAddMultiUserWarning = true;
|
||||
@ -172,6 +173,9 @@ final class AddImageWizardSelectDspVisual extends JPanel {
|
||||
constraints.weighty = 1;
|
||||
gridBagLayout.setConstraints(vertGlue, constraints);
|
||||
jPanel1.setLayout(gridBagLayout);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
jScrollPane1.getVerticalScrollBar().setValue(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,7 +38,7 @@ import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import static org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil.updateSchemaVersion;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor;
|
||||
import org.sleuthkit.autopsy.healthmonitor.HealthMonitor;
|
||||
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
|
||||
import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
@ -1004,8 +1004,8 @@ abstract class AbstractSqlEamDb implements EamDb {
|
||||
bulkArtifacts.get(type.getDbTableName()).clear();
|
||||
}
|
||||
|
||||
TimingMetric timingMetric = EnterpriseHealthMonitor.getTimingMetric("Correlation Engine: Bulk insert");
|
||||
EnterpriseHealthMonitor.submitTimingMetric(timingMetric);
|
||||
TimingMetric timingMetric = HealthMonitor.getTimingMetric("Correlation Engine: Bulk insert");
|
||||
HealthMonitor.submitTimingMetric(timingMetric);
|
||||
|
||||
// Reset state
|
||||
bulkArtifactsCount = 0;
|
||||
|
@ -49,7 +49,7 @@ import org.sleuthkit.datamodel.HashUtility;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.autopsy.centralrepository.eventlisteners.IngestEventsListener;
|
||||
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor;
|
||||
import org.sleuthkit.autopsy.healthmonitor.HealthMonitor;
|
||||
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
|
||||
|
||||
/**
|
||||
@ -135,9 +135,9 @@ final class IngestModule implements FileIngestModule {
|
||||
*/
|
||||
if (abstractFile.getKnown() != TskData.FileKnown.KNOWN && flagTaggedNotableItems) {
|
||||
try {
|
||||
TimingMetric timingMetric = EnterpriseHealthMonitor.getTimingMetric("Correlation Engine: Notable artifact query");
|
||||
TimingMetric timingMetric = HealthMonitor.getTimingMetric("Correlation Engine: Notable artifact query");
|
||||
List<String> caseDisplayNamesList = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5);
|
||||
EnterpriseHealthMonitor.submitTimingMetric(timingMetric);
|
||||
HealthMonitor.submitTimingMetric(timingMetric);
|
||||
if (!caseDisplayNamesList.isEmpty()) {
|
||||
postCorrelatedBadFileToBlackboard(abstractFile, caseDisplayNamesList);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ CommonFilesPanel.selectedFileCategoriesButton.text=Match on the following file c
|
||||
CommonFilesPanel.selectedFileCategoriesButton.toolTipText=Select from the options below...
|
||||
CommonFilesPanel.pictureVideoCheckbox.text=Pictures and Videos
|
||||
CommonFilesPanel.documentsCheckbox.text=Documents
|
||||
CommonFilesPanel.commonFilesSearchLabel.text=<html>Find duplicate files in the current case.</html>
|
||||
CommonFilesPanel.commonFilesSearchLabel.text=<html>Find files in multiple data sources in the current case.</html>
|
||||
CommonFilesPanel.allFileCategoriesRadioButton.toolTipText=No filtering applied to results...
|
||||
CommonFilesPanel.allFileCategoriesRadioButton.text=Match on all file types
|
||||
CommonFilesPanel.text=Indicate which data sources to consider while searching for duplicates:
|
||||
|
@ -104,12 +104,15 @@ public final class CommonFilesPanel extends javax.swing.JPanel {
|
||||
CommonFilesPanel.this.selectDataSourceComboBox.setModel(CommonFilesPanel.this.dataSourcesList);
|
||||
|
||||
boolean multipleDataSources = this.caseHasMultipleSources();
|
||||
CommonFilesPanel.this.allDataSourcesRadioButton.setEnabled(multipleDataSources);
|
||||
CommonFilesPanel.this.allDataSourcesRadioButton.setSelected(multipleDataSources);
|
||||
|
||||
|
||||
CommonFilesPanel.this.allDataSourcesRadioButton.setEnabled(true);
|
||||
CommonFilesPanel.this.allDataSourcesRadioButton.setSelected(true);
|
||||
|
||||
if (!multipleDataSources) {
|
||||
CommonFilesPanel.this.withinDataSourceRadioButton.setSelected(true);
|
||||
withinDataSourceSelected(true);
|
||||
CommonFilesPanel.this.withinDataSourceRadioButton.setEnabled(false);
|
||||
CommonFilesPanel.this.withinDataSourceRadioButton.setSelected(false);
|
||||
withinDataSourceSelected(false);
|
||||
CommonFilesPanel.this.selectDataSourceComboBox.setEnabled(false);
|
||||
}
|
||||
|
||||
CommonFilesPanel.this.searchButton.setEnabled(true);
|
||||
@ -120,7 +123,7 @@ public final class CommonFilesPanel extends javax.swing.JPanel {
|
||||
}
|
||||
|
||||
private boolean caseHasMultipleSources() {
|
||||
return CommonFilesPanel.this.dataSourceMap.size() >= 2;
|
||||
return CommonFilesPanel.this.dataSourceMap.size() >= 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,11 +19,14 @@
|
||||
package org.sleuthkit.autopsy.commonfilesearch;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.HelpCtx;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.actions.CallableSystemAction;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.core.Installer;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* Encapsulates a menu action which triggers the common files search dialog.
|
||||
@ -32,15 +35,22 @@ final public class CommonFilesSearchAction extends CallableSystemAction {
|
||||
|
||||
private static CommonFilesSearchAction instance = null;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CommonFilesSearchAction.class.getName());
|
||||
|
||||
CommonFilesSearchAction() {
|
||||
super();
|
||||
this.setEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(){
|
||||
return super.isEnabled() && Case.isCaseOpen() && Installer.isJavaFxInited();
|
||||
boolean shouldBeEnabled = false;
|
||||
try {
|
||||
shouldBeEnabled = Case.isCaseOpen() && Case.getCurrentCase().getDataSources().size() > 1;
|
||||
} catch(TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting data sources for action enabled check", ex);
|
||||
}
|
||||
return super.isEnabled() && shouldBeEnabled;
|
||||
}
|
||||
|
||||
public static synchronized CommonFilesSearchAction getDefault() {
|
||||
|
@ -38,24 +38,27 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable {
|
||||
|
||||
private static final Map<String, Integer> COLUMN_WIDTHS;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
static {
|
||||
Map<String, Integer> map = new HashMap<>();
|
||||
map.put(Bundle.CommonFilesSearchResultsViewerTable_matchColLbl(), 235);
|
||||
map.put(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), 260);
|
||||
map.put(Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), 65);
|
||||
map.put(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), 300);
|
||||
map.put(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), 200);
|
||||
map.put(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), 100);
|
||||
map.put(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), 150);
|
||||
map.put(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), 130);
|
||||
map.put(Bundle.CommonFilesSearchResultsViewerTable_tagsColLbl1(), 300);
|
||||
|
||||
COLUMN_WIDTHS = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"CommonFilesSearchResultsViewerTable.matchColLbl=Match",
|
||||
"CommonFilesSearchResultsViewerTable.filesColLbl=Files",
|
||||
"CommonFilesSearchResultsViewerTable.instancesColLbl=Instances",
|
||||
"CommonFilesSearchResultsViewerTable.pathColLbl=Parent Path",
|
||||
"CommonFilesSearchResultsViewerTable.hashsetHitsColLbl=Hash Set Hits",
|
||||
"CommonFilesSearchResultsViewerTable.dataSourceColLbl=Data Source",
|
||||
"CommonFilesSearchResultsViewerTable.dataSourceColLbl=Data Source(s)",
|
||||
"CommonFilesSearchResultsViewerTable.mimeTypeColLbl=MIME Type",
|
||||
"CommonFilesSearchResultsViewerTable.tagsColLbl1=Tags"
|
||||
})
|
||||
@ -68,7 +71,7 @@ public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable {
|
||||
|
||||
TableColumn column = columnsEnumerator.nextElement();
|
||||
|
||||
final Object headerValue = column.getHeaderValue();
|
||||
final String headerValue = column.getHeaderValue().toString();
|
||||
final Integer get = COLUMN_WIDTHS.get(headerValue);
|
||||
|
||||
column.setPreferredWidth(get);
|
||||
|
@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.commonfilesearch;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
@ -30,7 +28,7 @@ import org.sleuthkit.datamodel.AbstractFile;
|
||||
|
||||
/**
|
||||
* Used by the Common Files search feature to encapsulate instances of a given
|
||||
* MD5s matched in the search. These nodes will be children of <code>Md5Node</code>s.
|
||||
MD5s matched in the search. These nodes will be children of <code>Md5Node</code>s.
|
||||
*/
|
||||
public class FileInstanceNode extends FileNode {
|
||||
|
||||
@ -46,6 +44,8 @@ public class FileInstanceNode extends FileNode {
|
||||
public FileInstanceNode(AbstractFile fsContent, String dataSource) {
|
||||
super(fsContent);
|
||||
this.dataSource = dataSource;
|
||||
|
||||
this.setDisplayName(fsContent.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -73,64 +73,16 @@ public class FileInstanceNode extends FileNode {
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
fillPropertyMap(map, this);
|
||||
|
||||
final String NO_DESCR = Bundle.FileInstanceNode_createSheet_noDescription();
|
||||
for (CommonFilePropertyType propType : CommonFilePropertyType.values()) {
|
||||
final String propString = propType.toString();
|
||||
final Object property = map.get(propString);
|
||||
final NodeProperty<Object> nodeProperty = new NodeProperty<>(propString, propString, NO_DESCR, property);
|
||||
sheetSet.put(nodeProperty);
|
||||
}
|
||||
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, this.getContent().getName()));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, this.getContent().getParentPath()));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, getHashSetHitsForFile(this.getContent())));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource()));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, StringUtils.defaultString(this.getContent().getMIMEType())));
|
||||
|
||||
this.addTagProperty(sheetSet);
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill map with AbstractFile properties
|
||||
*
|
||||
* @param map map with preserved ordering, where property names/values are
|
||||
* put
|
||||
* @param node The item to get properties for.
|
||||
*/
|
||||
static private void fillPropertyMap(Map<String, Object> map, FileInstanceNode node) {
|
||||
|
||||
map.put(CommonFilePropertyType.ParentPath.toString(), node.getContent().getParentPath());
|
||||
map.put(CommonFilePropertyType.HashsetHits.toString(), getHashSetHitsForFile(node.getContent()));
|
||||
map.put(CommonFilePropertyType.DataSource.toString(), node.getDataSource());
|
||||
map.put(CommonFilePropertyType.MimeType.toString(), StringUtils.defaultString(node.getContent().getMIMEType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates the columns to be displayed for reach row represented by an
|
||||
* instance of this object.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"CommonFilePropertyType.pathColLbl=Parent Path",
|
||||
"CommonFilePropertyType.hashsetHitsColLbl=Hash Set Hits",
|
||||
"CommonFilePropertyType.dataSourceColLbl=Data Source",
|
||||
"CommonFilePropertyType.caseColLbl=Case",
|
||||
"CommonFilePropertyType.mimeTypeColLbl=MIME Type"
|
||||
})
|
||||
public enum CommonFilePropertyType {
|
||||
|
||||
ParentPath(Bundle.CommonFilePropertyType_pathColLbl()),
|
||||
HashsetHits(Bundle.CommonFilePropertyType_hashsetHitsColLbl()),
|
||||
DataSource(Bundle.CommonFilePropertyType_dataSourceColLbl()),
|
||||
MimeType(Bundle.CommonFilePropertyType_mimeTypeColLbl());
|
||||
|
||||
final private String displayString;
|
||||
|
||||
private CommonFilePropertyType(String displayString) {
|
||||
this.displayString = displayString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return displayString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ package org.sleuthkit.autopsy.commonfilesearch;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
@ -50,7 +49,7 @@ final public class InstanceCountNode extends DisplayableItemNode {
|
||||
* @param md5Metadata
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"InstanceCountNode.displayName=Matches with %s instances"
|
||||
"InstanceCountNode.displayName=Files with %s instances (%s)"
|
||||
})
|
||||
public InstanceCountNode(int instanceCount, List<Md5Metadata> md5Metadata) {
|
||||
super(Children.create(new Md5NodeFactory(md5Metadata), true));
|
||||
@ -58,7 +57,8 @@ final public class InstanceCountNode extends DisplayableItemNode {
|
||||
this.instanceCount = instanceCount;
|
||||
this.metadataList = md5Metadata;
|
||||
|
||||
this.setDisplayName(String.format(Bundle.InstanceCountNode_displayName(), Integer.toString(instanceCount)));
|
||||
this.setDisplayName(String.format(Bundle.InstanceCountNode_displayName(), Integer.toString(instanceCount), md5Metadata.size()));
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,51 +101,13 @@ final public class InstanceCountNode extends DisplayableItemNode {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
fillPropertyMap(map, this);
|
||||
|
||||
|
||||
final String NO_DESCR = Bundle.InstanceCountNode_createSheet_noDescription();
|
||||
for (InstanceCountNode.InstanceCountNodePropertyType propType : InstanceCountNode.InstanceCountNodePropertyType.values()) {
|
||||
final String propString = propType.toString();
|
||||
sheetSet.put(new NodeProperty<>(propString, propString, NO_DESCR, map.get(propString)));
|
||||
}
|
||||
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, ""));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), NO_DESCR, this.getInstanceCount()));
|
||||
return sheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill map with AbstractFile properties
|
||||
*
|
||||
* @param map map with preserved ordering, where property names/values are
|
||||
* put
|
||||
* @param node The item to get properties for.
|
||||
*/
|
||||
static private void fillPropertyMap(Map<String, Object> map, InstanceCountNode node) {
|
||||
map.put(InstanceCountNodePropertyType.Match.toString(), node.getInstanceCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fields which will appear in the tree table.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"InstanceCountNodePropertyType.matchCountColLbl1=Match"
|
||||
})
|
||||
public enum InstanceCountNodePropertyType{
|
||||
|
||||
Match(Bundle.InstanceCountNodePropertyType_matchCountColLbl1());
|
||||
|
||||
final private String displayString;
|
||||
|
||||
private InstanceCountNodePropertyType(String displayName){
|
||||
this.displayString = displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return this.displayString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ChildFactory which builds CommonFileParentNodes from the
|
||||
|
@ -19,9 +19,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.commonfilesearch;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
@ -52,6 +50,9 @@ public class Md5Node extends DisplayableItemNode {
|
||||
private final int commonFileCount;
|
||||
private final String dataSources;
|
||||
|
||||
@NbBundle.Messages({
|
||||
"Md5Node.Md5Node.format=MD5: %s"
|
||||
})
|
||||
/**
|
||||
* Create a Match node whose children will all have this object in common.
|
||||
* @param data the common feature, and the children
|
||||
@ -64,7 +65,8 @@ public class Md5Node extends DisplayableItemNode {
|
||||
this.dataSources = String.join(", ", data.getDataSources());
|
||||
this.md5Hash = data.getMd5();
|
||||
|
||||
this.setDisplayName(this.md5Hash);
|
||||
this.setDisplayName(String.format(Bundle.Md5Node_Md5Node_format(), this.md5Hash));
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,29 +103,15 @@ public class Md5Node extends DisplayableItemNode {
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
fillPropertyMap(map, this);
|
||||
|
||||
final String NO_DESCR = Bundle.Md5Node_createSheet_noDescription();
|
||||
for (Md5Node.CommonFileParentPropertyType propType : Md5Node.CommonFileParentPropertyType.values()) {
|
||||
final String propString = propType.toString();
|
||||
sheetSet.put(new NodeProperty<>(propString, propString, NO_DESCR, map.get(propString)));
|
||||
}
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, ""));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, ""));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, ""));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSources()));
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill map with AbstractFile properties
|
||||
*
|
||||
* @param map map with preserved ordering, where property names/values are
|
||||
* put
|
||||
* @param node The item to get properties for.
|
||||
*/
|
||||
static private void fillPropertyMap(Map<String, Object> map, Md5Node node) {
|
||||
//map.put(CommonFileParentPropertyType.Case.toString(), "");
|
||||
map.put(CommonFileParentPropertyType.DataSource.toString(), node.getDataSources());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
|
||||
@ -172,24 +160,4 @@ public class Md5Node extends DisplayableItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"CommonFileParentPropertyType.fileColLbl=File",
|
||||
"CommonFileParentPropertyType.instanceColLbl=Instance Count",
|
||||
"CommonFileParentPropertyType.caseColLbl=Case",
|
||||
"CommonFileParentPropertyType.dataSourceColLbl=Data Source"})
|
||||
public enum CommonFileParentPropertyType {
|
||||
|
||||
DataSource(Bundle.CommonFileParentPropertyType_dataSourceColLbl());
|
||||
|
||||
final private String displayString;
|
||||
|
||||
private CommonFileParentPropertyType(String displayString) {
|
||||
this.displayString = displayString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.displayString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
* but the initial method was needed only be viewers in
|
||||
* corecomponents and therefore can stay out of public API.
|
||||
*/
|
||||
class DataContentViewerUtility {
|
||||
public class DataContentViewerUtility {
|
||||
/**
|
||||
* Returns the first non-Blackboard Artifact from a Node.
|
||||
* Needed for (at least) Hex and Strings that want to view
|
||||
@ -39,7 +39,7 @@ class DataContentViewerUtility {
|
||||
* @param node Node passed into content viewer
|
||||
* @return highest priority content or null if there is no content
|
||||
*/
|
||||
static Content getDefaultContent(Node node) {
|
||||
public static Content getDefaultContent(Node node) {
|
||||
Content bbContentSeen = null;
|
||||
for (Content content : (node).getLookup().lookupAll(Content.class)) {
|
||||
if (content instanceof BlackboardArtifact) {
|
||||
|
@ -54,7 +54,6 @@ DataModelActionsFactory.srcFileInDir.text=View Source File in Directory
|
||||
DataModelActionsFactory.fileInDir.text=View File in Directory
|
||||
DataModelActionsFactory.viewNewWin.text=View in New Window
|
||||
DataModelActionsFactory.openExtViewer.text=Open in External Viewer
|
||||
DataModelActionsFactory.srfFileSameMD5.text=Search for files with the same MD5 hash
|
||||
DataSourcesNode.name=Data Sources
|
||||
DataSourcesNode.group_by_datasource.name=Data Source Files
|
||||
DataSourcesNode.createSheet.name.name=Name
|
||||
|
@ -55,7 +55,6 @@ DataModelActionsFactory.srcFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u
|
||||
DataModelActionsFactory.fileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a
|
||||
DataModelActionsFactory.viewNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6\u306b\u8868\u793a
|
||||
DataModelActionsFactory.openExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u306b\u8868\u793a
|
||||
DataModelActionsFactory.srfFileSameMD5.text=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22
|
||||
DataSourcesNode.name=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9
|
||||
DataSourcesNode.group_by_datasource.name=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb
|
||||
DataSourcesNode.createSheet.name.name=\u540d\u524d
|
||||
|
@ -38,7 +38,6 @@ import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.datamodel.Reports.ReportNode;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.directorytree.HashSearchAction;
|
||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
@ -76,8 +75,6 @@ public class DataModelActionsFactory {
|
||||
.getMessage(DataModelActionsFactory.class, "DataModelActionsFactory.viewNewWin.text");
|
||||
public static final String OPEN_IN_EXTERNAL_VIEWER = NbBundle
|
||||
.getMessage(DataModelActionsFactory.class, "DataModelActionsFactory.openExtViewer.text");
|
||||
public static final String SEARCH_FOR_FILES_SAME_MD5 = NbBundle
|
||||
.getMessage(DataModelActionsFactory.class, "DataModelActionsFactory.srfFileSameMD5.text");
|
||||
|
||||
public static List<Action> getActions(File file, boolean isArtifactSource) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
@ -88,7 +85,6 @@ public class DataModelActionsFactory {
|
||||
actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, fileNode));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(new HashSearchAction(SEARCH_FOR_FILES_SAME_MD5, fileNode));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
@ -367,7 +363,6 @@ public class DataModelActionsFactory {
|
||||
actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, tagNode));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(new HashSearchAction(SEARCH_FOR_FILES_SAME_MD5, tagNode));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
@ -404,7 +399,6 @@ public class DataModelActionsFactory {
|
||||
actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, tagNode));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(new HashSearchAction(SEARCH_FOR_FILES_SAME_MD5, tagNode));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
|
@ -34,7 +34,6 @@ import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.directorytree.HashSearchAction;
|
||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
|
||||
import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction;
|
||||
@ -166,7 +165,6 @@ public class FileNode extends AbstractFsContentNode<AbstractFile> {
|
||||
actionsList.add(null); // Creates an item separator
|
||||
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(new HashSearchAction(Bundle.FileNode_getActions_searchFilesSameMD5_text(), this));
|
||||
actionsList.add(null); // Creates an item separator
|
||||
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
|
@ -36,7 +36,6 @@ import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.directorytree.HashSearchAction;
|
||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
|
||||
import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction;
|
||||
@ -107,8 +106,6 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
|
||||
actionsList.add(null); // creates a menu separator
|
||||
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(new HashSearchAction(
|
||||
NbBundle.getMessage(this.getClass(), "LocalFileNode.getActions.searchFilesSameMd5.text"), this));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
|
||||
|
@ -401,10 +401,8 @@ public class DataResultFilterNode extends FilterNode {
|
||||
}
|
||||
Content c = ban.getLookup().lookup(File.class);
|
||||
Node n = null;
|
||||
boolean md5Action = false;
|
||||
if (c != null) {
|
||||
n = new FileNode((AbstractFile) c);
|
||||
md5Action = true;
|
||||
} else if ((c = ban.getLookup().lookup(Directory.class)) != null) {
|
||||
n = new DirectoryNode((Directory) c);
|
||||
} else if ((c = ban.getLookup().lookup(VirtualDirectory.class)) != null) {
|
||||
@ -438,10 +436,6 @@ public class DataResultFilterNode extends FilterNode {
|
||||
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.openInExtViewer.text"), n));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
if (md5Action) {
|
||||
actionsList.add(new HashSearchAction(
|
||||
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.searchFilesSameMd5.text"), n));
|
||||
}
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
actionsList.add(AddBlackboardArtifactTagAction.getInstance());
|
||||
|
@ -18,11 +18,10 @@
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="treeView" alignment="0" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="backButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="backButton" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="forwardButton" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="65" max="32767" attributes="0"/>
|
||||
<Component id="forwardButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="51" max="32767" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
@ -43,14 +42,14 @@
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="forwardButton" min="-2" pref="26" max="-2" attributes="1"/>
|
||||
<Component id="backButton" min="-2" pref="26" max="-2" attributes="1"/>
|
||||
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
||||
<Component id="forwardButton" max="32767" attributes="1"/>
|
||||
<Component id="backButton" min="-2" max="-2" attributes="1"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="treeView" pref="838" max="32767" attributes="0"/>
|
||||
<Component id="treeView" pref="843" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
@ -72,7 +71,7 @@
|
||||
<Component class="javax.swing.JButton" name="backButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/directorytree/btn_step_back.png"/>
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/directorytree/btn_step_back_large.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="DirectoryTreeTopComponent.backButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
@ -80,7 +79,7 @@
|
||||
<Property name="borderPainted" type="boolean" value="false"/>
|
||||
<Property name="contentAreaFilled" type="boolean" value="false"/>
|
||||
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/directorytree/btn_step_back_disabled.png"/>
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/directorytree/btn_step_back_disabled_large.png"/>
|
||||
</Property>
|
||||
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
|
||||
<Insets value="[2, 0, 2, 0]"/>
|
||||
@ -92,10 +91,10 @@
|
||||
<Dimension value="[5, 5]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[23, 23]"/>
|
||||
<Dimension value="[32, 32]"/>
|
||||
</Property>
|
||||
<Property name="rolloverIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/directorytree/btn_step_back_hover.png"/>
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/directorytree/btn_step_back_hover_large.png"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
@ -105,7 +104,7 @@
|
||||
<Component class="javax.swing.JButton" name="forwardButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/directorytree/btn_step_forward.png"/>
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/directorytree/btn_step_forward_large.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="DirectoryTreeTopComponent.forwardButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
@ -113,7 +112,7 @@
|
||||
<Property name="borderPainted" type="boolean" value="false"/>
|
||||
<Property name="contentAreaFilled" type="boolean" value="false"/>
|
||||
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/directorytree/btn_step_forward_disabled.png"/>
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/directorytree/btn_step_forward_disabled_large.png"/>
|
||||
</Property>
|
||||
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
|
||||
<Insets value="[2, 0, 2, 0]"/>
|
||||
@ -125,10 +124,10 @@
|
||||
<Dimension value="[5, 5]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[23, 23]"/>
|
||||
<Dimension value="[32, 32]"/>
|
||||
</Property>
|
||||
<Property name="rolloverIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/directorytree/btn_step_forward_hover.png"/>
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/directorytree/btn_step_forward_hover_large.png"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
|
@ -184,32 +184,32 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
|
||||
treeView.setBorder(null);
|
||||
|
||||
backButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back.png"))); // NOI18N
|
||||
backButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_large.png"))); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(backButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.backButton.text")); // NOI18N
|
||||
backButton.setBorderPainted(false);
|
||||
backButton.setContentAreaFilled(false);
|
||||
backButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_disabled.png"))); // NOI18N
|
||||
backButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_disabled_large.png"))); // NOI18N
|
||||
backButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
||||
backButton.setMaximumSize(new java.awt.Dimension(55, 100));
|
||||
backButton.setMinimumSize(new java.awt.Dimension(5, 5));
|
||||
backButton.setPreferredSize(new java.awt.Dimension(23, 23));
|
||||
backButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_hover.png"))); // NOI18N
|
||||
backButton.setPreferredSize(new java.awt.Dimension(32, 32));
|
||||
backButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back_hover_large.png"))); // NOI18N
|
||||
backButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
backButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
forwardButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward.png"))); // NOI18N
|
||||
forwardButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_large.png"))); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(forwardButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.forwardButton.text")); // NOI18N
|
||||
forwardButton.setBorderPainted(false);
|
||||
forwardButton.setContentAreaFilled(false);
|
||||
forwardButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_disabled.png"))); // NOI18N
|
||||
forwardButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_disabled_large.png"))); // NOI18N
|
||||
forwardButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
||||
forwardButton.setMaximumSize(new java.awt.Dimension(55, 100));
|
||||
forwardButton.setMinimumSize(new java.awt.Dimension(5, 5));
|
||||
forwardButton.setPreferredSize(new java.awt.Dimension(23, 23));
|
||||
forwardButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_hover.png"))); // NOI18N
|
||||
forwardButton.setPreferredSize(new java.awt.Dimension(32, 32));
|
||||
forwardButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_forward_hover_large.png"))); // NOI18N
|
||||
forwardButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
forwardButtonActionPerformed(evt);
|
||||
@ -231,11 +231,10 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(treeView)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 65, Short.MAX_VALUE)
|
||||
.addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 51, Short.MAX_VALUE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(showRejectedCheckBox)
|
||||
.addComponent(groupByDatasourceCheckBox))
|
||||
@ -252,11 +251,11 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
.addComponent(groupByDatasourceCheckBox))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))))
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
|
||||
.addComponent(forwardButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 838, Short.MAX_VALUE)
|
||||
.addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 843, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
@ -800,10 +799,23 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds the directory tree
|
||||
* Rebuilds the autopsy tree.
|
||||
*
|
||||
* Does nothing if there is no open case.
|
||||
*/
|
||||
private void rebuildTree() {
|
||||
|
||||
// if no open case or has no data then there is no tree to rebuild
|
||||
Case currentCase;
|
||||
try {
|
||||
currentCase = Case.getCurrentCaseThrows();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
return;
|
||||
}
|
||||
if (null == currentCase || currentCase.hasData() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// refresh all children of the root.
|
||||
autopsyTreeChildrenFactory.refreshChildren();
|
||||
|
||||
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.directorytree;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.swing.AbstractAction;
|
||||
import org.openide.nodes.Node;
|
||||
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbSearchAction;
|
||||
|
||||
/**
|
||||
* Action to lookup the interface and call the real action in HashDatabase. The
|
||||
* real action, HashDbSearchAction, implements HashSearchProvider, and should be
|
||||
* the only instance of it.
|
||||
*
|
||||
* //TODO: HashDBSearchAction needs a public constructor and a service
|
||||
* registration annotation for the lookup technique to work
|
||||
*/
|
||||
@Immutable
|
||||
public class HashSearchAction extends AbstractAction {
|
||||
|
||||
private final Node contentNode;
|
||||
|
||||
public HashSearchAction(String title, Node contentNode) {
|
||||
super(title);
|
||||
this.contentNode = contentNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
//HashSearchProvider searcher = Lookup.getDefault().lookup(HashSearchProvider.class);
|
||||
//TODO: HashDBSearchAction needs a public constructor and a service registration annotation for the above technique to work
|
||||
HashDbSearchAction searcher = HashDbSearchAction.getDefault();
|
||||
searcher.search(contentNode);
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
@ -61,8 +61,8 @@ public class HealthMonitorDashboard {
|
||||
private final static String ADMIN_ACCESS_FILE_NAME = "adminAccess"; // NON-NLS
|
||||
private final static String ADMIN_ACCESS_FILE_PATH = Paths.get(Places.getUserDirectory().getAbsolutePath(), ADMIN_ACCESS_FILE_NAME).toString();
|
||||
|
||||
Map<String, List<EnterpriseHealthMonitor.DatabaseTimingResult>> timingData;
|
||||
List<EnterpriseHealthMonitor.UserData> userData;
|
||||
Map<String, List<HealthMonitor.DatabaseTimingResult>> timingData;
|
||||
List<HealthMonitor.UserData> userData;
|
||||
|
||||
private JComboBox<String> timingDateComboBox = null;
|
||||
private JComboBox<String> timingHostComboBox = null;
|
||||
@ -90,7 +90,7 @@ public class HealthMonitorDashboard {
|
||||
* Display the dashboard.
|
||||
*/
|
||||
@NbBundle.Messages({"HealthMonitorDashboard.display.errorCreatingDashboard=Error creating health monitor dashboard",
|
||||
"HealthMonitorDashboard.display.dashboardTitle=Enterprise Health Monitor"})
|
||||
"HealthMonitorDashboard.display.dashboardTitle=Health Monitor"})
|
||||
public void display() {
|
||||
|
||||
// Update the enabled status and get the timing data, then create all
|
||||
@ -153,14 +153,14 @@ public class HealthMonitorDashboard {
|
||||
private void updateData() throws HealthMonitorException {
|
||||
|
||||
// Update the monitor status
|
||||
EnterpriseHealthMonitor.getInstance().updateFromGlobalEnabledStatus();
|
||||
HealthMonitor.getInstance().updateFromGlobalEnabledStatus();
|
||||
|
||||
if(EnterpriseHealthMonitor.monitorIsEnabled()) {
|
||||
if(HealthMonitor.monitorIsEnabled()) {
|
||||
// Get a copy of the timing data from the database
|
||||
timingData = EnterpriseHealthMonitor.getInstance().getTimingMetricsFromDatabase(DateRange.getMaximumTimestampRange());
|
||||
timingData = HealthMonitor.getInstance().getTimingMetricsFromDatabase(DateRange.getMaximumTimestampRange());
|
||||
|
||||
// Get a copy of the user data from the database
|
||||
userData = EnterpriseHealthMonitor.getInstance().getUserMetricsFromDatabase(DateRange.getMaximumTimestampRange());
|
||||
userData = HealthMonitor.getInstance().getUserMetricsFromDatabase(DateRange.getMaximumTimestampRange());
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ public class HealthMonitorDashboard {
|
||||
private JPanel createTimingPanel() throws HealthMonitorException {
|
||||
|
||||
// If the monitor isn't enabled, just add a message
|
||||
if(! EnterpriseHealthMonitor.monitorIsEnabled()) {
|
||||
if(! HealthMonitor.monitorIsEnabled()) {
|
||||
//timingMetricPanel.setPreferredSize(new Dimension(400,100));
|
||||
JPanel emptyTimingMetricPanel = new JPanel();
|
||||
emptyTimingMetricPanel.add(new JLabel(Bundle.HealthMonitorDashboard_createTimingPanel_timingMetricsTitle()));
|
||||
@ -223,7 +223,7 @@ public class HealthMonitorDashboard {
|
||||
JPanel timingControlPanel = new JPanel();
|
||||
|
||||
// If the monitor is not enabled, don't add any components
|
||||
if(! EnterpriseHealthMonitor.monitorIsEnabled()) {
|
||||
if(! HealthMonitor.monitorIsEnabled()) {
|
||||
return timingControlPanel;
|
||||
}
|
||||
|
||||
@ -247,7 +247,7 @@ public class HealthMonitorDashboard {
|
||||
// Create an array of host names
|
||||
Set<String> hostNameSet = new HashSet<>();
|
||||
for(String metricType:timingData.keySet()) {
|
||||
for(EnterpriseHealthMonitor.DatabaseTimingResult result: timingData.get(metricType)) {
|
||||
for(HealthMonitor.DatabaseTimingResult result: timingData.get(metricType)) {
|
||||
hostNameSet.add(result.getHostName());
|
||||
}
|
||||
}
|
||||
@ -364,7 +364,7 @@ public class HealthMonitorDashboard {
|
||||
for(String metricName:timingData.keySet()) {
|
||||
|
||||
// If necessary, trim down the list of results to fit the selected time range
|
||||
List<EnterpriseHealthMonitor.DatabaseTimingResult> intermediateTimingDataForDisplay;
|
||||
List<HealthMonitor.DatabaseTimingResult> intermediateTimingDataForDisplay;
|
||||
if(timingDateComboBox.getSelectedItem() != null) {
|
||||
DateRange selectedDateRange = DateRange.fromLabel(timingDateComboBox.getSelectedItem().toString());
|
||||
long threshold = System.currentTimeMillis() - selectedDateRange.getTimestampRange();
|
||||
@ -403,7 +403,7 @@ public class HealthMonitorDashboard {
|
||||
"HealthMonitorDashboard.createUserPanel.userMetricsTitle=User Metrics"})
|
||||
private JPanel createUserPanel() throws HealthMonitorException {
|
||||
// If the monitor isn't enabled, just add a message
|
||||
if(! EnterpriseHealthMonitor.monitorIsEnabled()) {
|
||||
if(! HealthMonitor.monitorIsEnabled()) {
|
||||
JPanel emptyUserMetricPanel = new JPanel();
|
||||
emptyUserMetricPanel.add(new JLabel(Bundle.HealthMonitorDashboard_createUserPanel_userMetricsTitle()));
|
||||
emptyUserMetricPanel.add(new JLabel(" "));
|
||||
@ -448,7 +448,7 @@ public class HealthMonitorDashboard {
|
||||
JPanel userControlPanel = new JPanel();
|
||||
|
||||
// If the monitor is not enabled, don't add any components
|
||||
if(! EnterpriseHealthMonitor.monitorIsEnabled()) {
|
||||
if(! HealthMonitor.monitorIsEnabled()) {
|
||||
return userControlPanel;
|
||||
}
|
||||
|
||||
@ -535,7 +535,7 @@ public class HealthMonitorDashboard {
|
||||
JButton enableButton = new JButton(Bundle.HealthMonitorDashboard_createAdminPanel_enableButton());
|
||||
JButton disableButton = new JButton(Bundle.HealthMonitorDashboard_createAdminPanel_disableButton());
|
||||
|
||||
boolean isEnabled = EnterpriseHealthMonitor.monitorIsEnabled();
|
||||
boolean isEnabled = HealthMonitor.monitorIsEnabled();
|
||||
enableButton.setEnabled(! isEnabled);
|
||||
disableButton.setEnabled(isEnabled);
|
||||
|
||||
@ -545,7 +545,7 @@ public class HealthMonitorDashboard {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
try {
|
||||
dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
EnterpriseHealthMonitor.setEnabled(true);
|
||||
HealthMonitor.setEnabled(true);
|
||||
redisplay();
|
||||
} catch (HealthMonitorException ex) {
|
||||
logger.log(Level.SEVERE, "Error enabling monitoring", ex);
|
||||
@ -561,7 +561,7 @@ public class HealthMonitorDashboard {
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
try {
|
||||
dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
EnterpriseHealthMonitor.setEnabled(false);
|
||||
HealthMonitor.setEnabled(false);
|
||||
redisplay();
|
||||
} catch (HealthMonitorException ex) {
|
||||
logger.log(Level.SEVERE, "Error disabling monitoring", ex);
|
||||
|
@ -45,7 +45,7 @@ public class Installer extends ModuleInstall {
|
||||
public void restored() {
|
||||
|
||||
try {
|
||||
EnterpriseHealthMonitor.startUpIfEnabled();
|
||||
HealthMonitor.startUpIfEnabled();
|
||||
} catch (HealthMonitorException ex) {
|
||||
logger.log(Level.SEVERE, "Error starting health services monitor", ex);
|
||||
}
|
||||
@ -54,7 +54,7 @@ public class Installer extends ModuleInstall {
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
EnterpriseHealthMonitor.shutdown();
|
||||
HealthMonitor.shutdown();
|
||||
} catch (HealthMonitorException ex) {
|
||||
logger.log(Level.SEVERE, "Error stopping health services monitor", ex);
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ import java.util.logging.Level;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor.DatabaseTimingResult;
|
||||
import org.sleuthkit.autopsy.healthmonitor.HealthMonitor.DatabaseTimingResult;
|
||||
|
||||
/**
|
||||
* Creates a graph of the given timing metric data
|
||||
|
@ -38,7 +38,7 @@ import javax.swing.JPanel;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor.UserData;
|
||||
import org.sleuthkit.autopsy.healthmonitor.HealthMonitor.UserData;
|
||||
|
||||
/**
|
||||
* Creates graphs using the given user metric data
|
||||
|
@ -33,7 +33,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.Blackboard;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor;
|
||||
import org.sleuthkit.autopsy.healthmonitor.HealthMonitor;
|
||||
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
|
||||
import org.sleuthkit.autopsy.ingest.FileIngestModule;
|
||||
import org.sleuthkit.autopsy.ingest.IngestMessage;
|
||||
@ -184,7 +184,7 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
String md5Hash = file.getMd5Hash();
|
||||
if (md5Hash == null || md5Hash.isEmpty()) {
|
||||
try {
|
||||
TimingMetric metric = EnterpriseHealthMonitor.getTimingMetric("Disk Reads: Hash calculation");
|
||||
TimingMetric metric = HealthMonitor.getTimingMetric("Disk Reads: Hash calculation");
|
||||
long calcstart = System.currentTimeMillis();
|
||||
md5Hash = HashUtility.calculateMd5Hash(file);
|
||||
if (file.getSize() > 0) {
|
||||
@ -192,10 +192,10 @@ public class HashDbIngestModule implements FileIngestModule {
|
||||
// strongly with file size until the files get large.
|
||||
// Only normalize if the file size is greater than ~1MB.
|
||||
if (file.getSize() < 1000000) {
|
||||
EnterpriseHealthMonitor.submitTimingMetric(metric);
|
||||
HealthMonitor.submitTimingMetric(metric);
|
||||
} else {
|
||||
// In testing, this normalization gave reasonable resuls
|
||||
EnterpriseHealthMonitor.submitNormalizedTimingMetric(metric, file.getSize() / 500000);
|
||||
HealthMonitor.submitNormalizedTimingMetric(metric, file.getSize() / 500000);
|
||||
}
|
||||
}
|
||||
file.setMd5Hash(md5Hash);
|
||||
|
@ -37,7 +37,7 @@ import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.MetaTypeCond
|
||||
*/
|
||||
public final class FilesSetsManager extends Observable {
|
||||
|
||||
@NbBundle.Messages({"FilesSetsManager.allFilesAndDirectories=All Files and Directories",
|
||||
@NbBundle.Messages({"FilesSetsManager.allFilesAndDirectories=All Files and Directories (Not Unallocated Space)",
|
||||
"FilesSetsManager.allFilesDirectoriesAndUnallocated=All Files, Directories, and Unallocated Space"})
|
||||
private static final List<String> ILLEGAL_FILE_NAME_CHARS = Collections.unmodifiableList(new ArrayList<>(Arrays.asList("\\", "/", ":", "*", "?", "\"", "<", ">")));
|
||||
private static final List<String> ILLEGAL_FILE_PATH_CHARS = Collections.unmodifiableList(new ArrayList<>(Arrays.asList("\\", ":", "*", "?", "\"", "<", ">")));
|
||||
|
@ -380,6 +380,11 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
|
||||
throw new AutoIngestMonitorException("Error removing priority for job " + job.toString(), ex);
|
||||
}
|
||||
job.setPriority(DEFAULT_PRIORITY);
|
||||
|
||||
/**
|
||||
* Update job object in pending jobs queue
|
||||
*/
|
||||
jobsSnapshot.addOrReplacePendingJob(job);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -428,6 +433,11 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
|
||||
throw new AutoIngestMonitorException("Error bumping priority for job " + job.toString(), ex);
|
||||
}
|
||||
job.setPriority(highestPriority);
|
||||
|
||||
/**
|
||||
* Update job object in pending jobs queue
|
||||
*/
|
||||
jobsSnapshot.addOrReplacePendingJob(job);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -466,7 +476,7 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
|
||||
}
|
||||
|
||||
/*
|
||||
* If the job was still in the pending jobs queue, bump its
|
||||
* If the job was still in the pending jobs queue, reset its
|
||||
* priority.
|
||||
*/
|
||||
if (null != jobToDeprioritize) {
|
||||
@ -480,6 +490,11 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
|
||||
}
|
||||
jobToDeprioritize.setPriority(DEFAULT_PRIORITY);
|
||||
|
||||
/**
|
||||
* Update job object in pending jobs queue
|
||||
*/
|
||||
jobsSnapshot.addOrReplacePendingJob(jobToDeprioritize);
|
||||
|
||||
/*
|
||||
* Publish a deprioritization event.
|
||||
*/
|
||||
@ -538,6 +553,11 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
|
||||
}
|
||||
jobToPrioritize.setPriority(highestPriority);
|
||||
|
||||
/**
|
||||
* Update job object in pending jobs queue
|
||||
*/
|
||||
jobsSnapshot.addOrReplacePendingJob(jobToPrioritize);
|
||||
|
||||
/*
|
||||
* Publish a prioritization event.
|
||||
*/
|
||||
|
@ -102,6 +102,7 @@ public class SharedConfiguration {
|
||||
private boolean hideKnownFilesInViews;
|
||||
private boolean hideSlackFilesInDataSource;
|
||||
private boolean hideSlackFilesInViews;
|
||||
private boolean groupDatasources;
|
||||
private boolean keepPreferredViewer;
|
||||
|
||||
/**
|
||||
@ -206,6 +207,8 @@ public class SharedConfiguration {
|
||||
uploadHashDbSettings(remoteFolder);
|
||||
uploadFileExporterSettings(remoteFolder);
|
||||
uploadCentralRepositorySettings(remoteFolder);
|
||||
uploadObjectDetectionClassifiers(remoteFolder);
|
||||
uploadPythonModules(remoteFolder);
|
||||
|
||||
try {
|
||||
Files.deleteIfExists(uploadInProgress.toPath());
|
||||
@ -271,6 +274,8 @@ public class SharedConfiguration {
|
||||
downloadAndroidTriageSettings(remoteFolder);
|
||||
downloadFileExporterSettings(remoteFolder);
|
||||
downloadCentralRepositorySettings(remoteFolder);
|
||||
downloadObjectDetectionClassifiers(remoteFolder);
|
||||
downloadPythonModules(remoteFolder);
|
||||
|
||||
// Download general settings, then restore the current
|
||||
// values for the unshared fields
|
||||
@ -348,6 +353,7 @@ public class SharedConfiguration {
|
||||
fileIngestThreads = UserPreferences.numberOfFileIngestThreads();
|
||||
hideSlackFilesInDataSource = UserPreferences.hideSlackFilesInDataSourcesTree();
|
||||
hideSlackFilesInViews = UserPreferences.hideSlackFilesInViewsTree();
|
||||
groupDatasources = UserPreferences.groupItemsInTreeByDatasource();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -364,6 +370,7 @@ public class SharedConfiguration {
|
||||
UserPreferences.setNumberOfFileIngestThreads(fileIngestThreads);
|
||||
UserPreferences.setHideSlackFilesInDataSourcesTree(hideSlackFilesInDataSource);
|
||||
UserPreferences.setHideSlackFilesInViewsTree(hideSlackFilesInViews);
|
||||
UserPreferences.setGroupItemsInTreeByDatasource(groupDatasources);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -511,6 +518,71 @@ public class SharedConfiguration {
|
||||
throw new SharedConfigurationException(String.format("Failed to copy %s to %s", remoteFile.getAbsolutePath(), localSettingsFolder.getAbsolutePath()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an entire local settings folder to the remote folder, deleting any existing files.
|
||||
*
|
||||
* @param localFolder The local folder to copy
|
||||
* @param remoteBaseFolder The remote folder that will hold a copy of the original folder
|
||||
*
|
||||
* @throws SharedConfigurationException
|
||||
*/
|
||||
private void copyLocalFolderToRemoteFolder(File localFolder, File remoteBaseFolder) throws SharedConfigurationException {
|
||||
logger.log(Level.INFO, "Uploading {0} to {1}", new Object[]{localFolder.getAbsolutePath(), remoteBaseFolder.getAbsolutePath()});
|
||||
|
||||
File newRemoteFolder = new File(remoteBaseFolder, localFolder.getName());
|
||||
|
||||
if(newRemoteFolder.exists()) {
|
||||
try {
|
||||
FileUtils.deleteDirectory(newRemoteFolder);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to delete remote folder {0}", newRemoteFolder.getAbsolutePath());
|
||||
throw new SharedConfigurationException(String.format("Failed to delete remote folder {0}", newRemoteFolder.getAbsolutePath()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
FileUtils.copyDirectoryToDirectory(localFolder, remoteBaseFolder);
|
||||
} catch (IOException ex) {
|
||||
throw new SharedConfigurationException(String.format("Failed to copy %s to %s", localFolder, remoteBaseFolder.getAbsolutePath()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an entire remote settings folder to the local folder, deleting any existing files.
|
||||
* No error if the remote folder does not exist.
|
||||
*
|
||||
* @param localFolder The local folder that will be overwritten.
|
||||
* @param remoteBaseFolder The remote folder holding the folder that will be copied
|
||||
*
|
||||
* @throws SharedConfigurationException
|
||||
*/
|
||||
private void copyRemoteFolderToLocalFolder(File localFolder, File remoteBaseFolder) throws SharedConfigurationException {
|
||||
logger.log(Level.INFO, "Downloading {0} from {1}", new Object[]{localFolder.getAbsolutePath(), remoteBaseFolder.getAbsolutePath()});
|
||||
|
||||
// Clean out the local folder regardless of whether the remote version exists. leave the
|
||||
// folder in place since Autopsy expects it to exist.
|
||||
if(localFolder.exists()) {
|
||||
try {
|
||||
FileUtils.cleanDirectory(localFolder);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to delete files from local folder {0}", localFolder.getAbsolutePath());
|
||||
throw new SharedConfigurationException(String.format("Failed to delete files from local folder {0}", localFolder.getAbsolutePath()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
File remoteSubFolder = new File(remoteBaseFolder, localFolder.getName());
|
||||
if(! remoteSubFolder.exists()) {
|
||||
logger.log(Level.INFO, "{0} does not exist", remoteSubFolder.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
FileUtils.copyDirectory(remoteSubFolder, localFolder);
|
||||
} catch (IOException ex) {
|
||||
throw new SharedConfigurationException(String.format("Failed to copy %s from %s", localFolder, remoteBaseFolder.getAbsolutePath()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload the basic set of auto-ingest settings to the shared folder.
|
||||
@ -828,6 +900,58 @@ public class SharedConfiguration {
|
||||
copyToLocalFolder(AUTO_INGEST_PROPERTIES, moduleDirPath, remoteFolder, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload the object detection classifiers.
|
||||
*
|
||||
* @param remoteFolder Shared settings folder
|
||||
*
|
||||
* @throws SharedConfigurationException
|
||||
*/
|
||||
private void uploadObjectDetectionClassifiers(File remoteFolder) throws SharedConfigurationException {
|
||||
publishTask("Uploading object detection classfiers");
|
||||
File classifiersFolder = new File(PlatformUtil.getObjectDetectionClassifierPath());
|
||||
copyLocalFolderToRemoteFolder(classifiersFolder, remoteFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the object detection classifiers.
|
||||
*
|
||||
* @param remoteFolder Shared settings folder
|
||||
*
|
||||
* @throws SharedConfigurationException
|
||||
*/
|
||||
private void downloadObjectDetectionClassifiers(File remoteFolder) throws SharedConfigurationException {
|
||||
publishTask("Downloading object detection classfiers");
|
||||
File classifiersFolder = new File(PlatformUtil.getObjectDetectionClassifierPath());
|
||||
copyRemoteFolderToLocalFolder(classifiersFolder, remoteFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload the Python modules.
|
||||
*
|
||||
* @param remoteFolder Shared settings folder
|
||||
*
|
||||
* @throws SharedConfigurationException
|
||||
*/
|
||||
private void uploadPythonModules(File remoteFolder) throws SharedConfigurationException {
|
||||
publishTask("Uploading python modules");
|
||||
File classifiersFolder = new File(PlatformUtil.getUserPythonModulesPath());
|
||||
copyLocalFolderToRemoteFolder(classifiersFolder, remoteFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the Python modules.
|
||||
*
|
||||
* @param remoteFolder Shared settings folder
|
||||
*
|
||||
* @throws SharedConfigurationException
|
||||
*/
|
||||
private void downloadPythonModules(File remoteFolder) throws SharedConfigurationException {
|
||||
publishTask("Downloading python modules");
|
||||
File classifiersFolder = new File(PlatformUtil.getUserPythonModulesPath());
|
||||
copyRemoteFolderToLocalFolder(classifiersFolder, remoteFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload settings and hash databases to the shared folder. The general
|
||||
* algorithm is: - Copy the general settings in hashsets.xml - For each hash
|
||||
|
@ -1179,6 +1179,12 @@ public final class DrawableDB {
|
||||
* @return the number of files with Cat-0
|
||||
*/
|
||||
public long getUncategorizedCount(Collection<Long> fileIDs) {
|
||||
|
||||
// if the fileset is empty, return count as 0
|
||||
if (fileIDs.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DrawableTagsManager tagsManager = controller.getTagsManager();
|
||||
|
||||
// get a comma seperated list of TagName ids for non zero categories
|
||||
|
@ -82,6 +82,7 @@ import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData.DbType;
|
||||
|
||||
/**
|
||||
* Provides an abstraction layer on top of {@link DrawableDB} ( and to some
|
||||
@ -351,11 +352,22 @@ public class GroupManager {
|
||||
case MIME_TYPE:
|
||||
if (nonNull(db)) {
|
||||
HashSet<String> types = new HashSet<>();
|
||||
try (SleuthkitCase.CaseDbQuery executeQuery = controller.getSleuthKitCase().executeQuery("select group_concat(obj_id), mime_type from tsk_files group by mime_type "); //NON-NLS
|
||||
|
||||
// Use the group_concat function to get a list of files for each mime type.
|
||||
// This has different syntax on Postgres vs SQLite
|
||||
String groupConcatClause;
|
||||
if (DbType.POSTGRESQL == controller.getSleuthKitCase().getDatabaseType()) {
|
||||
groupConcatClause = " array_to_string(array_agg(obj_id), ',') as object_ids";
|
||||
}
|
||||
else {
|
||||
groupConcatClause = "select group_concat(obj_id) as object_ids";
|
||||
}
|
||||
String query = "select " + groupConcatClause + " , mime_type from tsk_files group by mime_type ";
|
||||
try (SleuthkitCase.CaseDbQuery executeQuery = controller.getSleuthKitCase().executeQuery(query); //NON-NLS
|
||||
ResultSet resultSet = executeQuery.getResultSet();) {
|
||||
while (resultSet.next()) {
|
||||
final String mimeType = resultSet.getString("mime_type"); //NON-NLS
|
||||
String objIds = resultSet.getString("group_concat(obj_id)"); //NON-NLS
|
||||
String objIds = resultSet.getString("object_ids"); //NON-NLS
|
||||
|
||||
Pattern.compile(",").splitAsStream(objIds)
|
||||
.map(Long::valueOf)
|
||||
|
@ -36,7 +36,6 @@ import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||
import org.sleuthkit.autopsy.directorytree.HashSearchAction;
|
||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
@ -48,7 +47,6 @@ import org.sleuthkit.datamodel.LayoutFile;
|
||||
import org.sleuthkit.datamodel.LocalFile;
|
||||
import org.sleuthkit.datamodel.Report;
|
||||
import org.sleuthkit.datamodel.SlackFile;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
|
||||
/**
|
||||
@ -126,50 +124,45 @@ class AdHocSearchFilterNode extends FilterNode {
|
||||
|
||||
@Override
|
||||
public List<Action> visit(File f) {
|
||||
return getFileActions(true);
|
||||
return getFileActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> visit(DerivedFile f) {
|
||||
return getFileActions(true);
|
||||
return getFileActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> visit(Directory d) {
|
||||
return getFileActions(false);
|
||||
return getFileActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> visit(LayoutFile lf) {
|
||||
//we want hashsearch enabled on carved files but not unallocated blocks
|
||||
boolean enableHashSearch = (lf.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.CARVED);
|
||||
return getFileActions(enableHashSearch);
|
||||
return getFileActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> visit(LocalFile lf) {
|
||||
return getFileActions(true);
|
||||
return getFileActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> visit(SlackFile f) {
|
||||
return getFileActions(false);
|
||||
return getFileActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> visit(VirtualDirectory dir) {
|
||||
return getFileActions(false);
|
||||
return getFileActions();
|
||||
}
|
||||
|
||||
private List<Action> getFileActions(boolean enableHashSearch) {
|
||||
private List<Action> getFileActions() {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
actionsList.add(new NewWindowViewAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.viewInNewWinActionLbl"), AdHocSearchFilterNode.this));
|
||||
actionsList.add(new ExternalViewerAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.openExternViewActLbl"), getOriginal()));
|
||||
actionsList.add(null);
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
Action hashSearchAction = new HashSearchAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.searchSameMd5"), getOriginal());
|
||||
hashSearchAction.setEnabled(enableHashSearch);
|
||||
actionsList.add(hashSearchAction);
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
|
||||
@ -185,7 +178,7 @@ class AdHocSearchFilterNode extends FilterNode {
|
||||
|
||||
@Override
|
||||
protected List<Action> defaultVisit(Content c) {
|
||||
return getFileActions(false);
|
||||
return getFileActions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import org.apache.solr.common.SolrInputDocument;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor;
|
||||
import org.sleuthkit.autopsy.healthmonitor.HealthMonitor;
|
||||
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||
import org.sleuthkit.autopsy.keywordsearch.Chunker.Chunk;
|
||||
@ -237,9 +237,9 @@ class Ingester {
|
||||
|
||||
try {
|
||||
//TODO: consider timeout thread, or vary socket timeout based on size of indexed content
|
||||
TimingMetric metric = EnterpriseHealthMonitor.getTimingMetric("Solr: Index chunk");
|
||||
TimingMetric metric = HealthMonitor.getTimingMetric("Solr: Index chunk");
|
||||
solrServer.addDocument(updateDoc);
|
||||
EnterpriseHealthMonitor.submitTimingMetric(metric);
|
||||
HealthMonitor.submitTimingMetric(metric);
|
||||
uncommitedIngests = true;
|
||||
|
||||
} catch (KeywordSearchModuleException | NoOpenCoreException ex) {
|
||||
|
@ -70,7 +70,7 @@ import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor;
|
||||
import org.sleuthkit.autopsy.healthmonitor.HealthMonitor;
|
||||
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
|
||||
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
@ -710,9 +710,9 @@ public class Server {
|
||||
if (null == currentCore) {
|
||||
throw new NoOpenCoreException();
|
||||
}
|
||||
TimingMetric metric = EnterpriseHealthMonitor.getTimingMetric("Solr: Index chunk");
|
||||
TimingMetric metric = HealthMonitor.getTimingMetric("Solr: Index chunk");
|
||||
currentCore.addDocument(doc);
|
||||
EnterpriseHealthMonitor.submitTimingMetric(metric);
|
||||
HealthMonitor.submitTimingMetric(metric);
|
||||
} finally {
|
||||
currentCoreLock.readLock().unlock();
|
||||
}
|
||||
@ -781,9 +781,9 @@ public class Server {
|
||||
IndexingServerProperties properties = getMultiUserServerProperties(theCase.getCaseDirectory());
|
||||
currentSolrServer = new HttpSolrServer("http://" + properties.getHost() + ":" + properties.getPort() + "/solr"); //NON-NLS
|
||||
}
|
||||
TimingMetric metric = EnterpriseHealthMonitor.getTimingMetric("Solr: Connectivity check");
|
||||
TimingMetric metric = HealthMonitor.getTimingMetric("Solr: Connectivity check");
|
||||
connectToSolrServer(currentSolrServer);
|
||||
EnterpriseHealthMonitor.submitTimingMetric(metric);
|
||||
HealthMonitor.submitTimingMetric(metric);
|
||||
|
||||
} catch (SolrServerException | IOException ex) {
|
||||
throw new KeywordSearchModuleException(NbBundle.getMessage(Server.class, "Server.connect.exception.msg", ex.getLocalizedMessage()), ex);
|
||||
@ -1325,13 +1325,13 @@ public class Server {
|
||||
* @throws IOException
|
||||
*/
|
||||
void connectToSolrServer(HttpSolrServer solrServer) throws SolrServerException, IOException {
|
||||
TimingMetric metric = EnterpriseHealthMonitor.getTimingMetric("Solr: Connectivity check");
|
||||
TimingMetric metric = HealthMonitor.getTimingMetric("Solr: Connectivity check");
|
||||
CoreAdminRequest statusRequest = new CoreAdminRequest();
|
||||
statusRequest.setCoreName( null );
|
||||
statusRequest.setAction( CoreAdminParams.CoreAdminAction.STATUS );
|
||||
statusRequest.setIndexInfoNeeded(false);
|
||||
statusRequest.process(solrServer);
|
||||
EnterpriseHealthMonitor.submitTimingMetric(metric);
|
||||
HealthMonitor.submitTimingMetric(metric);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
\section common_files_overview Overview
|
||||
|
||||
The common files feature allows you to search for multiple copies of the same file within a case.
|
||||
The common files feature allows you to search for multiple copies of the same file in different data sources within a case.
|
||||
|
||||
\section common_files_usage Usage
|
||||
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 52 KiB |