5129 resolve merge conflicts

This commit is contained in:
William Schaefer 2019-06-10 10:25:09 -04:00
commit 1f3bf30e64
38 changed files with 1124 additions and 214 deletions

View File

@ -18,13 +18,10 @@
*/
package org.sleuthkit.autopsy.communications.relationships;
import java.util.List;
import java.util.TimeZone;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Sheet;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
@ -40,7 +37,6 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHO
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT;
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TimeUtilities;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.communications.Utils;
@ -72,7 +68,6 @@ final class MessageNode extends BlackboardArtifactNode {
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
List<Tag> tags = getAllTagsFromDatabase();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
@ -81,17 +76,6 @@ final class MessageNode extends BlackboardArtifactNode {
sheetSet.put(new NodeProperty<>("Type", Bundle.MessageNode_Node_Property_Type(), "", getDisplayName())); //NON-NLS
addScoreProperty(sheetSet, tags);
CorrelationAttributeInstance correlationAttribute = null;
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
correlationAttribute = getCorrelationAttributeInstance();
}
addCommentProperty(sheetSet, tags, correlationAttribute);
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
addCountProperty(sheetSet, correlationAttribute);
}
final BlackboardArtifact artifact = getArtifact();
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());

View File

@ -23,6 +23,7 @@ import org.openide.nodes.Node;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.datamodel.AbstractFile;
/**
* A DataContentViewer that displays text with the TextViewers available.
@ -90,6 +91,17 @@ public class TextContentViewer implements DataContentViewer {
if (node == null) {
return false;
}
// get the node's File, if it has one
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
if (file == null) {
return false;
}
// disable the text content viewer for directories and empty files
if (file.isDir() || file.getSize() == 0) {
return false;
}
return panel.isSupported(node);
}

View File

@ -79,7 +79,7 @@ public class TextContentViewerPanel extends javax.swing.JPanel implements DataCo
}
/**
* Deterime wether the content viewer which displays this panel isSupported.
* Determine whether the content viewer which displays this panel isSupported.
* This panel is supported if any of the TextViewer's displayed in it are
* supported.
*

View File

@ -217,3 +217,4 @@ DataResultViewerTable.pageNumLabel.text=
DataResultViewerTable.pageLabel.text=Page:
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nSetting this value to 0 will display all results in the results table.\n<br>Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n</html>
DataResultViewerTable.exportCSVButton.text=Save table as CSV

View File

@ -32,6 +32,7 @@ DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found
DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on associated tag(s)
DataResultViewerTable.countRender.name=O
DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository
DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export
DataResultViewerTable.firstColLbl=Name
DataResultViewerTable.goToPageTextField.err=Invalid page number
# {0} - totalPages
@ -270,3 +271,4 @@ DataResultViewerTable.pageNumLabel.text=
DataResultViewerTable.pageLabel.text=Page:
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nSetting this value to 0 will display all results in the results table.\n<br>Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n</html>
DataResultViewerTable.exportCSVButton.text=Save table as CSV

View File

@ -16,13 +16,13 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="outlineView" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<EmptySpace pref="608" max="32767" attributes="0"/>
<Component id="outlineView" pref="904" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="pageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pageNumLabel" min="-2" pref="53" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
<Component id="pagesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="pagePrevButton" linkSize="1" min="-2" pref="16" max="-2" attributes="0"/>
@ -32,14 +32,15 @@
<Component id="gotoPageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="gotoPageTextField" min="-2" pref="33" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="exportCSVButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="pageLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="pageNumLabel" alignment="2" min="-2" max="-2" attributes="0"/>
@ -48,9 +49,10 @@
<Component id="pageNextButton" linkSize="2" alignment="2" min="-2" pref="15" max="-2" attributes="0"/>
<Component id="gotoPageLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="gotoPageTextField" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="exportCSVButton" alignment="2" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="outlineView" pref="324" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
<Component id="outlineView" pref="321" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
@ -164,5 +166,15 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="gotoPageTextFieldActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="exportCSVButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerTable.exportCSVButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="exportCSVButtonActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -77,6 +77,7 @@ import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo;
@ -176,6 +177,13 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
initComponents();
initializePagingSupport();
/*
* Disable the CSV export button for the common properties results
*/
if (this instanceof org.sleuthkit.autopsy.commonpropertiessearch.CommonAttributesSearchResultsViewerTable) {
exportCSVButton.setEnabled(false);
}
/*
* Configure the child OutlineView (explorer view) component.
@ -1291,6 +1299,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
outlineView = new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL);
gotoPageLabel = new javax.swing.JLabel();
gotoPageTextField = new javax.swing.JTextField();
exportCSVButton = new javax.swing.JButton();
pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageLabel.text")); // NOI18N
@ -1338,17 +1347,24 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
}
});
exportCSVButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.exportCSVButton.text")); // NOI18N
exportCSVButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
exportCSVButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(608, Short.MAX_VALUE)
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 904, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(pageLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGap(14, 14, 14)
.addComponent(pagesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
@ -1358,7 +1374,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
.addComponent(gotoPageLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(exportCSVButton))
);
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pageNextButton, pagePrevButton});
@ -1366,7 +1383,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGap(3, 3, 3)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(pageLabel)
.addComponent(pageNumLabel)
@ -1374,9 +1391,10 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(gotoPageLabel)
.addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 324, Short.MAX_VALUE)
.addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(exportCSVButton))
.addGap(3, 3, 3)
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 321, Short.MAX_VALUE)
.addContainerGap())
);
@ -1397,7 +1415,19 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
pagingSupport.gotoPage();
}//GEN-LAST:event_gotoPageTextFieldActionPerformed
@NbBundle.Messages({"DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export"
})
private void exportCSVButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCSVButtonActionPerformed
Node currentRoot = this.getExplorerManager().getRootContext();
if (currentRoot != null && currentRoot.getChildren().getNodesCount() > 0) {
org.sleuthkit.autopsy.directorytree.ExportCSVAction.saveNodesToCSV(java.util.Arrays.asList(currentRoot.getChildren().getNodes()), this);
} else {
MessageNotifyUtil.Message.info(Bundle.DataResultViewerTable_exportCSVButtonActionPerformed_empty());
}
}//GEN-LAST:event_exportCSVButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton exportCSVButton;
private javax.swing.JLabel gotoPageLabel;
private javax.swing.JTextField gotoPageTextField;
private org.openide.explorer.view.OutlineView outlineView;

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,7 +18,6 @@
*/
package org.sleuthkit.autopsy.datamodel;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
@ -27,8 +26,6 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.io.FilenameUtils;
@ -65,6 +62,7 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@ -76,15 +74,10 @@ import org.sleuthkit.datamodel.TskData;
public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends AbstractContentNode<T> {
private static final Logger logger = Logger.getLogger(AbstractAbstractFileNode.class.getName());
@NbBundle.Messages("AbstractAbstractFileNode.addFileProperty.desc=no description")
private static final String NO_DESCR = AbstractAbstractFileNode_addFileProperty_desc();
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE,
Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CR_COMMENT_CHANGED);
private static final ExecutorService translationPool;
private static final Integer MAX_POOL_SIZE = 10;
/**
* @param abstractFile file to wrap
*/
@ -101,7 +94,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
}
if (UserPreferences.displayTranslatedFileNames()) {
AbstractAbstractFileNode.translationPool.submit(new TranslationTask(
backgroundTasksPool.submit(new TranslationTask(
new WeakReference<>(this), weakPcl));
}
@ -109,14 +102,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
// or when tags are added.
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
}
static {
//Initialize this pool only once! This will be used by every instance of AAFN
//to do their heavy duty SCO column and translation updates.
translationPool = Executors.newFixedThreadPool(MAX_POOL_SIZE,
new ThreadFactoryBuilder().setNameFormat("translation-task-thread-%d").build());
}
/**
* The finalizer removes event listeners as the BlackboardArtifactNode is
* being garbage collected. Yes, we know that finalizers are considered to
@ -137,16 +123,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
}
/**
* Event signals to indicate the background tasks have completed processing.
* Currently, we have one property task in the background:
*
* 1) Retreiving the translation of the file name
*/
enum NodeSpecificEvents {
TRANSLATION_AVAILABLE,
}
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
@ -190,7 +167,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
} else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
ContentTagAddedEvent event = (ContentTagAddedEvent) evt;
if (event.getAddedTag().getContent().equals(content)) {
List<ContentTag> tags = getContentTagsFromDatabase();
List<Tag> tags = this.getAllTagsFromDatabase();
Pair<Score, String> scorePropAndDescr = getScorePropertyAndDescription(tags);
Score value = scorePropAndDescr.getLeft();
String descr = scorePropAndDescr.getRight();
@ -202,7 +179,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
} else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt;
if (event.getDeletedTagInfo().getContentID() == content.getId()) {
List<ContentTag> tags = getContentTagsFromDatabase();
List<Tag> tags = getAllTagsFromDatabase();
Pair<Score, String> scorePropAndDescr = getScorePropertyAndDescription(tags);
Score value = scorePropAndDescr.getLeft();
String descr = scorePropAndDescr.getRight();
@ -214,7 +191,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
} else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
CommentChangedEvent event = (CommentChangedEvent) evt;
if (event.getContentID() == content.getId()) {
List<ContentTag> tags = getContentTagsFromDatabase();
List<Tag> tags = getAllTagsFromDatabase();
CorrelationAttributeInstance attribute = getCorrelationAttributeInstance();
updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, getCommentProperty(tags, attribute)));
}
@ -223,6 +200,18 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
//Set the tooltip
this.setShortDescription(content.getName());
updateSheet(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, content.getName()));
} else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString())) {
SCOData scoData = (SCOData)evt.getNewValue();
if (scoData.getScoreAndDescription() != null) {
updateSheet(new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft()));
}
if (scoData.getComment() != null) {
updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, scoData.getComment()));
}
if (scoData.getCountAndDescription() != null &&
!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
updateSheet(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
}
}
};
/**
@ -235,38 +224,6 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
*/
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
/**
* Updates the values of the properties in the current property sheet with
* the new properties being passed in. Only if that property exists in the
* current sheet will it be applied. That way, we allow for subclasses to
* add their own (or omit some!) properties and we will not accidentally
* disrupt their UI.
*
* Race condition if not synchronized. Only one update should be applied at
* a time.
*
* @param newProps New file property instances to be updated in the current
* sheet.
*/
private synchronized void updateSheet(NodeProperty<?>... newProps) {
//Refresh ONLY those properties in the sheet currently. Subclasses may have
//only added a subset of our properties or their own props.s
Sheet visibleSheet = this.getSheet();
Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES);
Property<?>[] visibleProps = visibleSheetSet.getProperties();
for (NodeProperty<?> newProp : newProps) {
for (int i = 0; i < visibleProps.length; i++) {
if (visibleProps[i].getName().equals(newProp.getName())) {
visibleProps[i] = newProp;
}
}
}
visibleSheetSet.put(visibleProps);
visibleSheet.put(visibleSheetSet);
//setSheet() will notify Netbeans to update this node in the UI.
this.setSheet(visibleSheet);
}
/*
* This is called when the node is first initialized. Any new updates or
* changes happen by directly manipulating the sheet. That means we can fire
@ -368,18 +325,18 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
properties.add(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, ""));
}
//SCO column prereq info..
List<ContentTag> tags = getContentTagsFromDatabase();
CorrelationAttributeInstance attribute = getCorrelationAttributeInstance();
Pair<DataResultViewerTable.Score, String> scoreAndDescription = getScorePropertyAndDescription(tags);
properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
DataResultViewerTable.HasCommentStatus comment = getCommentProperty(tags, attribute);
properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, comment));
if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute);
properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), countAndDescription.getRight(), countAndDescription.getLeft()));
// Create place holders for S C O
properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), VALUE_LOADING, ""));
properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), VALUE_LOADING, ""));
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), VALUE_LOADING, ""));
}
// Get the SCO columns data in a background task
backgroundTasksPool.submit(new GetSCOTask(
new WeakReference<>(this), weakPcl));
properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content)));
properties.add(new NodeProperty<>(MOD_TIME.toString(), MOD_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getMtime(), content)));
properties.add(new NodeProperty<>(CHANGED_TIME.toString(), CHANGED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCtime(), content)));
@ -441,7 +398,8 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
"AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated",
"# {0} - occuranceCount",
"AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting
String description = Bundle.AbstractAbstractFileNode_createSheet_count_noCentralRepo_description();
try {
@ -468,7 +426,8 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
"AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.",
"AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag.",
"AbstractAbstractFileNode.createSheet.noScore.description=No score"})
Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<ContentTag> tags) {
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
DataResultViewerTable.Score score = DataResultViewerTable.Score.NO_SCORE;
String description = Bundle.AbstractAbstractFileNode_createSheet_noScore_description();
if (content.getKnown() == TskData.FileKnown.BAD) {
@ -486,7 +445,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
if (!tags.isEmpty() && (score == DataResultViewerTable.Score.NO_SCORE || score == DataResultViewerTable.Score.INTERESTING_SCORE)) {
score = DataResultViewerTable.Score.INTERESTING_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_taggedFile_description();
for (ContentTag tag : tags) {
for (Tag tag : tags) {
if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
score = DataResultViewerTable.Score.NOTABLE_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_notableTaggedFile_description();
@ -499,11 +458,12 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
@NbBundle.Messages({
"AbstractAbstractFileNode.createSheet.comment.displayName=C"})
HasCommentStatus getCommentProperty(List<ContentTag> tags, CorrelationAttributeInstance attribute) {
@Override
protected HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
DataResultViewerTable.HasCommentStatus status = !tags.isEmpty() ? DataResultViewerTable.HasCommentStatus.TAG_NO_COMMENT : DataResultViewerTable.HasCommentStatus.NO_COMMENT;
for (ContentTag tag : tags) {
for (Tag tag : tags) {
if (!StringUtils.isBlank(tag.getComment())) {
//if the tag is null or empty or contains just white space it will indicate there is not a comment
status = DataResultViewerTable.HasCommentStatus.TAG_COMMENT;
@ -571,7 +531,13 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
return tags;
}
CorrelationAttributeInstance getCorrelationAttributeInstance() {
@Override
protected List<Tag> getAllTagsFromDatabase() {
return new ArrayList<>(getContentTagsFromDatabase());
}
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance attribute = null;
if (EamDb.isEnabled() && !UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
attribute = EamArtifactUtil.getInstanceFromContent(content);

View File

@ -18,20 +18,29 @@
*/
package org.sleuthkit.autopsy.datamodel;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.lookup.Lookups;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskException;
@ -50,6 +59,38 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
T content;
private static final Logger logger = Logger.getLogger(AbstractContentNode.class.getName());
/**
* A pool of background tasks to run any long computation needed to
* populate this node.
*/
static final ExecutorService backgroundTasksPool;
private static final Integer MAX_POOL_SIZE = 10;
/**
* Default no description string
*/
@NbBundle.Messages({"AbstractContentNode.nodescription=no description",
"AbstractContentNode.valueLoading=value loading"})
protected static final String NO_DESCR = Bundle.AbstractContentNode_nodescription();
protected static final String VALUE_LOADING = Bundle.AbstractContentNode_valueLoading();
/**
* Event signals to indicate the background tasks have completed processing.
* Currently, we have one property task in the background:
*
* 1) Retrieving the translation of the file name
*/
enum NodeSpecificEvents {
TRANSLATION_AVAILABLE,
SCO_AVAILABLE
}
static {
//Initialize this pool only once! This will be used by every instance of AAFN
//to do their heavy duty SCO column and translation updates.
backgroundTasksPool = Executors.newFixedThreadPool(MAX_POOL_SIZE,
new ThreadFactoryBuilder().setNameFormat("content-node-background-task-%d").build());
}
/**
* Handles aspects that depend on the Content object
*
@ -240,4 +281,79 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
public int read(byte[] buf, long offset, long len) throws TskException {
return content.read(buf, offset, len);
}
/**
* Updates the values of the properties in the current property sheet with
* the new properties being passed in. Only if that property exists in the
* current sheet will it be applied. That way, we allow for subclasses to
* add their own (or omit some!) properties and we will not accidentally
* disrupt their UI.
*
* Race condition if not synchronized. Only one update should be applied at
* a time.
*
* @param newProps New file property instances to be updated in the current
* sheet.
*/
protected synchronized void updateSheet(NodeProperty<?>... newProps) {
//Refresh ONLY those properties in the sheet currently. Subclasses may have
//only added a subset of our properties or their own props.s
Sheet visibleSheet = this.getSheet();
Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES);
Property<?>[] visibleProps = visibleSheetSet.getProperties();
for (NodeProperty<?> newProp : newProps) {
for (int i = 0; i < visibleProps.length; i++) {
if (visibleProps[i].getName().equals(newProp.getName())) {
visibleProps[i] = newProp;
}
}
}
visibleSheetSet.put(visibleProps);
visibleSheet.put(visibleSheetSet);
//setSheet() will notify Netbeans to update this node in the UI.
this.setSheet(visibleSheet);
}
/**
* Reads and returns a list of all tags associated with this content node.
*
* @return list of tags associated with the node.
*/
abstract protected List<Tag> getAllTagsFromDatabase();
/**
* Returns correlation attribute instance for the underlying content of the node.
*
* @return correlation attribute instance for the underlying content of the node.
*/
abstract protected CorrelationAttributeInstance getCorrelationAttributeInstance();
/**
* Returns Score property for the node.
*
* @param tags list of tags.
*
* @return Score property for the underlying content of the node.
*/
abstract protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags);
/**
* Returns comment property for the node.
*
* @param tags list of tags
* @param attribute correlation attribute instance
*
* @return Comment property for the underlying content of the node.
*/
abstract protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute);
/**
* Returns occurrences/count property for the node.
*
* @param attribute correlation attribute instance
*
* @return count property for the underlying content of the node.
*/
abstract protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute);
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -22,6 +22,7 @@ import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
@ -37,6 +38,7 @@ import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.Action;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
@ -55,11 +57,13 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager;
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
@ -95,10 +99,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
private Content associated = null;
private List<NodeProperty<? extends Object>> customProperties;
private final static String NO_DESCR = NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.noDesc.text");
/*
* Artifact types which should have the full unique path of the associated
* content as a property.
@ -151,6 +152,19 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
contentCache.invalidateAll();
}
}
else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString())) {
SCOData scoData = (SCOData)evt.getNewValue();
if (scoData.getScoreAndDescription() != null) {
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft()));
}
if (scoData.getComment() != null) {
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, scoData.getComment()));
}
if (scoData.getCountAndDescription() != null &&
!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
}
}
}
};
@ -335,8 +349,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
List<Tag> tags = getAllTagsFromDatabase();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
@ -351,17 +363,17 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
NO_DESCR,
this.getSourceName()));
addScoreProperty(sheetSet, tags);
CorrelationAttributeInstance correlationAttribute = null;
// Create place holders for S C O
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), VALUE_LOADING, ""));
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), VALUE_LOADING, ""));
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
correlationAttribute = getCorrelationAttributeInstance();
}
addCommentProperty(sheetSet, tags, correlationAttribute);
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
addCountProperty(sheetSet, correlationAttribute);
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), VALUE_LOADING, ""));
}
// Get the SCO columns data in a background task
backgroundTasksPool.submit(new GetSCOTask(
new WeakReference<>(this), weakPcl));
if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
try {
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
@ -520,6 +532,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @return a list of tags which on the artifact or the file it is associated
* with
*/
@Override
protected final List<Tag> getAllTagsFromDatabase() {
List<Tag> tags = new ArrayList<>();
try {
@ -569,6 +582,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
}
@Override
protected final CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance correlationAttribute = null;
if (EamDb.isEnabled()) {
@ -581,16 +595,36 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* Used by (subclasses of) BlackboardArtifactNode to add the comment
* property to their sheets.
*
* @param sheetSet the modifiable Sheet.Set returned by
* Sheet.get(Sheet.PROPERTIES)
* @param sheetSet the modifiable Sheet.Set to add the property to
* @param tags the list of tags associated with the file
* @param attribute the correlation attribute associated with this
* artifact's associated file, null if central repo is not
* enabled
*
* @deprecated Use the GetSCOTask to get this data on a background thread...,
* and then update the property sheet asynchronously
*/
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
"BlackboardArtifactNode.createSheet.comment.displayName=C"})
@Deprecated
protected final void addCommentProperty(Sheet.Set sheetSet, List<Tag> tags, CorrelationAttributeInstance attribute) {
HasCommentStatus status = getCommentProperty(tags, attribute );
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR,
status));
}
/**
* Gets the comment property for the node
*
* @param tags the list of tags associated with the file
* @param attribute the correlation attribute associated with this
* artifact's associated file, null if central repo is not
* enabled
* @return comment property
*/
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
HasCommentStatus status = tags.size() > 0 ? HasCommentStatus.TAG_NO_COMMENT : HasCommentStatus.NO_COMMENT;
for (Tag tag : tags) {
if (!StringUtils.isBlank(tag.getComment())) {
@ -609,17 +643,17 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
status = HasCommentStatus.CR_COMMENT;
}
}
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR,
status));
return status;
}
/**
* Used by (subclasses of) BlackboardArtifactNode to add the Score property
* to their sheets.
*
* @param sheetSet the modifiable Sheet.Set returned by
* Sheet.get(Sheet.PROPERTIES)
* @param sheetSet the modifiable Sheet.Set to add the property to
* @param tags the list of tags associated with the file
*
* @deprecated Use the GetSCOTask to get this data on a background thread...,
* and then update the property sheet asynchronously
*/
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
"BlackboardArtifactNode.createSheet.score.displayName=S",
@ -628,8 +662,22 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
"BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.",
"BlackboardArtifactNode.createSheet.notableTaggedItem.description=Result or associated file tagged with notable tag.",
"BlackboardArtifactNode.createSheet.noScore.description=No score"})
protected final void addScoreProperty(Sheet.Set sheetSet, List<Tag> tags) {
Score score = Score.NO_SCORE;
@Deprecated
protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List<Tag> tags) {
Pair<DataResultViewerTable.Score, String> scoreAndDescription = getScorePropertyAndDescription(tags);
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
}
/**
* Get the score property for the node.
*
* @param tags the list of tags associated with the file
*
* @return score property and description
*/
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
Score score = Score.NO_SCORE;
String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
if (associated instanceof AbstractFile) {
if (((AbstractFile) associated).getKnown() == TskData.FileKnown.BAD) {
@ -673,18 +721,43 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
}
}
}
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), description, score));
return Pair.of(score, description);
}
/**
* Used by (subclasses of) BlackboardArtifactNode to add the Occurrences property
* to their sheets.
*
* @param sheetSet the modifiable Sheet.Set to add the property to
* @param attribute correlation attribute instance
*
* @deprecated Use the GetSCOTask to get this data on a background thread...,
* and then update the property sheet asynchronously
*/
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
"BlackboardArtifactNode.createSheet.count.displayName=O",
"BlackboardArtifactNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated",
"BlackboardArtifactNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this artifact's associated file when the column was populated",
"# {0} - occuranceCount",
"BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
@Deprecated
protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting
Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute);
sheetSet.put(
new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft()));
}
/**
* Gets the Occurrences property for the node.
*
* @param attribute correlation attribute instance
*
* @return count and description
*
*/
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
Long count = -1L;
String description = Bundle.BlackboardArtifactNode_createSheet_count_noCentralRepo_description();
try {
//don't perform the query if there is no correlation value
@ -699,14 +772,13 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex);
}
sheetSet.put(
new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), description, count));
}
return Pair.of(count, description);
}
private void updateSheet() {
this.setSheet(createSheet());
}
private String getRootParentName() {
String parentName = associated.getName();
Content parent = associated;

View File

@ -1,5 +1,4 @@
AbstractAbstractFileNode.accessTimeColLbl=Access Time
AbstractAbstractFileNode.addFileProperty.desc=no description
AbstractAbstractFileNode.attrAddrColLbl=Attr. Addr.
AbstractAbstractFileNode.changeTimeColLbl=Change Time
AbstractAbstractFileNode.createdTimeColLbl=Created Time
@ -37,6 +36,8 @@ AbstractAbstractFileNode.tagsProperty.displayName=Tags
AbstractAbstractFileNode.typeDirColLbl=Type(Dir)
AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)
AbstractAbstractFileNode.useridColLbl=UserID
AbstractContentNode.nodescription=no description
AbstractContentNode.valueLoading=value loading
AbstractFsContentNode.noDesc.text=no description
ArtifactStringContent.attrsTableHeader.sources=Source(s)
ArtifactStringContent.attrsTableHeader.type=Type

View File

@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.actions.ReplaceBlackboardArtifactTagAction;
import org.sleuthkit.autopsy.actions.ReplaceContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.datamodel.Reports.ReportNode;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
@ -92,6 +93,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -119,6 +121,7 @@ public class DataModelActionsFactory {
actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, slackFileNode));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -155,6 +158,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());//
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -189,6 +193,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -223,6 +228,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -257,6 +263,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -291,6 +298,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -325,6 +333,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -379,6 +388,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -415,6 +425,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {

View File

@ -28,6 +28,7 @@ import org.openide.util.Utilities;
import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
@ -89,6 +90,7 @@ public class DirectoryNode extends AbstractFsContentNode<AbstractFile> {
actionsList.add(ViewFileInTimelineAction.createViewFileAction(content));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(new RunIngestModulesAction(content));
actionsList.add(null); // creates a menu separator

View File

@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
@ -173,6 +174,7 @@ public class FileNode extends AbstractFsContentNode<AbstractFile> {
actionsList.add(null); // Creates an item separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // Creates an item separator
actionsList.add(AddContentTagAction.getInstance());

View File

@ -0,0 +1,73 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.util.List;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.datamodel.Tag;
/**
* Background task to get Score, Comment and Occurrences values for an
* Abstract content node.
*
*/
class GetSCOTask implements Runnable {
private final WeakReference<AbstractContentNode<?>> weakNodeRef;
private final PropertyChangeListener listener;
GetSCOTask(WeakReference<AbstractContentNode<?>> weakContentRef, PropertyChangeListener listener) {
this.weakNodeRef = weakContentRef;
this.listener = listener;
}
@Override
public void run() {
AbstractContentNode<?> contentNode = weakNodeRef.get();
//Check for stale reference
if (contentNode == null) {
return;
}
// get the SCO column values
List<Tag> tags = contentNode.getAllTagsFromDatabase();
CorrelationAttributeInstance attribute = contentNode.getCorrelationAttributeInstance();
SCOData scoData = new SCOData();
scoData.setScoreAndDescription(contentNode.getScorePropertyAndDescription(tags));
scoData.setComment(contentNode.getCommentProperty(tags, attribute));
if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
scoData.setCountAndDescription(contentNode.getCountPropertyAndDescription(attribute));
}
// signal SCO data is available.
if (listener != null) {
listener.propertyChange(new PropertyChangeEvent(
AutopsyEvent.SourceType.LOCAL.toString(),
AbstractAbstractFileNode.NodeSpecificEvents.SCO_AVAILABLE.toString(),
null, scoData));
}
}
}

View File

@ -28,12 +28,15 @@ import java.util.EnumSet;
import java.util.List;
import java.util.logging.Level;
import javax.swing.Action;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
@ -47,6 +50,7 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException;
import org.sleuthkit.datamodel.Tag;
/**
* This class is used to represent the "Node" for the image. The children of
@ -250,4 +254,67 @@ public class ImageNode extends AbstractContentNode<Image> {
}
};
/**
* Reads and returns a list of all tags associated with this content node.
*
* Null implementation of an abstract method.
*
* @return list of tags associated with the node.
*/
@Override
protected List<Tag> getAllTagsFromDatabase() {
return new ArrayList<>();
}
/**
* Returns correlation attribute instance for the underlying content of the node.
*
* Null implementation of an abstract method.
*
* @return correlation attribute instance for the underlying content of the node.
*/
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
return null;
}
/**
* Returns Score property for the node.
*
* Null implementation of an abstract method.
*
* @param tags list of tags.
*
* @return Score property for the underlying content of the node.
*/
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR);
}
/**
* Returns comment property for the node.
*
* Null implementation of an abstract method.
*
* @param tags list of tags
* @param attribute correlation attribute instance
*
* @return Comment property for the underlying content of the node.
*/
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
}
/**
* Returns occurrences/count property for the node.
*
* Null implementation of an abstract method.
*
* @param attribute correlation attribute instance
*
* @return count property for the underlying content of the node.
*/
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
return Pair.of(-1L, NO_DESCR);
}
}

View File

@ -29,6 +29,7 @@ import org.openide.util.Utilities;
import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
@ -105,6 +106,7 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());

View File

@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
@ -82,6 +83,7 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());

View File

@ -0,0 +1,56 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
/**
* Container to bag the S C & O data for an abstract file node.
*
*/
class SCOData {
private Pair<DataResultViewerTable.Score, String> scoreAndDescription = null;
private DataResultViewerTable.HasCommentStatus comment = null;
private Pair<Long, String> countAndDescription = null;
Pair<DataResultViewerTable.Score, String> getScoreAndDescription() {
return scoreAndDescription;
}
DataResultViewerTable.HasCommentStatus getComment() {
return comment;
}
Pair<Long, String> getCountAndDescription() {
return countAndDescription;
}
void setScoreAndDescription(Pair<DataResultViewerTable.Score, String> scoreAndDescription) {
this.scoreAndDescription = scoreAndDescription;
}
void setComment(DataResultViewerTable.HasCommentStatus comment) {
this.comment = comment;
}
void setCountAndDescription(Pair<Long, String> countAndDescription) {
this.countAndDescription = countAndDescription;
}
}

View File

@ -28,6 +28,7 @@ import org.openide.util.Utilities;
import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
@ -85,6 +86,7 @@ public class SlackFileNode extends AbstractFsContentNode<AbstractFile> {
NbBundle.getMessage(this.getClass(), "SlackFileNode.getActions.viewInNewWin.text"), this));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());

View File

@ -25,6 +25,7 @@ import javax.swing.Action;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
@ -61,6 +62,7 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode<Spec
Bundle.SpecialDirectoryNode_getActions_viewInNewWin_text(), this));
actions.add(null); // creates a menu separator
actions.add(ExtractAction.getInstance());
actions.add(ExportCSVAction.getInstance());
actions.add(null); // creates a menu separator
actions.add(new FileSearchAction(Bundle.ImageNode_getActions_openFileSearchByAttr_text()));
if (content.isDataSource()) {

View File

@ -25,10 +25,14 @@ import java.util.EnumSet;
import java.util.List;
import java.util.logging.Level;
import javax.swing.Action;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException;
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
@ -39,6 +43,7 @@ import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.datamodel.Volume;
import org.sleuthkit.autopsy.directorytree.FileSystemDetailsAction;
import org.sleuthkit.datamodel.Tag;
/**
* This class is used to represent the "Node" for the volume. Its child is the
@ -212,4 +217,67 @@ public class VolumeNode extends AbstractContentNode<Volume> {
public String getItemType() {
return DisplayableItemNode.FILE_PARENT_NODE_KEY;
}
/**
* Reads and returns a list of all tags associated with this content node.
*
* Null implementation of an abstract method.
*
* @return list of tags associated with the node.
*/
@Override
protected List<Tag> getAllTagsFromDatabase() {
return new ArrayList<>();
}
/**
* Returns correlation attribute instance for the underlying content of the node.
*
* Null implementation of an abstract method.
*
* @return correlation attribute instance for the underlying content of the node.
*/
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
return null;
}
/**
* Returns Score property for the node.
*
* Null implementation of an abstract method.
*
* @param tags list of tags.
*
* @return Score property for the underlying content of the node.
*/
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR);
}
/**
* Returns comment property for the node.
*
* Null implementation of an abstract method.
*
* @param tags list of tags
* @param attribute correlation attribute instance
*
* @return Comment property for the underlying content of the node.
*/
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
}
/**
* Returns occurrences/count property for the node.
*
* Null implementation of an abstract method.
*
* @param attribute correlation attribute instance
*
* @return count property for the underlying content of the node.
*/
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
return Pair.of(-1L, NO_DESCR);
}
}

View File

@ -1,3 +1,8 @@
CSVWriter.done.notifyMsg.error=Error exporting to CSV file
# {0} - Output file
CSVWriter.done.notifyMsg.success=Wrote to {0}
CSVWriter.progress.cancelling=Cancelling
CSVWriter.progress.extracting=Exporting to CSV file
CTL_DirectoryTreeTopComponent=Directory Tree
DataResultFilterNode.viewSourceArtifact.text=View Source Result
# {0} - dataSourceCount
@ -5,6 +10,11 @@ DirectoryTreeTopComponent.componentOpened.groupDataSources.text=This case contai
DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source?
DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module.
DirectoryTreeTopComponent.resultsView.title=Listing
ExportCSV.saveNodesToCSV.empty=No data to export
# {0} - Output file
ExportCSV.saveNodesToCSV.fileExists=File {0} already exists
ExportCSV.saveNodesToCSV.noCurrentCase=No open case available
ExportCSV.title.text=Export selected rows to CSV
ExternalViewerAction.actionPerformed.failure.exe.message=The file is an executable and will not be opened.
ExternalViewerAction.actionPerformed.failure.IO.message=There is no associated editor for files of this type or the associated application failed to launch.
ExternalViewerAction.actionPerformed.failure.missingFile.message=The file no longer exists.

View File

@ -372,6 +372,7 @@ public class DataResultFilterNode extends FilterNode {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
actionsList.add(AddBlackboardArtifactTagAction.getInstance());

View File

@ -125,6 +125,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
}
}
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.addAll(ContextMenuExtensionPoint.getActions());
return actionsList;
}
@ -142,6 +143,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
}
}
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.addAll(ContextMenuExtensionPoint.getActions());
return actionsList;
}
@ -150,6 +152,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
public List<? extends Action> visit(final DerivedFile d) {
List<Action> actionsList = new ArrayList<>();
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(AddContentTagAction.getInstance());
final Collection<AbstractFile> selectedFilesList =
@ -166,6 +169,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
public List<? extends Action> visit(final LocalFile d) {
List<Action> actionsList = new ArrayList<>();
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(AddContentTagAction.getInstance());
final Collection<AbstractFile> selectedFilesList =
@ -182,6 +186,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
public List<? extends Action> visit(final org.sleuthkit.datamodel.File d) {
List<Action> actionsList = new ArrayList<>();
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(AddContentTagAction.getInstance());
final Collection<AbstractFile> selectedFilesList =

View File

@ -0,0 +1,360 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this content 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.Component;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.Cancellable;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType;
import org.openide.nodes.Node;
import org.openide.nodes.Node.PropertySet;
import org.openide.nodes.Node.Property;
/**
* Exports CSV version of result nodes to a location selected by the user.
*/
public final class ExportCSVAction extends AbstractAction {
private static final Logger logger = Logger.getLogger(ExportCSVAction.class.getName());
private final static String DEFAULT_FILENAME = "Results";
private final static List<String> columnsToSkip = Arrays.asList(AbstractFilePropertyType.SCORE.toString(),
AbstractFilePropertyType.COMMENT.toString(), AbstractFilePropertyType.OCCURRENCES.toString());
private static String userDefinedExportPath;
// This class is a singleton to support multi-selection of nodes, since
// 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 ExportCSVAction instance;
/**
* Get an instance of the Action. See above for why
* the class is a singleton.
*
* @return the instance
*/
public static synchronized ExportCSVAction getInstance() {
if (null == instance) {
instance = new ExportCSVAction();
}
return instance;
}
/**
* Private constructor for the action.
*/
@NbBundle.Messages({"ExportCSV.title.text=Export selected rows to CSV"})
private ExportCSVAction() {
super(Bundle.ExportCSV_title_text());
}
/**
* Asks user to choose destination, then extracts content to destination
* (recursing on directories).
*
* @param e The action event.
*/
@Override
public void actionPerformed(ActionEvent e) {
Collection<? extends Node> selectedNodes = Utilities.actionsGlobalContext().lookupAll(Node.class);
saveNodesToCSV(selectedNodes, (Component)e.getSource());
}
/**
* Save the selected nodes to a CSV file
*
* @param nodesToExport the nodes to save
* @param component
*/
@NbBundle.Messages({
"# {0} - Output file",
"ExportCSV.saveNodesToCSV.fileExists=File {0} already exists",
"ExportCSV.saveNodesToCSV.noCurrentCase=No open case available",
"ExportCSV.saveNodesToCSV.empty=No data to export"})
public static void saveNodesToCSV(Collection<? extends Node> nodesToExport, Component component) {
if (nodesToExport.isEmpty()) {
MessageNotifyUtil.Message.info(Bundle.ExportCSV_saveNodesToCSV_empty());
return;
}
try {
// Set up the file chooser with a default name and either the Export
// folder or the last used folder.
String fileName = getDefaultOutputFileName(nodesToExport.iterator().next().getParentNode());
JFileChooser fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new File(getExportDirectory(Case.getCurrentCaseThrows())));
fileChooser.setSelectedFile(new File(fileName));
fileChooser.setFileFilter(new FileNameExtensionFilter("csv file", "csv"));
int returnVal = fileChooser.showSaveDialog(component);
if (returnVal == JFileChooser.APPROVE_OPTION) {
// Get the file name, appending .csv if necessary
File selectedFile = fileChooser.getSelectedFile();
if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS
selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS
}
// Save the directory used for next time
updateExportDirectory(selectedFile.getParent(), Case.getCurrentCaseThrows());
if (selectedFile.exists()) {
logger.log(Level.SEVERE, "File {0} already exists", selectedFile.getAbsolutePath()); //NON-NLS
MessageNotifyUtil.Message.info(Bundle.ExportCSV_saveNodesToCSV_fileExists(selectedFile));
return;
}
CSVWriter writer = new CSVWriter(nodesToExport, selectedFile);
writer.execute();
}
} catch (NoCurrentCaseException ex) {
JOptionPane.showMessageDialog(component, Bundle.ExportCSV_saveNodesToCSV_noCurrentCase());
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
}
}
/**
* Create a default name for the CSV output.
*
* @param parent The parent node for the selected nodes
*
* @return the default name
*/
private static String getDefaultOutputFileName(Node parent) {
String dateStr = String.format("%1$tY%1$tm%1$te%1$tI%1$tM%1$tS", Calendar.getInstance());
if (parent != null) {
// The first value in the property set is generally a reasonable name
for (PropertySet set : parent.getPropertySets()) {
for (Property<?> prop : set.getProperties()) {
try {
String parentName = prop.getValue().toString();
// Strip off the count (if present)
parentName = parentName.replaceAll("\\([0-9]+\\)$", "");
// Strip out any invalid characters
parentName = parentName.replaceAll("[\\\\/:*?\"<>|]", "_");
return parentName + " " + dateStr;
} catch (IllegalAccessException | InvocationTargetException ex) {
logger.log(Level.WARNING, "Failed to get property set value as string", ex);
}
}
}
}
return DEFAULT_FILENAME + " " + dateStr;
}
/**
* Get the export directory path.
*
* @param openCase The current case.
*
* @return The export directory path.
*/
private static String getExportDirectory(Case openCase) {
String caseExportPath = openCase.getExportDirectory();
if (userDefinedExportPath == null) {
return caseExportPath;
}
File file = new File(userDefinedExportPath);
if (file.exists() == false || file.isDirectory() == false) {
return caseExportPath;
}
return userDefinedExportPath;
}
/**
* Update the default export directory. If the directory path matches the
* case export directory, then the directory used will always match the
* export directory of any given case. Otherwise, the path last used will be
* saved.
*
* @param exportPath The export path.
* @param openCase The current case.
*/
private static void updateExportDirectory(String exportPath, Case openCase) {
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
userDefinedExportPath = null;
} else {
userDefinedExportPath = exportPath;
}
}
/**
* Thread that does the actual extraction work
*/
private static class CSVWriter extends SwingWorker<Object, Void> {
private static final Logger logger = Logger.getLogger(CSVWriter.class.getName());
private ProgressHandle progress;
private final Collection<? extends Node> nodesToExport;
private final File outputFile;
/**
* Create an instance of the CSVWriter.
*
* @param extractionTasks List of file extraction tasks.
*/
CSVWriter(Collection<? extends Node> nodesToExport, File outputFile) {
this.nodesToExport = nodesToExport;
this.outputFile = outputFile;
}
@NbBundle.Messages({"CSVWriter.progress.extracting=Exporting to CSV file",
"CSVWriter.progress.cancelling=Cancelling"})
@Override
protected Object doInBackground() throws Exception {
if (nodesToExport.isEmpty()) {
return null;
}
// Set up progress bar.
final String displayName = Bundle.CSVWriter_progress_extracting();
progress = ProgressHandle.createHandle(displayName, new Cancellable() {
@Override
public boolean cancel() {
if (progress != null) {
progress.setDisplayName(Bundle.CSVWriter_progress_cancelling());
}
return ExportCSVAction.CSVWriter.this.cancel(true);
}
});
progress.start();
progress.switchToIndeterminate();
try (BufferedWriter br = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8))) {
// Write BOM
br.write('\ufeff');
// Write the header
List<String> headers = new ArrayList<>();
PropertySet[] sets = nodesToExport.iterator().next().getPropertySets();
for(PropertySet set : sets) {
for (Property<?> prop : set.getProperties()) {
if ( ! columnsToSkip.contains(prop.getDisplayName())) {
headers.add(prop.getDisplayName());
}
}
}
br.write(listToCSV(headers));
// Write each line
Iterator<?> nodeIterator = nodesToExport.iterator();
while (nodeIterator.hasNext()) {
if (this.isCancelled()) {
break;
}
Node node = (Node)nodeIterator.next();
List<String> values = new ArrayList<>();
sets = node.getPropertySets();
for(PropertySet set : sets) {
for (Property<?> prop : set.getProperties()) {
if ( ! columnsToSkip.contains(prop.getDisplayName())) {
values.add(escapeQuotes(prop.getValue().toString()));
}
}
}
br.write(listToCSV(values));
}
}
return null;
}
/**
* Escape any quotes in the string
*
* @param original
*
* @return the string with quotes escaped
*/
private String escapeQuotes(String original) {
return original.replaceAll("\"", "\\\\\"");
}
/**
* Convert list of values to a comma separated string.
*
* @param values Values to convert
*
* @return values as CSV
*/
private String listToCSV(List<String> values) {
return "\"" + String.join("\",\"", values) + "\"\n";
}
@NbBundle.Messages({"CSVWriter.done.notifyMsg.error=Error exporting to CSV file",
"# {0} - Output file",
"CSVWriter.done.notifyMsg.success=Wrote to {0}"})
@Override
protected void done() {
boolean msgDisplayed = false;
try {
super.get();
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS
MessageNotifyUtil.Message.info(Bundle.CSVWriter_done_notifyMsg_error());
msgDisplayed = true;
} catch (java.util.concurrent.CancellationException ex) {
// catch and ignore if we were cancelled
} finally {
progress.finish();
if (!this.isCancelled() && !msgDisplayed) {
MessageNotifyUtil.Message.info(Bundle.CSVWriter_done_notifyMsg_success(outputFile));
}
}
}
}
}

View File

@ -278,6 +278,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
//enable the new button
FilesSetDefsPanel.this.newSetButton.setEnabled(canBeEnabled);
FilesSetDefsPanel.this.importSetButton.setEnabled(canBeEnabled);
// Get the selected interesting files set and populate the set
// components.
FilesSet selectedSet = FilesSetDefsPanel.this.setsList.getSelectedValue();
@ -302,6 +303,12 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
if (!FilesSetDefsPanel.this.rulesListModel.isEmpty()) {
FilesSetDefsPanel.this.rulesList.setSelectedIndex(0);
}
} else {
// Disable the edit, delete, copy, and export buttons
FilesSetDefsPanel.this.editSetButton.setEnabled(false);
FilesSetDefsPanel.this.deleteSetButton.setEnabled(false);
FilesSetDefsPanel.this.copySetButton.setEnabled(false);
FilesSetDefsPanel.this.exportSetButton.setEnabled(false);
}
}

View File

@ -120,4 +120,13 @@ public final class TextTranslationService {
public boolean hasProvider() {
return selectedTranslator.isPresent();
}
/**
* Returns the hard limit for translation request sizes.
*
* @return
*/
public int getMaxPayloadSize() {
return selectedTranslator.get().getMaxPayloadSize();
}
}

View File

@ -56,4 +56,11 @@ public interface TextTranslator {
* Save the settings as they have been modified in the component.
*/
void saveSettings();
/**
* Returns the hard limit for translation request sizes.
*
* @return
*/
int getMaxPayloadSize();
}

View File

@ -108,7 +108,6 @@ public class BingTranslator implements TextTranslator {
String toTranslate = string.trim();
//Translates some text into English, without specifying the source langauge.
// HTML files were producing lots of white space at the end
//Google Translate required us to replace (\r\n|\n) with <br />
//but Bing Translator doesn not have that requirement.
//The free account has a maximum file size. If you have a paid account,
@ -172,4 +171,9 @@ public class BingTranslator implements TextTranslator {
throw new TranslationException("JSON text does not match Bing Translator scheme: " + e);
}
}
@Override
public int getMaxPayloadSize() {
return MAX_STRING_LENGTH;
}
}

View File

@ -47,7 +47,8 @@ import org.sleuthkit.autopsy.texttranslation.TranslationException;
public final class GoogleTranslator implements TextTranslator {
private static final Logger logger = Logger.getLogger(GoogleTranslator.class.getName());
private static final int MAX_STRING_LENGTH = 15000;
//See translate method for justification of this limit.
private static final int MAX_PAYLOAD_SIZE = 5000;
private final GoogleTranslatorSettingsPanel settingsPanel;
private final GoogleTranslatorSettings settings = new GoogleTranslatorSettings();
private Translate googleTranslate;
@ -90,21 +91,20 @@ public final class GoogleTranslator implements TextTranslator {
if (googleTranslate != null) {
try {
// Translates some text into English, without specifying the source language.
// HTML files were producing lots of white space at the end
String substring = string.trim();
// We can't currently set parameters, so we are using the default behavior of
// assuming the input is HTML. We need to replace newlines with <br> for Google to preserve them
substring = substring.replaceAll("(\r\n|\n)", "<br />");
// The API complains if the "Payload" is over 204800 bytes. I'm assuming that
// deals with the full request. At some point, we get different errors about too
// much text. Officially, Google says they will googleTranslate only 5k chars,
// but we have seen more than that working.
// there could be a value betwen 15k and 25k that works. I (BC) didn't test further
if (substring.length() > MAX_STRING_LENGTH) {
substring = substring.substring(0, MAX_STRING_LENGTH);
// The API complains if the "Payload" is over 204800 bytes. Google references that
//their service is optimized for 2K code points and recommends keeping the requests that size.
//There is a hard limit of 30K code points per request. There is also a time-based quota that
//we are not enforcing, which may lead to 403 errors. We are currently configured for a max of 5K
//in each request, for two reasons. 1) To be more in line with Google's recommendation. 2) To
//minimize accidental exceedence of time based quotas.
if (substring.length() > MAX_PAYLOAD_SIZE) {
substring = substring.substring(0, MAX_PAYLOAD_SIZE);
}
Translation translation
= googleTranslate.translate(substring);
@ -178,4 +178,9 @@ public final class GoogleTranslator implements TextTranslator {
settings.saveSettings();
loadTranslator();
}
@Override
public int getMaxPayloadSize() {
return MAX_PAYLOAD_SIZE;
}
}

View File

@ -1,7 +1,6 @@
OptionsCategory_Name_Machine_Translation=Machine Translation
OptionsCategory_Keywords_Machine_Translation_Settings=Machine Translation Settings
TranslationContentPanel.ShowLabel.text=Show:
TranslationContentPanel.warningLabel2MB.text=Only the first 1MB of text will be displayed
TranslationContentPanel.ocrLabel.text=OCR:
TranslationOptionsPanel.translationServiceLabel.text=Text translator:
TranslationOptionsPanelController.moduleErr=Module Error

View File

@ -1,6 +1,6 @@
OptionsCategory_Name_Machine_Translation=Machine Translation
OptionsCategory_Keywords_Machine_Translation_Settings=Machine Translation Settings
TranslatedContentPanel.comboBoxOption.originalText=Original Text
TranslatedContentPanel.comboBoxOption.originalText=Original Text (Up to 25KB)
TranslatedContentPanel.comboBoxOption.translatedText=Translated Text
TranslatedContentViewer.emptyTranslation=The resulting translation was empty.
TranslatedContentViewer.errorExtractingText=Could not extract text from file.
@ -12,11 +12,11 @@ TranslatedContentViewer.noServiceProvider=Machine Translation software was not f
TranslatedContentViewer.textAlreadyIndexed=Please view the original text in the Indexed Text viewer.
TranslatedContentViewer.translatingText=Translating text, please wait...
TranslatedContentViewer.translationException=Error encountered while attempting translation.
TranslatedTextViewer.maxPayloadSize=Up to the first %dKB of text will be translated
TranslatedTextViewer.title=Translation
TranslatedTextViewer.toolTip=Displays translated file text.
TranslationContentPanel.autoDetectOCR=Autodetect language
TranslationContentPanel.ShowLabel.text=Show:
TranslationContentPanel.warningLabel2MB.text=Only the first 1MB of text will be displayed
TranslationContentPanel.ocrLabel.text=OCR:
TranslationOptionsPanel.noTextTranslators.text=No text translators exist, translation is disabled.
TranslationOptionsPanel.noTextTranslatorSelected.text=No text translator selected, translation is disabled.

View File

@ -67,7 +67,7 @@ public final class TranslatedTextViewer implements TextViewer {
private static final boolean OCR_ENABLED = true;
private static final boolean OCR_DISABLED = false;
private static final int MAX_SIZE_1MB = 1024000;
private static final int MAX_EXTRACT_SIZE_BYTES = 25600;
private static final List<String> INSTALLED_LANGUAGE_PACKS = PlatformUtil.getOcrLanguagePacks();
private final TranslationContentPanel panel = new TranslationContentPanel();
@ -77,6 +77,9 @@ public final class TranslatedTextViewer implements TextViewer {
= new ThreadFactoryBuilder().setNameFormat("translation-content-viewer-%d").build();
private final ExecutorService executorService = Executors.newSingleThreadExecutor(translationThreadFactory);
@NbBundle.Messages({
"TranslatedTextViewer.maxPayloadSize=Up to the first %dKB of text will be translated"
})
@Override
public void setNode(final Node node) {
this.node = node;
@ -92,6 +95,9 @@ public final class TranslatedTextViewer implements TextViewer {
panel.addLanguagePackNames(INSTALLED_LANGUAGE_PACKS);
}
}
int payloadMaxInKB = TextTranslationService.getInstance().getMaxPayloadSize() / 1000;
panel.setWarningLabelMsg(String.format(Bundle.TranslatedTextViewer_maxPayloadSize(), payloadMaxInKB));
//Force a background task.
displayDropDownListener.actionPerformed(null);
@ -296,8 +302,9 @@ public final class TranslatedTextViewer implements TextViewer {
//Correct for UTF-8
byte[] resultInUTF8Bytes = result.getBytes("UTF8");
byte[] trimTo1MB = Arrays.copyOfRange(resultInUTF8Bytes, 0, MAX_SIZE_1MB );
return new String(trimTo1MB, "UTF-8");
byte[] trimToArraySize = Arrays.copyOfRange(resultInUTF8Bytes, 0,
Math.min(resultInUTF8Bytes.length, MAX_EXTRACT_SIZE_BYTES) );
return new String(trimToArraySize, "UTF-8");
}
/**
@ -331,7 +338,7 @@ public final class TranslatedTextViewer implements TextViewer {
//Short-circuit the read if its greater than our max
//translatable size
int bytesLeft = MAX_SIZE_1MB - bytesRead;
int bytesLeft = MAX_EXTRACT_SIZE_BYTES - bytesRead;
if (bytesLeft < read) {
textBuilder.append(cbuf, 0, bytesLeft);
@ -341,9 +348,8 @@ public final class TranslatedTextViewer implements TextViewer {
textBuilder.append(cbuf, 0, read);
bytesRead += read;
}
// The trim is on here because HTML files were observed with nearly 1MB of white space at the end
return textBuilder.toString().trim();
return textBuilder.toString();
}
/**

View File

@ -16,7 +16,7 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jPanel1" alignment="1" pref="628" max="32767" attributes="0"/>
<Component id="jPanel1" alignment="1" pref="872" max="32767" attributes="0"/>
<Component id="jScrollPane1" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
@ -48,12 +48,12 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="warningLabel2MB" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="warningLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="422" max="32767" attributes="0"/>
<Component id="ShowLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="displayTextComboBox" min="-2" pref="128" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="displayTextComboBox" min="-2" pref="150" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="ocrLabel" min="-2" pref="26" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="ocrDropdown" min="-2" pref="180" max="-2" attributes="0"/>
@ -72,18 +72,25 @@
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="9" pref="9" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="103" alignment="1" groupAlignment="3" attributes="0">
<Component id="displayTextComboBox" alignment="3" pref="26" max="32767" attributes="0"/>
<Component id="ShowLabel" alignment="3" max="32767" attributes="0"/>
<Component id="ocrLabel" alignment="3" max="32767" attributes="0"/>
<Component id="warningLabel2MB" alignment="3" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="9" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="warningLabel" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="1" attributes="0">
<Group type="103" alignment="1" groupAlignment="3" attributes="0">
<Component id="displayTextComboBox" alignment="3" pref="26" max="32767" attributes="0"/>
<Component id="ShowLabel" alignment="3" max="32767" attributes="0"/>
<Component id="ocrLabel" alignment="3" max="32767" attributes="0"/>
</Group>
<Component id="ocrDropdown" alignment="1" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="7" max="-2" attributes="0"/>
</Group>
<Component id="ocrDropdown" alignment="1" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="7" max="-2" attributes="0"/>
</Group>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
@ -153,14 +160,11 @@
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="warningLabel2MB">
<Component class="javax.swing.JLabel" name="warningLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/warning16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties" key="TranslationContentPanel.warningLabel2MB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>

View File

@ -87,6 +87,10 @@ public class TranslationContentPanel extends javax.swing.JPanel {
dropDown.removeActionListener(listener);
}
}
void setWarningLabelMsg(String msg) {
warningLabel.setText(msg);
}
@NbBundle.Messages({"TranslationContentPanel.autoDetectOCR=Autodetect language"})
final void reset() {
@ -246,7 +250,7 @@ public class TranslationContentPanel extends javax.swing.JPanel {
/**
* Selection choices to be displayed in the combobox dropdown.
*/
@Messages({"TranslatedContentPanel.comboBoxOption.originalText=Original Text",
@Messages({"TranslatedContentPanel.comboBoxOption.originalText=Original Text (Up to 25KB)",
"TranslatedContentPanel.comboBoxOption.translatedText=Translated Text"})
static enum DisplayDropdownOptions {
ORIGINAL_TEXT(Bundle.TranslatedContentPanel_comboBoxOption_originalText()),
@ -280,7 +284,7 @@ public class TranslationContentPanel extends javax.swing.JPanel {
displayTextComboBox = new javax.swing.JComboBox<>();
ocrDropdown = new javax.swing.JComboBox<>();
ocrLabel = new javax.swing.JLabel();
warningLabel2MB = new javax.swing.JLabel();
warningLabel = new javax.swing.JLabel();
jScrollPane1 = new javax.swing.JScrollPane();
displayTextArea = new javax.swing.JTextArea();
@ -302,8 +306,7 @@ public class TranslationContentPanel extends javax.swing.JPanel {
org.openide.awt.Mnemonics.setLocalizedText(ocrLabel, org.openide.util.NbBundle.getMessage(TranslationContentPanel.class, "TranslationContentPanel.ocrLabel.text")); // NOI18N
ocrLabel.setEnabled(false);
warningLabel2MB.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/warning16.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(warningLabel2MB, org.openide.util.NbBundle.getMessage(TranslationContentPanel.class, "TranslationContentPanel.warningLabel2MB.text")); // NOI18N
warningLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/warning16.png"))); // NOI18N
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
@ -311,12 +314,12 @@ public class TranslationContentPanel extends javax.swing.JPanel {
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(warningLabel2MB)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(warningLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 422, Short.MAX_VALUE)
.addComponent(ShowLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(displayTextComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 128, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(displayTextComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 150, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(ocrLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(ocrDropdown, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)
@ -331,16 +334,20 @@ public class TranslationContentPanel extends javax.swing.JPanel {
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(9, 9, 9)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(displayTextComboBox, javax.swing.GroupLayout.DEFAULT_SIZE, 26, Short.MAX_VALUE)
.addComponent(ShowLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(ocrLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(warningLabel2MB))
.addComponent(ocrDropdown))
.addGap(7, 7, 7))
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(warningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addContainerGap())
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(displayTextComboBox, javax.swing.GroupLayout.DEFAULT_SIZE, 26, Short.MAX_VALUE)
.addComponent(ShowLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(ocrLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(ocrDropdown))
.addGap(7, 7, 7))))
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
@ -362,7 +369,7 @@ public class TranslationContentPanel extends javax.swing.JPanel {
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 628, Short.MAX_VALUE)
.addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 872, Short.MAX_VALUE)
.addComponent(jScrollPane1)
);
layout.setVerticalGroup(
@ -384,6 +391,6 @@ public class TranslationContentPanel extends javax.swing.JPanel {
private javax.swing.JSeparator jSeparator2;
private javax.swing.JComboBox<String> ocrDropdown;
private javax.swing.JLabel ocrLabel;
private javax.swing.JLabel warningLabel2MB;
private javax.swing.JLabel warningLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -32,6 +32,7 @@ import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.lookup.ProxyLookup;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.actions.AddContentTagAction;
@ -163,6 +164,7 @@ class AdHocSearchFilterNode extends FilterNode {
actionsList.add(new ExternalViewerAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.openExternViewActLbl"), getOriginal()));
actionsList.add(null);
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());

View File

@ -143,8 +143,9 @@ final class VcardParser {
* @throws NoCurrentCaseException If there is no open case.
*/
void parse(File vcardFile, AbstractFile abstractFile) throws IOException, NoCurrentCaseException {
VCard vcard = Ezvcard.parse(vcardFile).first();
addContactArtifact(vcard, abstractFile);
for (VCard vcard: Ezvcard.parse(vcardFile).all()) {
addContactArtifact(vcard, abstractFile);
}
}