mirror of
https://github.com/elisspace/autopsy.git
synced 2026-09-30 14:29:51 +00:00
Merge remote-tracking branch 'upstream/develop' into 5070_fileSearch
This commit is contained in:
@@ -45,6 +45,9 @@
|
||||
|
||||
<dependency conf="core->default" org="commons-validator" name="commons-validator" rev="1.6"/>
|
||||
<dependency conf="core->default" org="net.htmlparser.jericho" name="jericho-html" rev="3.3"/>
|
||||
<dependency org="com.squareup.okhttp" name="okhttp" rev="2.7.5"/>
|
||||
<!-- https://mvnrepository.com/artifact/javax.ws.rs/javax.ws.rs-api -->
|
||||
<dependency org="javax.ws.rs" name="javax.ws.rs-api" rev="2.0"/>
|
||||
<override org="jakarta.ws.rs" module="jakarta.ws.rs-api" rev="2.1.5"/>
|
||||
</dependencies>
|
||||
</ivy-module>
|
||||
|
||||
@@ -111,6 +111,10 @@ file.reference.grpc-context-1.19.0.jar=release/modules/ext/grpc-context-1.19.0.j
|
||||
file.reference.opencensus-api-0.19.2.jar=release/modules/ext/opencensus-api-0.19.2.jar
|
||||
file.reference.opencensus-contrib-http-util-0.19.2.jar=release/modules/ext/opencensus-contrib-http-util-0.19.2.jar
|
||||
file.reference.threetenbp-1.3.3.jar=release/modules/ext/threetenbp-1.3.3.jar
|
||||
file.reference.okhttp-2.7.5-javadoc.jar=release/modules/ext/okhttp-2.7.5-javadoc.jar
|
||||
file.reference.okhttp-2.7.5-sources.jar=release/modules/ext/okhttp-2.7.5-sources.jar
|
||||
file.reference.okhttp-2.7.5.jar=release/modules/ext/okhttp-2.7.5.jar
|
||||
file.reference.okio-1.6.0.jar=release/modules/ext/okio-1.6.0.jar
|
||||
javac.source=1.8
|
||||
javac.compilerargs=-Xlint -Xlint:-serial
|
||||
license.file=../LICENSE-2.0.txt
|
||||
|
||||
@@ -773,6 +773,14 @@
|
||||
<runtime-relative-path>ext/google-api-services-translate-v2-rev20170525-1.27.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/google-api-services-translate-v2-rev20170525-1.27.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/okhttp-2.7.5.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/okhttp-2.7.5.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/okio-1.6.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/okio-1.6.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
</data>
|
||||
</configuration>
|
||||
</project>
|
||||
|
||||
@@ -186,13 +186,16 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor {
|
||||
* associated with the data source that is
|
||||
* intended to be unique across multiple cases
|
||||
* (e.g., a UUID).
|
||||
* @param imageFilePath Path to the image file.
|
||||
* @param imagePath Path to the image file.
|
||||
* @param sectorSize The sector size (use '0' for autodetect).
|
||||
* @param timeZone The time zone to use when processing dates
|
||||
* and times for the image, obtained from
|
||||
* java.util.TimeZone.getID.
|
||||
* @param chunkSize The maximum size of each chunk of the raw
|
||||
* data source as it is divided up into virtual
|
||||
* unallocated space files.
|
||||
* @param ignoreFatOrphanFiles Whether to parse orphans if the image has a
|
||||
* FAT filesystem.
|
||||
* @param md5 The MD5 hash of the image, may be null.
|
||||
* @param sha1 The SHA-1 hash of the image, may be null.
|
||||
* @param sha256 The SHA-256 hash of the image, may be null.
|
||||
* @param src The source directory of image.
|
||||
* @param dest The destination directory to copy the source.
|
||||
* @param progressMonitor Progress monitor for reporting progress
|
||||
|
||||
@@ -66,6 +66,7 @@ DataSourceSummaryDialog.window.title=Data Sources Summary
|
||||
DataSourceSummaryNode.column.dataSourceName.header=Data Source Name
|
||||
DataSourceSummaryNode.column.files.header=Files
|
||||
DataSourceSummaryNode.column.results.header=Results
|
||||
DataSourceSummaryNode.column.status.header=Ingest Status
|
||||
DataSourceSummaryNode.column.tags.header=Tags
|
||||
DataSourceSummaryNode.column.type.header=Type
|
||||
DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source
|
||||
|
||||
@@ -37,8 +37,10 @@ import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryNode.DataSourceSummaryEntryNode;
|
||||
import static javax.swing.SwingConstants.RIGHT;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.table.TableColumn;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.IngestJobInfo;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
@@ -50,9 +52,10 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = Logger.getLogger(DataSourceBrowser.class.getName());
|
||||
private static final int COUNT_COLUMN_WIDTH = 25;
|
||||
private static final int USAGE_COLUMN_WIDTH = 120;
|
||||
private static final int DATA_SOURCE_COLUMN_WIDTH = 325;
|
||||
private static final int COUNT_COLUMN_WIDTH = 20;
|
||||
private static final int INGEST_STATUS_WIDTH = 50;
|
||||
private static final int USAGE_COLUMN_WIDTH = 110;
|
||||
private static final int DATA_SOURCE_COLUMN_WIDTH = 280;
|
||||
private final Outline outline;
|
||||
private final org.openide.explorer.view.OutlineView outlineView;
|
||||
private final ExplorerManager explorerManager;
|
||||
@@ -69,6 +72,7 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana
|
||||
outlineView = new org.openide.explorer.view.OutlineView();
|
||||
this.setVisible(true);
|
||||
outlineView.setPropertyColumns(
|
||||
Bundle.DataSourceSummaryNode_column_status_header(), Bundle.DataSourceSummaryNode_column_status_header(),
|
||||
Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_type_header(),
|
||||
Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_files_header(),
|
||||
Bundle.DataSourceSummaryNode_column_results_header(), Bundle.DataSourceSummaryNode_column_results_header(),
|
||||
@@ -90,6 +94,8 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana
|
||||
column.setPreferredWidth(COUNT_COLUMN_WIDTH);
|
||||
} else if (column.getHeaderValue().toString().equals(Bundle.DataSourceSummaryNode_column_type_header())) {
|
||||
column.setPreferredWidth(USAGE_COLUMN_WIDTH);
|
||||
} else if (column.getHeaderValue().toString().equals(Bundle.DataSourceSummaryNode_column_status_header())) {
|
||||
column.setPreferredWidth(INGEST_STATUS_WIDTH);
|
||||
} else {
|
||||
column.setPreferredWidth(DATA_SOURCE_COLUMN_WIDTH);
|
||||
}
|
||||
@@ -182,6 +188,47 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the DataSourceBrowser to display up to date status information for
|
||||
* the data sources.
|
||||
*
|
||||
* @param dataSourceId the ID of the data source which should be updated
|
||||
* @param newStatus the new status which the data source should have
|
||||
*/
|
||||
void refresh(long dataSourceId, IngestJobInfo.IngestJobStatusType newStatus) {
|
||||
|
||||
//attempt to update the status of any datasources that had status which was STARTED
|
||||
for (DataSourceSummary summary : dataSourceSummaryList) {
|
||||
if (summary.getDataSource().getId() == dataSourceId) {
|
||||
summary.setIngestStatus(newStatus);
|
||||
}
|
||||
}
|
||||
//figure out which nodes were previously selected
|
||||
Node[] selectedNodes = explorerManager.getSelectedNodes();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
explorerManager.setRootContext(new DataSourceSummaryNode(dataSourceSummaryList));
|
||||
List<Node> nodesToSelect = new ArrayList<>();
|
||||
for (Node node : explorerManager.getRootContext().getChildren().getNodes()) {
|
||||
if (node instanceof DataSourceSummaryEntryNode) {
|
||||
//there should only be one selected node as multi-select is disabled
|
||||
for (Node selectedNode : selectedNodes) {
|
||||
if (((DataSourceSummaryEntryNode) node).getDataSource().equals(((DataSourceSummaryEntryNode) selectedNode).getDataSource())) {
|
||||
nodesToSelect.add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//reselect the previously selected Nodes
|
||||
try {
|
||||
explorerManager.setSelectedNodes(nodesToSelect.toArray(new Node[nodesToSelect.size()]));
|
||||
} catch (PropertyVetoException ex) {
|
||||
logger.log(Level.WARNING, "Error selecting previously selected nodes", ex);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
|
||||
@@ -18,15 +18,27 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.datamodel.CaseDbAccessManager;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.IngestJobInfo.IngestJobStatusType;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Wrapper object for a DataSource and the information associated with it.
|
||||
*
|
||||
*/
|
||||
class DataSourceSummary {
|
||||
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DataSourceSummary.class.getName());
|
||||
private static final String INGEST_JOB_STATUS_QUERY = "status_id FROM ingest_jobs WHERE obj_id=";
|
||||
private final DataSource dataSource;
|
||||
private IngestJobStatusType status = null;
|
||||
private final String type;
|
||||
private final long filesCount;
|
||||
private final long resultsCount;
|
||||
@@ -45,12 +57,26 @@ class DataSourceSummary {
|
||||
*/
|
||||
DataSourceSummary(DataSource dSource, String typeValue, Long numberOfFiles, Long numberOfResults, Long numberOfTags) {
|
||||
dataSource = dSource;
|
||||
getStatusFromDatabase();
|
||||
type = typeValue == null ? "" : typeValue;
|
||||
filesCount = numberOfFiles == null ? 0 : numberOfFiles;
|
||||
resultsCount = numberOfResults == null ? 0 : numberOfResults;
|
||||
tagsCount = numberOfTags == null ? 0 : numberOfTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of the ingest job from the case database
|
||||
*/
|
||||
private void getStatusFromDatabase() {
|
||||
try {
|
||||
IngestJobQueryCallback callback = new IngestJobQueryCallback();
|
||||
Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select(INGEST_JOB_STATUS_QUERY + dataSource.getId(), callback);
|
||||
status = callback.getStatus();
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error getting status for data source from case database", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DataSource
|
||||
*
|
||||
@@ -60,6 +86,16 @@ class DataSourceSummary {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually set the ingest job status
|
||||
*
|
||||
* @param ingestStatus the status which the ingest job should have
|
||||
* currently, null to display empty string
|
||||
*/
|
||||
void setIngestStatus(IngestJobStatusType ingestStatus) {
|
||||
status = ingestStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of this DataSource
|
||||
*
|
||||
@@ -87,6 +123,16 @@ class DataSourceSummary {
|
||||
return resultsCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IngestJobStatusType associated with this data source.
|
||||
*
|
||||
* @return the IngestJobStatusType associated with this data source. Can be
|
||||
* null if the IngestJobStatusType is not STARTED or COMPLETED.
|
||||
*/
|
||||
IngestJobStatusType getIngestStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of tagged content objects in this DataSource
|
||||
*
|
||||
@@ -95,4 +141,40 @@ class DataSourceSummary {
|
||||
long getTagsCount() {
|
||||
return tagsCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to parse result set, getting the status to be associated with
|
||||
* this data source
|
||||
*/
|
||||
class IngestJobQueryCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
|
||||
|
||||
private IngestJobStatusType jobStatus = null;
|
||||
|
||||
@Override
|
||||
public void process(ResultSet rs) {
|
||||
try {
|
||||
while (rs.next()) {
|
||||
IngestJobStatusType currentStatus = IngestJobStatusType.fromID(rs.getInt("status_id"));
|
||||
if (currentStatus == IngestJobStatusType.COMPLETED) {
|
||||
jobStatus = currentStatus;
|
||||
} else if (currentStatus == IngestJobStatusType.STARTED) {
|
||||
jobStatus = currentStatus;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.WARNING, "Error getting status for ingest job", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status which was determined for this callback
|
||||
*
|
||||
* @return the status of the data source which was queried for
|
||||
*/
|
||||
IngestJobStatusType getStatus() {
|
||||
return jobStatus;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+16
-2
@@ -19,14 +19,18 @@
|
||||
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
|
||||
|
||||
import java.awt.Frame;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.Map;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent;
|
||||
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent.Reason;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.IngestJobInfo;
|
||||
|
||||
/**
|
||||
* Dialog for displaying the Data Sources Summary information
|
||||
@@ -38,7 +42,6 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
|
||||
private final DataSourceSummaryDetailsPanel detailsPanel;
|
||||
private final DataSourceBrowser dataSourcesPanel;
|
||||
private final IngestJobInfoPanel ingestHistoryPanel;
|
||||
private static final Logger logger = Logger.getLogger(DataSourceSummaryDialog.class.getName());
|
||||
|
||||
/**
|
||||
* Creates new form DataSourceSummaryDialog for displaying a summary of the
|
||||
@@ -73,6 +76,17 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
|
||||
this.repaint();
|
||||
}
|
||||
});
|
||||
//add listener to refresh jobs with Started status when they complete
|
||||
IngestManager.getInstance().addIngestJobEventListener((PropertyChangeEvent evt) -> {
|
||||
if (evt instanceof DataSourceAnalysisCompletedEvent) {
|
||||
DataSourceAnalysisCompletedEvent dsEvent = (DataSourceAnalysisCompletedEvent) evt;
|
||||
if (dsEvent.getResult() == Reason.ANALYSIS_COMPLETED) {
|
||||
dataSourcesPanel.refresh(dsEvent.getDataSource().getId(), IngestJobInfo.IngestJobStatusType.COMPLETED);
|
||||
} else if (dsEvent.getResult() == Reason.ANALYSIS_CANCELLED) {
|
||||
dataSourcesPanel.refresh(dsEvent.getDataSource().getId(), null);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.pack();
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@ final class DataSourceSummaryNode extends AbstractNode {
|
||||
static final class DataSourceSummaryEntryNode extends AbstractNode {
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final String status;
|
||||
private final String type;
|
||||
private final long filesCount;
|
||||
private final long resultsCount;
|
||||
@@ -124,6 +125,7 @@ final class DataSourceSummaryNode extends AbstractNode {
|
||||
DataSourceSummaryEntryNode(DataSourceSummary dataSourceSummary) {
|
||||
super(Children.LEAF);
|
||||
dataSource = dataSourceSummary.getDataSource();
|
||||
status = dataSourceSummary.getIngestStatus() == null ? "" : dataSourceSummary.getIngestStatus().getDisplayName();
|
||||
type = dataSourceSummary.getType();
|
||||
filesCount = dataSourceSummary.getFilesCount();
|
||||
resultsCount = dataSourceSummary.getResultsCount();
|
||||
@@ -143,6 +145,7 @@ final class DataSourceSummaryNode extends AbstractNode {
|
||||
}
|
||||
|
||||
@Messages({"DataSourceSummaryNode.column.dataSourceName.header=Data Source Name",
|
||||
"DataSourceSummaryNode.column.status.header=Ingest Status",
|
||||
"DataSourceSummaryNode.column.type.header=Type",
|
||||
"DataSourceSummaryNode.column.files.header=Files",
|
||||
"DataSourceSummaryNode.column.results.header=Results",
|
||||
@@ -157,6 +160,7 @@ final class DataSourceSummaryNode extends AbstractNode {
|
||||
}
|
||||
sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_dataSourceName_header(), Bundle.DataSourceSummaryNode_column_dataSourceName_header(), Bundle.DataSourceSummaryNode_column_dataSourceName_header(),
|
||||
dataSource));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_status_header(), Bundle.DataSourceSummaryNode_column_status_header(), Bundle.DataSourceSummaryNode_column_status_header(), status));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_type_header(),
|
||||
type));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_files_header(),
|
||||
|
||||
@@ -272,9 +272,11 @@ final public class FiltersPanel extends JPanel {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Populate the devices filter widgets
|
||||
*
|
||||
* @param initialState
|
||||
*/
|
||||
private void updateDeviceFilter(boolean initialState) {
|
||||
try {
|
||||
@@ -301,7 +303,7 @@ final public class FiltersPanel extends JPanel {
|
||||
* Given a list of subFilters, set the states of the panel controls
|
||||
* accordingly.
|
||||
*
|
||||
* @param subFilters A list of subFilters
|
||||
* @param commFilter Contains a list of subFilters
|
||||
*/
|
||||
public void setFilters(CommunicationsFilter commFilter) {
|
||||
List<CommunicationsFilter.SubFilter> subFilters = commFilter.getAndFilters();
|
||||
|
||||
@@ -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());
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
|
||||
|
||||
/**
|
||||
* Sets the value of currentSelection and passes the SelectionInfo onto the
|
||||
* currently selected\visible tab.
|
||||
* currently selected or visible tab.
|
||||
*
|
||||
* @param info Currently selected account nodes
|
||||
*/
|
||||
|
||||
@@ -83,7 +83,7 @@ MediaViewImagePanel.zoomResetButton.text=Reset
|
||||
MediaViewImagePanel.zoomTextField.text=
|
||||
MediaViewImagePanel.rotationTextField.text=
|
||||
MediaViewImagePanel.rotateLeftButton.toolTipText=
|
||||
HtmlPanel.showImagesToggleButton.text=Show Images
|
||||
HtmlPanel.showImagesToggleButton.text=Download Images
|
||||
MediaPlayerPanel.audioSlider.toolTipText=
|
||||
MediaPlayerPanel.VolumeIcon.text=\ \ \ \ \ Volume
|
||||
MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00
|
||||
|
||||
@@ -22,7 +22,7 @@ GstVideoPanel.cannotProcFile.err=The media player cannot process this file.
|
||||
GstVideoPanel.noOpenCase.errMsg=No open case available.
|
||||
Html_text_display_error=The HTML text cannot be displayed, it may not be correctly formed HTML.
|
||||
HtmlPanel_showImagesToggleButton_hide=Hide Images
|
||||
HtmlPanel_showImagesToggleButton_show=Show Images
|
||||
HtmlPanel_showImagesToggleButton_show=Download Images
|
||||
HtmlViewer_file_error=This file is missing or unreadable.
|
||||
MediaFileViewer.initGst.gstException.msg=Error initializing gstreamer for audio/video viewing and frame extraction capabilities. Video and audio viewing will be disabled.
|
||||
GstVideoPanel.setupVideo.infoLabel.text=Playback of deleted videos is not supported, use an external player.
|
||||
@@ -145,7 +145,7 @@ MediaViewImagePanel.zoomResetButton.text=Reset
|
||||
MediaViewImagePanel.zoomTextField.text=
|
||||
MediaViewImagePanel.rotationTextField.text=
|
||||
MediaViewImagePanel.rotateLeftButton.toolTipText=
|
||||
HtmlPanel.showImagesToggleButton.text=Show Images
|
||||
HtmlPanel.showImagesToggleButton.text=Download Images
|
||||
MediaPlayerPanel.audioSlider.toolTipText=
|
||||
MediaPlayerPanel.VolumeIcon.text=\ \ \ \ \ Volume
|
||||
MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00
|
||||
|
||||
@@ -77,12 +77,12 @@ public class FileViewer extends javax.swing.JPanel implements DataContentViewer
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the FileTypeViewer for a given mimetype
|
||||
* Get the FileTypeViewer for a given file
|
||||
*
|
||||
* @param mimeType
|
||||
* @param file
|
||||
*
|
||||
* @return FileTypeViewer, null if no known content viewer supports the
|
||||
* mimetype
|
||||
* file
|
||||
*/
|
||||
private FileTypeViewer getSupportingViewer(AbstractFile file) {
|
||||
FileTypeViewer viewer = mimeTypeToViewerMap.get(file.getMIMEType());
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="showImagesToggleButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="95" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="75" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="htmlJPanel" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.contentviewers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
@@ -25,8 +30,9 @@ import javafx.concurrent.Worker;
|
||||
import javafx.scene.web.WebView;
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
import javafx.scene.Scene;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Node;
|
||||
import net.htmlparser.jericho.Attribute;
|
||||
import net.htmlparser.jericho.OutputDocument;
|
||||
import net.htmlparser.jericho.Source;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
@@ -38,6 +44,7 @@ import org.w3c.dom.events.EventTarget;
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class HtmlPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(HtmlPanel.class.getName());
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String TEXT_TYPE = "text/plain";
|
||||
private final JFXPanel jfxPanel = new JFXPanel();
|
||||
@@ -92,26 +99,76 @@ final class HtmlPanel extends javax.swing.JPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans out input HTML string
|
||||
* Cleans out input HTML string so it will not access resources over the internet
|
||||
*
|
||||
* @param htmlInString The HTML string to cleanse
|
||||
*
|
||||
* @return The cleansed HTML String
|
||||
*/
|
||||
private String cleanseHTML(String htmlInString) {
|
||||
org.jsoup.nodes.Document doc = Jsoup.parse(htmlInString);
|
||||
// remove all 'img' tags.
|
||||
doc.select("img").stream().forEach(Node::remove);
|
||||
// remove all 'span' tags, these are often images which are ads
|
||||
doc.select("span").stream().forEach(Node::remove);
|
||||
return doc.html();
|
||||
String returnString = "";
|
||||
try {
|
||||
Source source = new Source(new StringReader(htmlInString));
|
||||
OutputDocument document = new OutputDocument(source);
|
||||
//remove background images
|
||||
source.getAllTags().stream().filter((tag) -> (tag.toString().contains("background-image"))).forEachOrdered((tag) -> {
|
||||
document.remove(tag);
|
||||
});
|
||||
//remove images
|
||||
source.getAllElements("img").forEach((element) -> {
|
||||
document.remove(element.getAllTags());
|
||||
});
|
||||
//remove frames
|
||||
source.getAllElements("frame").forEach((element) -> {
|
||||
document.remove(element.getAllTags());
|
||||
});
|
||||
//remove iframes
|
||||
source.getAllElements("iframe").forEach((element) -> {
|
||||
document.remove(element.getAllTags());
|
||||
});
|
||||
//remove pictures
|
||||
source.getAllElements("picture").forEach((element) -> {
|
||||
document.remove(element.getAllTags());
|
||||
});
|
||||
//remove svg
|
||||
source.getAllElements("svg").forEach((element) -> {
|
||||
document.remove(element.getAllTags());
|
||||
});
|
||||
//remove audio
|
||||
source.getAllElements("audio").forEach((element) -> {
|
||||
document.remove(element.getAllTags());
|
||||
});
|
||||
//remove video
|
||||
source.getAllElements("video").forEach((element) -> {
|
||||
document.remove(element.getAllTags());
|
||||
});
|
||||
//remove tracks
|
||||
source.getAllElements("track").forEach((element) -> {
|
||||
document.remove(element.getAllTags());
|
||||
});
|
||||
//remove embeded external elements
|
||||
source.getAllElements("embed").forEach((element) -> {
|
||||
document.remove(element.getAllTags());
|
||||
});
|
||||
//remove linked elements
|
||||
source.getAllElements("link").forEach((element) -> {
|
||||
document.remove(element.getAllTags());
|
||||
});
|
||||
//remove other URI elements such as input boxes
|
||||
List<Attribute> attributesToRemove = source.getURIAttributes();
|
||||
document.remove(attributesToRemove);
|
||||
returnString = document.toString();
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Unable to read html for cleaning out URI elements with Jericho", ex);
|
||||
}
|
||||
return returnString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the panel to reflect the current show/hide images setting.
|
||||
*/
|
||||
@Messages({
|
||||
"HtmlPanel_showImagesToggleButton_show=Show Images",
|
||||
"HtmlPanel_showImagesToggleButton_show=Download Images",
|
||||
"HtmlPanel_showImagesToggleButton_hide=Hide Images",
|
||||
"Html_text_display_error=The HTML text cannot be displayed, it may not be correctly formed HTML.",})
|
||||
private void refresh() {
|
||||
@@ -164,7 +221,7 @@ final class HtmlPanel extends javax.swing.JPanel {
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(showImagesToggleButton)
|
||||
.addGap(0, 95, Short.MAX_VALUE))
|
||||
.addGap(0, 75, Short.MAX_VALUE))
|
||||
.addComponent(htmlJPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
|
||||
+12
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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.
|
||||
*
|
||||
|
||||
@@ -504,10 +504,10 @@ public final class UserPreferences {
|
||||
/**
|
||||
* Get the maximum number of results to display in a result table.
|
||||
*
|
||||
* @return Saved value or default (0) which indicates no max.
|
||||
* @return Saved value or default (10,000).
|
||||
*/
|
||||
public static int getResultsTablePageSize() {
|
||||
return preferences.getInt(RESULTS_TABLE_PAGE_SIZE, 0);
|
||||
return preferences.getInt(RESULTS_TABLE_PAGE_SIZE, 10_000);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -216,4 +216,5 @@ DataResultViewerTable.pagesLabel.text=Pages:
|
||||
DataResultViewerTable.pageNumLabel.text=
|
||||
DataResultViewerTable.pageLabel.text=Page:
|
||||
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
|
||||
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nAll results are shown in the results table if this value is 0 (default).\n<br>You may want to consider setting this value if you have large numbers of results that are taking a long time to display.\n</html>
|
||||
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
|
||||
|
||||
@@ -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
|
||||
@@ -269,4 +270,5 @@ DataResultViewerTable.pagesLabel.text=Pages:
|
||||
DataResultViewerTable.pageNumLabel.text=
|
||||
DataResultViewerTable.pageLabel.text=Page:
|
||||
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
|
||||
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nAll results are shown in the results table if this value is 0 (default).\n<br>You may want to consider setting this value if you have large numbers of results that are taking a long time to display.\n</html>
|
||||
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
|
||||
|
||||
@@ -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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="exportCSVButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -286,7 +286,12 @@ public abstract class BaseChildFactory<T extends Content> extends ChildFactory.D
|
||||
* If pageSize is set split keys into pages, otherwise create a
|
||||
* single page containing all keys.
|
||||
*/
|
||||
pages = Lists.partition(keys, pageSize > 0 ? pageSize : keys.size());
|
||||
if (keys.isEmpty()) {
|
||||
pages.clear();
|
||||
} else {
|
||||
pages = Lists.partition(keys, pageSize > 0 ? pageSize : keys.size());
|
||||
}
|
||||
|
||||
if (pages.size() != oldPageCount) {
|
||||
try {
|
||||
// Number of pages has changed so we need to send out a notification.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -390,6 +390,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
* @param skCase
|
||||
* @param o Observable that will notify when there could be new
|
||||
* data to display
|
||||
* @param nodeName
|
||||
*/
|
||||
private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o, String nodeName) {
|
||||
super(nodeName, new ViewsKnownAndSlackFilter<>());
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -251,10 +251,10 @@ final class HexView extends JPanel {
|
||||
this.setHighlight(startByte, endByte);
|
||||
|
||||
if (startByte != endByte) {
|
||||
/**
|
||||
* @param 1 Start
|
||||
* @param 2 End
|
||||
* @param 3 Len
|
||||
/*
|
||||
* param 1 Start
|
||||
* param 2 End
|
||||
* param 3 Len
|
||||
*/
|
||||
int length = endByte - startByte;
|
||||
String text = Bundle.HexView_statusTemplate_nonZeroLength(
|
||||
@@ -266,8 +266,8 @@ final class HexView extends JPanel {
|
||||
String.format("0x%1$x", length));
|
||||
statusLabel.setText(text);
|
||||
} else {
|
||||
/**
|
||||
* @param 1 Start
|
||||
/*
|
||||
* param 1 Start
|
||||
*/
|
||||
String text = Bundle.HexView_statusTemplate_zeroLength(startByte, String.format("0x%1$x", startByte));
|
||||
statusLabel.setText(text);
|
||||
|
||||
@@ -58,10 +58,10 @@ public final class RejTreeKeyView extends RejTreeNodeView {
|
||||
public RejTreeKeyView(RejTreeKeyNode node) {
|
||||
super(new BorderLayout());
|
||||
|
||||
/**
|
||||
* @param 1 Name
|
||||
* @param 2 Number of subkeys
|
||||
* @param 3 Number of values
|
||||
/*
|
||||
* param 1 Name
|
||||
* param 2 Number of subkeys
|
||||
* param 3 Number of values
|
||||
*/
|
||||
String metadataTemplate = "<html><i>"
|
||||
+ Bundle.RejTreeKeyView_template_name()
|
||||
|
||||
@@ -51,9 +51,9 @@ public final class RejTreeValueView extends RejTreeNodeView {
|
||||
"RejTreeValueView.valueBorder.title=Value",})
|
||||
public RejTreeValueView(RejTreeValueNode node) {
|
||||
super(new BorderLayout());
|
||||
/**
|
||||
* @param 1 Name
|
||||
* @param 2 Type
|
||||
/*
|
||||
* param 1 Name
|
||||
* param 2 Type
|
||||
*/
|
||||
String metadataTemplate = "<html><i>"
|
||||
+ Bundle.RejTreeValueView_template_name()
|
||||
@@ -63,8 +63,8 @@ public final class RejTreeValueView extends RejTreeNodeView {
|
||||
String valueName;
|
||||
String valueType;
|
||||
|
||||
/**
|
||||
* @param 1 Value
|
||||
/*
|
||||
* param 1 Value
|
||||
*/
|
||||
String valueTemplate = "<html>%1$s</html>";
|
||||
try {
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -63,9 +64,12 @@ class FileReportText implements FileReportModule {
|
||||
public void startReport(String baseReportDir) {
|
||||
this.reportPath = baseReportDir + FILE_NAME;
|
||||
try {
|
||||
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(this.reportPath)));
|
||||
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(this.reportPath), StandardCharsets.UTF_8));
|
||||
out.write('\ufeff');
|
||||
} catch (FileNotFoundException ex) {
|
||||
logger.log(Level.WARNING, "Failed to create report text file", ex); //NON-NLS
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Failed to write BOM to report text file", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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.texttranslation.translators;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.RequestBody;
|
||||
import com.squareup.okhttp.Response;
|
||||
import java.awt.Component;
|
||||
import java.io.IOException;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.texttranslation.TextTranslator;
|
||||
import org.sleuthkit.autopsy.texttranslation.TranslationException;
|
||||
|
||||
/**
|
||||
* Translates text by making HTTP requests to Bing Translator. This requires a
|
||||
* valid subscription key for a Microsoft Azure account.
|
||||
*/
|
||||
@ServiceProvider(service = TextTranslator.class)
|
||||
public class BingTranslator implements TextTranslator {
|
||||
|
||||
//The target language follows the to= in the string below. You can include multiple target
|
||||
//languages separated by commas. A full list of supported languages is here:
|
||||
//https://docs.microsoft.com/en-us/azure/cognitive-services/translator/language-support
|
||||
private static final String BASE_URL = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=";
|
||||
private static final int MAX_STRING_LENGTH = 5000;
|
||||
private final BingTranslatorSettingsPanel settingsPanel;
|
||||
private final BingTranslatorSettings settings = new BingTranslatorSettings();
|
||||
// This sends messages to Microsoft.
|
||||
private final OkHttpClient CLIENT = new OkHttpClient();
|
||||
|
||||
/**
|
||||
* Create a Bing Translator
|
||||
*/
|
||||
public BingTranslator() {
|
||||
settingsPanel = new BingTranslatorSettingsPanel(settings.getAuthenticationKey(), settings.getTargetLanguageCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tranlationurl for the specified language code
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param languageCode language code for language to translate to
|
||||
*
|
||||
* @return a string representation of the url to request translation from
|
||||
*/
|
||||
static String getTranlatorUrl(String languageCode) {
|
||||
return BASE_URL + languageCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an input text to the JSON format required by Bing Translator,
|
||||
* posts it to Microsoft, and returns the JSON text response.
|
||||
*
|
||||
* @param string The input text to be translated.
|
||||
*
|
||||
* @return The translation response as a JSON string
|
||||
*
|
||||
* @throws IOException if the request could not be executed due to
|
||||
* cancellation, a connectivity problem or timeout.
|
||||
*/
|
||||
public String postTranslationRequest(String string) throws IOException {
|
||||
MediaType mediaType = MediaType.parse("application/json");
|
||||
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("Text", string);
|
||||
jsonArray.add(jsonObject);
|
||||
String bodyString = jsonArray.toString();
|
||||
|
||||
RequestBody body = RequestBody.create(mediaType,
|
||||
bodyString);
|
||||
Request request = new Request.Builder()
|
||||
.url(getTranlatorUrl(settings.getTargetLanguageCode())).post(body)
|
||||
.addHeader("Ocp-Apim-Subscription-Key", settings.getAuthenticationKey())
|
||||
.addHeader("Content-type", "application/json").build();
|
||||
Response response = CLIENT.newCall(request).execute();
|
||||
return response.body().string();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String translate(String string) throws TranslationException {
|
||||
if (settings.getAuthenticationKey() == null || settings.getAuthenticationKey().isEmpty()) {
|
||||
throw new TranslationException("Bing Translator has not been configured, authentication key needs to be specified");
|
||||
}
|
||||
String toTranslate = string.trim();
|
||||
//Translates some text into English, without specifying the source langauge.
|
||||
|
||||
//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,
|
||||
//you probably still want to limit file size to prevent accidentally
|
||||
//translating very large documents.
|
||||
if (toTranslate.length() > MAX_STRING_LENGTH) {
|
||||
toTranslate = toTranslate.substring(0, MAX_STRING_LENGTH);
|
||||
}
|
||||
|
||||
try {
|
||||
String response = postTranslationRequest(toTranslate);
|
||||
return parseJSONResponse(response);
|
||||
} catch (IOException | TranslationException ex) {
|
||||
throw new TranslationException("Exception while attempting to translate using BingTranslator", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Messages({"BingTranslator.name.text=Bing Translator"})
|
||||
@Override
|
||||
public String getName() {
|
||||
return Bundle.BingTranslator_name_text();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponent() {
|
||||
return settingsPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveSettings() {
|
||||
settings.setAuthenticationKey(settingsPanel.getAuthenticationKey());
|
||||
settings.setTargetLanguageCode(settingsPanel.getTargetLanguageCode());
|
||||
settings.saveSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the response to get the translated text
|
||||
*
|
||||
* @param json_text the json which was received as a response to a
|
||||
* translation request
|
||||
*
|
||||
* @return the translated text
|
||||
*
|
||||
* @throws TranslationException
|
||||
*/
|
||||
private String parseJSONResponse(String json_text) throws TranslationException {
|
||||
/*
|
||||
* Here is an example of the text we get from Bing when input is "gato",
|
||||
* the Spanish word for cat: [ { "detectedLanguage": { "language": "es",
|
||||
* "score": 1.0 }, "translations": [ { "text": "cat", "to": "en" } ] } ]
|
||||
*/
|
||||
JsonParser parser = new JsonParser();
|
||||
try {
|
||||
JsonArray responses = parser.parse(json_text).getAsJsonArray();
|
||||
//As far as I know, there's always exactly one item in the array.
|
||||
JsonObject response0 = responses.get(0).getAsJsonObject();
|
||||
JsonArray translations = response0.getAsJsonArray("translations");
|
||||
JsonObject translation0 = translations.get(0).getAsJsonObject();
|
||||
return translation0.get("text").getAsString();
|
||||
} catch (IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException e) {
|
||||
throw new TranslationException("JSON text does not match Bing Translator scheme: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPayloadSize() {
|
||||
return MAX_STRING_LENGTH;
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Autopsy
|
||||
*
|
||||
* 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.texttranslation.translators;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
|
||||
/**
|
||||
* Class to handle the settings associated with the BingTranslator
|
||||
*/
|
||||
public final class BingTranslatorSettings {
|
||||
|
||||
private static final String AUTHENTICATION_KEY = "Credentials";
|
||||
private static final String BING_TRANSLATE_NAME = "BingTranslate";
|
||||
private static final String DEFAULT_AUTHENTICATION = "";
|
||||
private static final String DEFAULT_TARGET_LANGUAGE = "en";
|
||||
private static final String TARGET_LANGUAGE_CODE_KEY = "TargetLanguageCode";
|
||||
private String authenticationKey;
|
||||
private String targetLanguageCode;
|
||||
|
||||
/**
|
||||
* Construct a new BingTranslatorSettings object
|
||||
*/
|
||||
BingTranslatorSettings() {
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Authentication key to be used for the Microsoft translation
|
||||
* service
|
||||
*
|
||||
* @return the Authentication key for the service
|
||||
*/
|
||||
String getAuthenticationKey() {
|
||||
return authenticationKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Authentication key to be used for the Microsoft translation
|
||||
* service
|
||||
*
|
||||
* @param authKey the Authentication key for the service
|
||||
*/
|
||||
void setAuthenticationKey(String authKey) {
|
||||
authenticationKey = authKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings into memory from their on disk storage
|
||||
*/
|
||||
void loadSettings() {
|
||||
if (!ModuleSettings.configExists(BING_TRANSLATE_NAME)) {
|
||||
ModuleSettings.makeConfigFile(BING_TRANSLATE_NAME);
|
||||
}
|
||||
if (ModuleSettings.settingExists(BING_TRANSLATE_NAME, AUTHENTICATION_KEY)) {
|
||||
authenticationKey = ModuleSettings.getConfigSetting(BING_TRANSLATE_NAME, AUTHENTICATION_KEY);
|
||||
}
|
||||
if (authenticationKey == null || StringUtils.isBlank(authenticationKey)) {
|
||||
authenticationKey = DEFAULT_AUTHENTICATION;
|
||||
}
|
||||
if (ModuleSettings.settingExists(BING_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY)) {
|
||||
targetLanguageCode = ModuleSettings.getConfigSetting(BING_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY);
|
||||
}
|
||||
if (targetLanguageCode == null || StringUtils.isBlank(targetLanguageCode)) {
|
||||
targetLanguageCode = DEFAULT_TARGET_LANGUAGE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target language code
|
||||
*
|
||||
* @return the code used to identify the target language
|
||||
*/
|
||||
String getTargetLanguageCode() {
|
||||
return targetLanguageCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the target language code. If a blank code is specified it sets the
|
||||
* default code instead.
|
||||
*
|
||||
* @param code the target language code to set
|
||||
*/
|
||||
void setTargetLanguageCode(String code) {
|
||||
if (StringUtils.isBlank(code)) {
|
||||
targetLanguageCode = DEFAULT_TARGET_LANGUAGE;
|
||||
} else {
|
||||
targetLanguageCode = code;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the setting from memory to their location on disk
|
||||
*/
|
||||
void saveSettings() {
|
||||
ModuleSettings.setConfigSetting(BING_TRANSLATE_NAME, AUTHENTICATION_KEY, authenticationKey);
|
||||
ModuleSettings.setConfigSetting(BING_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY, targetLanguageCode);
|
||||
}
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
||||
<Component id="warningLabel" min="-2" pref="551" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="targetLanguageLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Component id="targetLanguageComboBox" min="-2" pref="192" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="authenticationKeyLabel" min="-2" pref="100" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="authenticationKeyField" min="-2" pref="486" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="20" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="testButton" min="-2" pref="79" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="25" max="-2" attributes="0"/>
|
||||
<Component id="untranslatedLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="testUntranslatedTextField" min="-2" pref="140" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="resultLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="testResultValueLabel" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="authenticationKeyField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="authenticationKeyLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="targetLanguageLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="targetLanguageComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="testButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="testUntranslatedTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="untranslatedLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="resultLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="testResultValueLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="warningLabel" min="-2" pref="18" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextField" name="authenticationKeyField">
|
||||
<Properties>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties" key="BingTranslatorSettingsPanel.authenticationKeyField.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="warningLabel">
|
||||
<Properties>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties" key="GoogleTranslatorSettingsPanel.warningLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="testButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties" key="BingTranslatorSettingsPanel.testButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="testButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="targetLanguageLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties" key="BingTranslatorSettingsPanel.targetLanguageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JComboBox" name="targetLanguageComboBox">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
|
||||
<StringArray count="0"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="targetLanguageComboBoxSelected"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<LanguageWrapper>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="testUntranslatedTextField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="DEFUALT_TEST_STRING" type="code"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="untranslatedLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties" key="BingTranslatorSettingsPanel.untranslatedLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="resultLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties" key="BingTranslatorSettingsPanel.resultLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="testResultValueLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties" key="BingTranslatorSettingsPanel.testResultValueLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="authenticationKeyLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties" key="BingTranslatorSettingsPanel.authenticationKeyLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Autopsy
|
||||
*
|
||||
* 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.texttranslation.translators;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.RequestBody;
|
||||
import com.squareup.okhttp.Response;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
|
||||
/**
|
||||
* Settings panel for the BingTranslator
|
||||
*/
|
||||
public class BingTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(BingTranslatorSettingsPanel.class.getName());
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String GET_TARGET_LANGUAGES_URL = "https://api.cognitive.microsofttranslator.com/languages?api-version=3.0&scope=translation";
|
||||
private static final String DEFUALT_TEST_STRING = "traducción exitoso"; //spanish which should translate to something along the lines of "successful translation"
|
||||
private String targetLanguageCode = "";
|
||||
|
||||
/**
|
||||
* Creates new form BingTranslatorSettingsPanel
|
||||
*/
|
||||
public BingTranslatorSettingsPanel(String authenticationKey, String code) {
|
||||
initComponents();
|
||||
authenticationKeyField.setText(authenticationKey);
|
||||
authenticationKeyField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
firePropertyChange("SettingChanged", true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
firePropertyChange("SettingChanged", true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
firePropertyChange("SettingChanged", true, false);
|
||||
}
|
||||
|
||||
});
|
||||
populateComboBox();
|
||||
selectLanguageByCode(code);
|
||||
targetLanguageCode = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the target language combo box with available target languages
|
||||
*/
|
||||
@Messages({"BingTranslatorSettingsPanel.warning.targetLanguageFailure=Unable to get list of target languages or parse the result that was received"})
|
||||
private void populateComboBox() {
|
||||
Request get_request = new Request.Builder()
|
||||
.url(GET_TARGET_LANGUAGES_URL).build();
|
||||
try {
|
||||
Response response = new OkHttpClient().newCall(get_request).execute();
|
||||
JsonParser parser = new JsonParser();
|
||||
String responseBody = response.body().string();
|
||||
JsonElement elementBody = parser.parse(responseBody);
|
||||
JsonObject asObject = elementBody.getAsJsonObject();
|
||||
JsonElement translationElement = asObject.get("translation");
|
||||
JsonObject responses = translationElement.getAsJsonObject();
|
||||
responses.entrySet().forEach((entry) -> {
|
||||
targetLanguageComboBox.addItem(new LanguageWrapper(entry.getKey(), entry.getValue().getAsJsonObject().get("name").getAsString()));
|
||||
});
|
||||
targetLanguageComboBox.setEnabled(true);
|
||||
} catch (IOException | IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException ex) {
|
||||
logger.log(Level.SEVERE, Bundle.BingTranslatorSettingsPanel_warning_targetLanguageFailure(), ex);
|
||||
warningLabel.setText(Bundle.BingTranslatorSettingsPanel_warning_targetLanguageFailure());
|
||||
targetLanguageComboBox.setEnabled(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a language code select the corresponding language in the combo box
|
||||
* if it is present
|
||||
*
|
||||
* @param code language code such as "en" for English
|
||||
*/
|
||||
private void selectLanguageByCode(String code) {
|
||||
for (int i = 0; i < targetLanguageComboBox.getModel().getSize(); i++) {
|
||||
if (targetLanguageComboBox.getModel().getElementAt(i).getLanguageCode().equals(code)) {
|
||||
targetLanguageComboBox.setSelectedIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
authenticationKeyField = new javax.swing.JTextField();
|
||||
warningLabel = new javax.swing.JLabel();
|
||||
testButton = new javax.swing.JButton();
|
||||
targetLanguageLabel = new javax.swing.JLabel();
|
||||
targetLanguageComboBox = new javax.swing.JComboBox<>();
|
||||
testUntranslatedTextField = new javax.swing.JTextField();
|
||||
untranslatedLabel = new javax.swing.JLabel();
|
||||
resultLabel = new javax.swing.JLabel();
|
||||
testResultValueLabel = new javax.swing.JLabel();
|
||||
authenticationKeyLabel = new javax.swing.JLabel();
|
||||
|
||||
authenticationKeyField.setToolTipText(org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.authenticationKeyField.toolTipText")); // NOI18N
|
||||
|
||||
warningLabel.setForeground(new java.awt.Color(255, 0, 0));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(warningLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.warningLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(testButton, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.testButton.text")); // NOI18N
|
||||
testButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
testButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(targetLanguageLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.targetLanguageLabel.text")); // NOI18N
|
||||
|
||||
targetLanguageComboBox.setEnabled(false);
|
||||
targetLanguageComboBox.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
targetLanguageComboBoxSelected(evt);
|
||||
}
|
||||
});
|
||||
|
||||
testUntranslatedTextField.setText(DEFUALT_TEST_STRING);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(untranslatedLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.untranslatedLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(resultLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.resultLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(testResultValueLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.testResultValueLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(authenticationKeyLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.authenticationKeyLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
|
||||
.addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(targetLanguageLabel)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 192, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(authenticationKeyLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(authenticationKeyField, javax.swing.GroupLayout.PREFERRED_SIZE, 486, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 20, Short.MAX_VALUE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(testButton, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(25, 25, 25)
|
||||
.addComponent(untranslatedLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(resultLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
|
||||
.addContainerGap())))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(authenticationKeyField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(authenticationKeyLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(targetLanguageLabel)
|
||||
.addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(testButton)
|
||||
.addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(untranslatedLabel)
|
||||
.addComponent(resultLabel)
|
||||
.addComponent(testResultValueLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap())
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@Messages({"BingTranslatorSettingsPanel.warning.invalidKey=Invalid translation authentication key"})
|
||||
private void testButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_testButtonActionPerformed
|
||||
if (testTranslationSetup()) {
|
||||
warningLabel.setText("");
|
||||
} else {
|
||||
warningLabel.setText(Bundle.BingTranslatorSettingsPanel_warning_invalidKey());
|
||||
}
|
||||
}//GEN-LAST:event_testButtonActionPerformed
|
||||
|
||||
private void targetLanguageComboBoxSelected(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_targetLanguageComboBoxSelected
|
||||
String selectedCode = ((LanguageWrapper) targetLanguageComboBox.getSelectedItem()).getLanguageCode();
|
||||
if (!StringUtils.isBlank(selectedCode) && !selectedCode.equals(targetLanguageCode)) {
|
||||
targetLanguageCode = selectedCode;
|
||||
firePropertyChange("SettingChanged", true, false);
|
||||
}
|
||||
}//GEN-LAST:event_targetLanguageComboBoxSelected
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JTextField authenticationKeyField;
|
||||
private javax.swing.JLabel authenticationKeyLabel;
|
||||
private javax.swing.JLabel resultLabel;
|
||||
private javax.swing.JComboBox<LanguageWrapper> targetLanguageComboBox;
|
||||
private javax.swing.JLabel targetLanguageLabel;
|
||||
private javax.swing.JButton testButton;
|
||||
private javax.swing.JLabel testResultValueLabel;
|
||||
private javax.swing.JTextField testUntranslatedTextField;
|
||||
private javax.swing.JLabel untranslatedLabel;
|
||||
private javax.swing.JLabel warningLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
* Attempts to translate the text specified in the Untranslated field using
|
||||
* the settings currently specified but not necessarily saved
|
||||
*
|
||||
* @return true if the translation was able to be performed, false otherwise
|
||||
*
|
||||
*/
|
||||
private boolean testTranslationSetup() {
|
||||
testResultValueLabel.setText("");
|
||||
MediaType mediaType = MediaType.parse("application/json");
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("Text", testUntranslatedTextField.getText());
|
||||
jsonArray.add(jsonObject);
|
||||
String bodyString = jsonArray.toString();
|
||||
|
||||
RequestBody body = RequestBody.create(mediaType,
|
||||
bodyString);
|
||||
Request request = new Request.Builder()
|
||||
.url(BingTranslator.getTranlatorUrl(targetLanguageCode)).post(body)
|
||||
.addHeader("Ocp-Apim-Subscription-Key", authenticationKeyField.getText())
|
||||
.addHeader("Content-type", "application/json").build();
|
||||
try {
|
||||
Response response = new OkHttpClient().newCall(request).execute();
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonArray responses = parser.parse(response.body().string()).getAsJsonArray();
|
||||
//As far as I know, there's always exactly one item in the array.
|
||||
JsonObject response0 = responses.get(0).getAsJsonObject();
|
||||
JsonArray translations = response0.getAsJsonArray("translations");
|
||||
JsonObject translation0 = translations.get(0).getAsJsonObject();
|
||||
testResultValueLabel.setText(translation0.get("text").getAsString());
|
||||
return true;
|
||||
} catch (IOException | IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException e) {
|
||||
logger.log(Level.WARNING, "Test of Bing Translator failed due to exception", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently set authentication key to be used for the Microsoft
|
||||
* translation service
|
||||
*
|
||||
* @return the authentication key specified in the textarea
|
||||
*/
|
||||
String getAuthenticationKey() {
|
||||
return authenticationKeyField.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected target language code
|
||||
*
|
||||
* @return the target language code of the language selected in the combobox
|
||||
*/
|
||||
String getTargetLanguageCode() {
|
||||
return targetLanguageCode;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,15 @@
|
||||
GoogleTranslatorSettingsPanel.browseButton.text=Browse
|
||||
GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials:
|
||||
GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials Path:
|
||||
GoogleTranslatorSettingsPanel.warningLabel.text=
|
||||
GoogleTranslatorSettingsPanel.targetLanguageLabel.text=Target Language:
|
||||
BingTranslatorSettingsPanel.testButton.text=Test
|
||||
BingTranslatorSettingsPanel.testResultValueLabel.text=
|
||||
BingTranslatorSettingsPanel.resultLabel.text=Result:
|
||||
BingTranslatorSettingsPanel.untranslatedLabel.text=Untranslated:
|
||||
BingTranslatorSettingsPanel.targetLanguageLabel.text=Target Language:
|
||||
BingTranslatorSettingsPanel.authenticationKeyField.toolTipText=Enter the hash for the
|
||||
GoogleTranslatorSettingsPanel.testButton.text=Test
|
||||
GoogleTranslatorSettingsPanel.untranslatedLabel.text=Untranslated:
|
||||
GoogleTranslatorSettingsPanel.resultLabel.text=Result:
|
||||
GoogleTranslatorSettingsPanel.testResultValueLabel.text=
|
||||
BingTranslatorSettingsPanel.authenticationKeyLabel.text=Authentication Key:
|
||||
|
||||
+16
-1
@@ -1,8 +1,12 @@
|
||||
BingTranslator.name.text=Bing Translator
|
||||
BingTranslatorSettingsPanel.warning.invalidKey=Invalid translation authentication key
|
||||
BingTranslatorSettingsPanel.warning.targetLanguageFailure=Unable to get list of target languages or parse the result that was received
|
||||
GoogleTranslator.name.text=Google Translate
|
||||
GoogleTranslatorSettingsPanel.browseButton.text=Browse
|
||||
GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials:
|
||||
GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials Path:
|
||||
GoogleTranslatorSettingsPanel.errorMessage.fileNotFound=Credentials file not found, please set the location to be a valid JSON credentials file.
|
||||
GoogleTranslatorSettingsPanel.errorMessage.noFileSelected=A JSON file must be selected to provide your credentials for Google Translate.
|
||||
GoogleTranslatorSettingsPanel.errorMessage.translationFailure=Translation failure with specified credentials
|
||||
GoogleTranslatorSettingsPanel.errorMessage.unableToMakeCredentials=Unable to construct credentials object from credentials file, please set the location to be a valid JSON credentials file.
|
||||
GoogleTranslatorSettingsPanel.errorMessage.unableToReadCredentials=Unable to read credentials from credentials file, please set the location to be a valid JSON credentials file.
|
||||
GoogleTranslatorSettingsPanel.errorMessage.unknownFailureGetting=Failure getting list of supported languages with current credentials file.
|
||||
@@ -11,3 +15,14 @@ GoogleTranslatorSettingsPanel.fileChooser.confirmButton=Select
|
||||
GoogleTranslatorSettingsPanel.json.description=JSON Files
|
||||
GoogleTranslatorSettingsPanel.warningLabel.text=
|
||||
GoogleTranslatorSettingsPanel.targetLanguageLabel.text=Target Language:
|
||||
BingTranslatorSettingsPanel.testButton.text=Test
|
||||
BingTranslatorSettingsPanel.testResultValueLabel.text=
|
||||
BingTranslatorSettingsPanel.resultLabel.text=Result:
|
||||
BingTranslatorSettingsPanel.untranslatedLabel.text=Untranslated:
|
||||
BingTranslatorSettingsPanel.targetLanguageLabel.text=Target Language:
|
||||
BingTranslatorSettingsPanel.authenticationKeyField.toolTipText=Enter the hash for the
|
||||
GoogleTranslatorSettingsPanel.testButton.text=Test
|
||||
GoogleTranslatorSettingsPanel.untranslatedLabel.text=Untranslated:
|
||||
GoogleTranslatorSettingsPanel.resultLabel.text=Result:
|
||||
GoogleTranslatorSettingsPanel.testResultValueLabel.text=
|
||||
BingTranslatorSettingsPanel.authenticationKeyLabel.text=Authentication Key:
|
||||
|
||||
@@ -28,10 +28,14 @@ import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
|
||||
import org.sleuthkit.autopsy.texttranslation.TextTranslator;
|
||||
import org.sleuthkit.autopsy.texttranslation.TranslationException;
|
||||
|
||||
@@ -43,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;
|
||||
@@ -57,26 +62,49 @@ public final class GoogleTranslator implements TextTranslator {
|
||||
loadTranslator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if google is able to be reached
|
||||
*
|
||||
* @return true if it can be, false otherwise
|
||||
*/
|
||||
private static boolean googleIsReachable() {
|
||||
String host = "www.google.com";
|
||||
InetAddress address;
|
||||
try {
|
||||
address = InetAddress.getByName(host);
|
||||
return address.isReachable(1500);
|
||||
} catch (UnknownHostException ex) {
|
||||
logger.log(Level.WARNING, "Unable to reach google.com due to unknown host", ex);
|
||||
return false;
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Unable to reach google.com due IOException", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String translate(String string) throws TranslationException {
|
||||
if (!googleIsReachable()) {
|
||||
throw new TranslationException("Failure translating using GoogleTranslator: Cannot connect to Google");
|
||||
}
|
||||
|
||||
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
|
||||
// asuming the input is HTML. We need to replace newlines with <br> for Google to preserve them
|
||||
// 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);
|
||||
@@ -84,8 +112,12 @@ public final class GoogleTranslator implements TextTranslator {
|
||||
|
||||
// put back the newlines
|
||||
translatedString = translatedString.replaceAll("<br />", "\n");
|
||||
|
||||
// With our current settings, Google Translate outputs HTML
|
||||
// so we need to undo the escape characters.
|
||||
translatedString = EscapeUtil.unEscapeHtml(translatedString);
|
||||
return translatedString;
|
||||
} catch (Throwable ex) {
|
||||
} catch (Throwable ex) {
|
||||
//Catching throwables because some of this Google Translate code throws throwables
|
||||
throw new TranslationException("Failure translating using GoogleTranslator", ex);
|
||||
}
|
||||
@@ -106,16 +138,20 @@ public final class GoogleTranslator implements TextTranslator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the Google Cloud Translation service give the currently saved
|
||||
* Load the Google Cloud Translation service given the currently saved
|
||||
* settings
|
||||
*/
|
||||
private void loadTranslator() {
|
||||
InputStream credentialStream = null;
|
||||
Credentials creds = null;
|
||||
try {
|
||||
credentialStream = new FileInputStream(settings.getCredentialPath());
|
||||
} catch (FileNotFoundException ex) {
|
||||
logger.log(Level.WARNING, "JSON file for GoogleTranslator credentials not found", ex);
|
||||
if (StringUtils.isBlank(settings.getCredentialPath())) {
|
||||
logger.log(Level.INFO, "No credentials file has been provided for Google Translator");
|
||||
} else {
|
||||
try {
|
||||
credentialStream = new FileInputStream(settings.getCredentialPath());
|
||||
} catch (FileNotFoundException ex) {
|
||||
logger.log(Level.WARNING, "JSON file for GoogleTranslator credentials not found", ex);
|
||||
}
|
||||
}
|
||||
if (credentialStream != null) {
|
||||
try {
|
||||
@@ -142,4 +178,9 @@ public final class GoogleTranslator implements TextTranslator {
|
||||
settings.saveSettings();
|
||||
loadTranslator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPayloadSize() {
|
||||
return MAX_PAYLOAD_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -92,12 +92,14 @@ public final class GoogleTranslatorSettings {
|
||||
}
|
||||
if (ModuleSettings.settingExists(GOOGLE_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY)) {
|
||||
targetLanguageCode = ModuleSettings.getConfigSetting(GOOGLE_TRANSLATE_NAME, TARGET_LANGUAGE_CODE_KEY);
|
||||
} else {
|
||||
}
|
||||
if (targetLanguageCode == null || StringUtils.isBlank(targetLanguageCode)) {
|
||||
targetLanguageCode = DEFAULT_TARGET_LANGUAGE;
|
||||
}
|
||||
if (ModuleSettings.settingExists(GOOGLE_TRANSLATE_NAME, CREDENTIAL_PATH_KEY)) {
|
||||
credentialPath = ModuleSettings.getConfigSetting(GOOGLE_TRANSLATE_NAME, CREDENTIAL_PATH_KEY);
|
||||
} else {
|
||||
}
|
||||
if (credentialPath == null) {
|
||||
credentialPath = DEFAULT_CREDENTIAL_PATH;
|
||||
}
|
||||
}
|
||||
|
||||
+66
-9
@@ -16,13 +16,9 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="warningLabel" min="-2" pref="551" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
||||
<Component id="credentialsLabel" max="32767" attributes="0"/>
|
||||
@@ -31,7 +27,7 @@
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="credentialsPathField" pref="443" max="32767" attributes="0"/>
|
||||
<Component id="credentialsPathField" pref="451" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="browseButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
|
||||
@@ -42,6 +38,18 @@
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<Component id="warningLabel" min="-2" pref="551" max="-2" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="testButton" min="-2" pref="83" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="untranslatedLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="testUntranslatedTextField" min="-2" pref="140" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="resultLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="testResultValueLabel" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
@@ -60,9 +68,16 @@
|
||||
<Component id="targetLanguageLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="targetLanguageComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<EmptySpace pref="15" max="32767" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="testButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="testUntranslatedTextField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="untranslatedLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="resultLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="testResultValueLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="warningLabel" min="-2" pref="18" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="23" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@@ -98,7 +113,7 @@
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<org.sleuthkit.autopsy.texttranslation.translators.GoogleLanguageWrapper>"/>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<org.sleuthkit.autopsy.texttranslation.translators.LanguageWrapper>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="targetLanguageLabel">
|
||||
@@ -118,5 +133,47 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="testResultValueLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties" key="GoogleTranslatorSettingsPanel.testResultValueLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="resultLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties" key="GoogleTranslatorSettingsPanel.resultLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="untranslatedLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties" key="GoogleTranslatorSettingsPanel.untranslatedLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="testUntranslatedTextField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="DEFUALT_TEST_STRING" type="code"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="testButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties" key="GoogleTranslatorSettingsPanel.testButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="testButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
||||
+109
-26
@@ -21,7 +21,9 @@ package org.sleuthkit.autopsy.texttranslation.translators;
|
||||
import com.google.auth.Credentials;
|
||||
import com.google.auth.oauth2.ServiceAccountCredentials;
|
||||
import com.google.cloud.translate.Language;
|
||||
import com.google.cloud.translate.Translate;
|
||||
import com.google.cloud.translate.TranslateOptions;
|
||||
import com.google.cloud.translate.Translation;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -44,6 +46,7 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(GoogleTranslatorSettingsPanel.class.getName());
|
||||
private static final String JSON_EXTENSION = "json";
|
||||
private static final String DEFUALT_TEST_STRING = "traducción exitoso"; //spanish which should translate to something along the lines of "successful translation"
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final ItemListener listener = new ComboBoxSelectionListener();
|
||||
private String targetLanguageCode = "";
|
||||
@@ -60,16 +63,15 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
|
||||
/**
|
||||
* Private method to make a temporary translation service given the current
|
||||
* settings and use it to retrieve the available target languages for
|
||||
* population of combobox with target language with unsaved settings.
|
||||
* settings with unsaved settings.
|
||||
*
|
||||
* @return A list of Languages
|
||||
* @return A Translate object which is the translation service
|
||||
*/
|
||||
@Messages({"GoogleTranslatorSettingsPanel.errorMessage.fileNotFound=Credentials file not found, please set the location to be a valid JSON credentials file.",
|
||||
"GoogleTranslatorSettingsPanel.errorMessage.unableToReadCredentials=Unable to read credentials from credentials file, please set the location to be a valid JSON credentials file.",
|
||||
"GoogleTranslatorSettingsPanel.errorMessage.unableToMakeCredentials=Unable to construct credentials object from credentials file, please set the location to be a valid JSON credentials file.",
|
||||
"GoogleTranslatorSettingsPanel.errorMessage.unknownFailureGetting=Failure getting list of supported languages with current credentials file.",})
|
||||
private List<Language> getListOfTargetLanguages() {
|
||||
private Translate getTemporaryTranslationService() {
|
||||
//This method also has the side effect of more or less validating the JSON file which was selected as it is necessary to get the list of target languages
|
||||
try {
|
||||
InputStream credentialStream;
|
||||
@@ -77,31 +79,31 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
credentialStream = new FileInputStream(credentialsPathField.getText());
|
||||
} catch (FileNotFoundException ignored) {
|
||||
warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_fileNotFound());
|
||||
return new ArrayList<>();
|
||||
return null;
|
||||
}
|
||||
Credentials creds;
|
||||
try {
|
||||
creds = ServiceAccountCredentials.fromStream(credentialStream);
|
||||
} catch (IOException ignored) {
|
||||
warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_unableToMakeCredentials());
|
||||
return new ArrayList<>();
|
||||
return null;
|
||||
}
|
||||
if (creds == null) {
|
||||
warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_unableToReadCredentials());
|
||||
logger.log(Level.WARNING, "Credentials were not successfully made, no translations will be available from the GoogleTranslator");
|
||||
return new ArrayList<>();
|
||||
return null;
|
||||
} else {
|
||||
TranslateOptions.Builder builder = TranslateOptions.newBuilder();
|
||||
builder.setCredentials(creds);
|
||||
builder.setTargetLanguage(targetLanguageCode); //localize the list to the currently selected target language
|
||||
warningLabel.setText(""); //clear any previous warning text
|
||||
return builder.build().getService().listSupportedLanguages();
|
||||
return builder.build().getService();
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
//Catching throwables because some of this Google Translate code throws throwables
|
||||
warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_unknownFailureGetting());
|
||||
logger.log(Level.WARNING, "Throwable caught while getting list of supported languages", throwable);
|
||||
return new ArrayList<>();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,29 +116,51 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
targetLanguageComboBox.removeItemListener(listener);
|
||||
try {
|
||||
if (!StringUtils.isBlank(credentialsPathField.getText())) {
|
||||
List<Language> listSupportedLanguages = getListOfTargetLanguages();
|
||||
List<Language> listSupportedLanguages;
|
||||
Translate tempService = getTemporaryTranslationService();
|
||||
if (tempService != null) {
|
||||
listSupportedLanguages = tempService.listSupportedLanguages();
|
||||
} else {
|
||||
listSupportedLanguages = new ArrayList<>();
|
||||
}
|
||||
targetLanguageComboBox.removeAllItems();
|
||||
if (!listSupportedLanguages.isEmpty()) {
|
||||
listSupportedLanguages.forEach((lang) -> {
|
||||
targetLanguageComboBox.addItem(new GoogleLanguageWrapper(lang));
|
||||
targetLanguageComboBox.addItem(new LanguageWrapper(lang));
|
||||
});
|
||||
selectLanguageByCode(targetLanguageCode);
|
||||
targetLanguageComboBox.addItemListener(listener);
|
||||
targetLanguageComboBox.setEnabled(true);
|
||||
enableControls(true);
|
||||
|
||||
} else {
|
||||
targetLanguageComboBox.setEnabled(false);
|
||||
enableControls(false);
|
||||
}
|
||||
} else {
|
||||
warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_noFileSelected());
|
||||
targetLanguageComboBox.setEnabled(false);
|
||||
enableControls(false);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_unknownFailurePopulating());
|
||||
logger.log(Level.WARNING, "Throwable caught while populating list of supported languages", throwable);
|
||||
targetLanguageComboBox.setEnabled(false);
|
||||
enableControls(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to enable/disable all controls which are dependent on valid
|
||||
* credentials having been provided
|
||||
*
|
||||
* @param enabled true to enable the controls, false to disable them
|
||||
*/
|
||||
private void enableControls(boolean enabled) {
|
||||
targetLanguageComboBox.setEnabled(enabled);
|
||||
testButton.setEnabled(enabled);
|
||||
testResultValueLabel.setEnabled(enabled);
|
||||
testUntranslatedTextField.setEnabled(enabled);
|
||||
untranslatedLabel.setEnabled(enabled);
|
||||
resultLabel.setEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a language code select the corresponding language in the combo box
|
||||
* if it is present
|
||||
@@ -145,7 +169,7 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
*/
|
||||
private void selectLanguageByCode(String code) {
|
||||
for (int i = 0; i < targetLanguageComboBox.getModel().getSize(); i++) {
|
||||
if (targetLanguageComboBox.getItemAt(i).getLanguage().getCode().equals(code)) {
|
||||
if (targetLanguageComboBox.getItemAt(i).getLanguageCode().equals(code)) {
|
||||
targetLanguageComboBox.setSelectedIndex(i);
|
||||
return;
|
||||
}
|
||||
@@ -167,6 +191,11 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
targetLanguageComboBox = new javax.swing.JComboBox<>();
|
||||
targetLanguageLabel = new javax.swing.JLabel();
|
||||
warningLabel = new javax.swing.JLabel();
|
||||
testResultValueLabel = new javax.swing.JLabel();
|
||||
resultLabel = new javax.swing.JLabel();
|
||||
untranslatedLabel = new javax.swing.JLabel();
|
||||
testUntranslatedTextField = new javax.swing.JTextField();
|
||||
testButton = new javax.swing.JButton();
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(credentialsLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.credentialsLabel.text")); // NOI18N
|
||||
|
||||
@@ -186,6 +215,25 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
warningLabel.setForeground(new java.awt.Color(255, 0, 0));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(warningLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.warningLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(testResultValueLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.testResultValueLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(resultLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.resultLabel.text")); // NOI18N
|
||||
resultLabel.setEnabled(false);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(untranslatedLabel, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.untranslatedLabel.text")); // NOI18N
|
||||
untranslatedLabel.setEnabled(false);
|
||||
|
||||
testUntranslatedTextField.setText(DEFUALT_TEST_STRING);
|
||||
testUntranslatedTextField.setEnabled(false);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(testButton, org.openide.util.NbBundle.getMessage(GoogleTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.testButton.text")); // NOI18N
|
||||
testButton.setEnabled(false);
|
||||
testButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
testButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
@@ -193,9 +241,6 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
|
||||
.addComponent(credentialsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
@@ -203,13 +248,24 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(credentialsPathField, javax.swing.GroupLayout.DEFAULT_SIZE, 443, Short.MAX_VALUE)
|
||||
.addComponent(credentialsPathField, javax.swing.GroupLayout.DEFAULT_SIZE, 451, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(browseButton)
|
||||
.addGap(14, 14, 14))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 317, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 0, Short.MAX_VALUE))))))
|
||||
.addGap(0, 0, Short.MAX_VALUE))))
|
||||
.addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(testButton, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(untranslatedLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(resultLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(testResultValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
@@ -223,9 +279,15 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(targetLanguageLabel)
|
||||
.addComponent(targetLanguageComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(23, Short.MAX_VALUE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 15, Short.MAX_VALUE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(testButton)
|
||||
.addComponent(testUntranslatedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(untranslatedLabel)
|
||||
.addComponent(resultLabel)
|
||||
.addComponent(testResultValueLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@@ -249,12 +311,33 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
}
|
||||
}//GEN-LAST:event_browseButtonActionPerformed
|
||||
|
||||
@Messages({"GoogleTranslatorSettingsPanel.errorMessage.translationFailure=Translation failure with specified credentials"})
|
||||
private void testButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_testButtonActionPerformed
|
||||
testResultValueLabel.setText("");
|
||||
Translate tempTranslate = getTemporaryTranslationService();
|
||||
if (tempTranslate != null) {
|
||||
try {
|
||||
Translation translation = tempTranslate.translate(testUntranslatedTextField.getText());
|
||||
testResultValueLabel.setText(translation.getTranslatedText());
|
||||
warningLabel.setText("");
|
||||
} catch (Exception ex) {
|
||||
warningLabel.setText(Bundle.GoogleTranslatorSettingsPanel_errorMessage_translationFailure());
|
||||
logger.log(Level.WARNING, Bundle.GoogleTranslatorSettingsPanel_errorMessage_translationFailure(), ex);
|
||||
}
|
||||
}
|
||||
}//GEN-LAST:event_testButtonActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton browseButton;
|
||||
private javax.swing.JLabel credentialsLabel;
|
||||
private javax.swing.JTextField credentialsPathField;
|
||||
private javax.swing.JComboBox<org.sleuthkit.autopsy.texttranslation.translators.GoogleLanguageWrapper> targetLanguageComboBox;
|
||||
private javax.swing.JLabel resultLabel;
|
||||
private javax.swing.JComboBox<org.sleuthkit.autopsy.texttranslation.translators.LanguageWrapper> targetLanguageComboBox;
|
||||
private javax.swing.JLabel targetLanguageLabel;
|
||||
private javax.swing.JButton testButton;
|
||||
private javax.swing.JLabel testResultValueLabel;
|
||||
private javax.swing.JTextField testUntranslatedTextField;
|
||||
private javax.swing.JLabel untranslatedLabel;
|
||||
private javax.swing.JLabel warningLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
@@ -284,7 +367,7 @@ public class GoogleTranslatorSettingsPanel extends javax.swing.JPanel {
|
||||
|
||||
@Override
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
String selectedCode = ((GoogleLanguageWrapper) targetLanguageComboBox.getSelectedItem()).getLanguage().getCode();
|
||||
String selectedCode = ((LanguageWrapper) targetLanguageComboBox.getSelectedItem()).getLanguageCode();
|
||||
if (!StringUtils.isBlank(selectedCode) && !selectedCode.equals(targetLanguageCode)) {
|
||||
targetLanguageCode = selectedCode;
|
||||
populateTargetLanguageComboBox();
|
||||
|
||||
+24
-10
@@ -21,19 +21,33 @@ package org.sleuthkit.autopsy.texttranslation.translators;
|
||||
import com.google.cloud.translate.Language;
|
||||
|
||||
/**
|
||||
* Wrapper for the Language class
|
||||
* Wrapper for Language definitions used by translators
|
||||
*/
|
||||
class GoogleLanguageWrapper {
|
||||
class LanguageWrapper {
|
||||
|
||||
private final Language language;
|
||||
private final String languageCode;
|
||||
private final String languageDisplayName;
|
||||
|
||||
/**
|
||||
* Create a new GoogleLanguageWrapper
|
||||
* Create a new LanguageWrapper to wrap the google language object
|
||||
*
|
||||
* @param lang the Language object to wrap
|
||||
*/
|
||||
GoogleLanguageWrapper(Language lang) {
|
||||
language = lang;
|
||||
LanguageWrapper(Language language) {
|
||||
languageCode = language.getCode();
|
||||
languageDisplayName = language.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new LanguageWrapper to wrap json elements that identify a
|
||||
* language for microsofts translation service
|
||||
*
|
||||
* @param code the code which uniquely identifies a language
|
||||
* @param name the name of the language
|
||||
*/
|
||||
LanguageWrapper(String code, String name) {
|
||||
languageCode = code;
|
||||
languageDisplayName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,14 +55,14 @@ class GoogleLanguageWrapper {
|
||||
*
|
||||
* @return the wrapped Language
|
||||
*/
|
||||
Language getLanguage() {
|
||||
return language;
|
||||
String getLanguageCode() {
|
||||
return languageCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
//toString overridden so that the jComboBox in the GoogleTranslatorSettingsPanel will display the name of the language
|
||||
return language.getName();
|
||||
//toString overridden so that the jComboBox in the TranslatorSettingsPanels will display the name of the language
|
||||
return languageDisplayName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
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
|
||||
TranslationOptionsPanelController.moduleErr.msg=A module caused an error listening to TranslationSettingsPanelController updates. See log to determine which module. Some data could be incomplete.
|
||||
TranslationContentPanel.warningLabel.text=
|
||||
|
||||
@@ -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.
|
||||
@@ -25,3 +25,4 @@ TranslationOptionsPanel.translationDisabled.text=Translation disabled
|
||||
TranslationOptionsPanel.translationServiceLabel.text=Text translator:
|
||||
TranslationOptionsPanelController.moduleErr=Module Error
|
||||
TranslationOptionsPanelController.moduleErr.msg=A module caused an error listening to TranslationSettingsPanelController updates. See log to determine which module. Some data could be incomplete.
|
||||
TranslationContentPanel.warningLabel.text=
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,13 +160,13 @@
|
||||
<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, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties" key="TranslationContentPanel.warningLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
||||
@@ -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,8 @@ 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
|
||||
org.openide.awt.Mnemonics.setLocalizedText(warningLabel, org.openide.util.NbBundle.getMessage(TranslationContentPanel.class, "TranslationContentPanel.warningLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
@@ -311,12 +315,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 +335,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 +370,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 +392,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
|
||||
}
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.texttranslation.translators;
|
||||
|
||||
//import java.io.IOException;
|
||||
//import org.junit.After;
|
||||
//import org.junit.AfterClass;
|
||||
//import org.junit.Before;
|
||||
//import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
//import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests for the BingTranslator translation service, these tests have been
|
||||
* commented out because they require credentials to perform
|
||||
*/
|
||||
public class BingTranslatorTest {
|
||||
|
||||
@Test
|
||||
public void testTranslate() {
|
||||
// BingTranslator translator = new BingTranslator();
|
||||
// String input = "gato";
|
||||
// String expectedTranslation = "cat";
|
||||
// runTest(translator, input, expectedTranslation);
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testQuickStartSentence() throws Exception {
|
||||
// BingTranslator translator = new BingTranslator();
|
||||
// String input = "Willkommen bei Microsoft Translator. Raten Sie mal, wie viele Sprachen ich spreche.";
|
||||
// String expectedTranslation = "Welcome to Microsoft Translator. Guess how many languages I speak.";
|
||||
// runTest(translator, input, expectedTranslation);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testCharacterEscapes() throws Exception {
|
||||
// BingTranslator translator = new BingTranslator();
|
||||
// String input = "\"gato\"";;
|
||||
// String expectedTranslation = "Cat";
|
||||
// runTest(translator, input, expectedTranslation);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testLineBreaks() throws Exception {
|
||||
// BingTranslator translator = new BingTranslator();
|
||||
// String input = "gato\nperro";;
|
||||
// String expectedTranslation = "cat\nDog";
|
||||
// runTest(translator, input, expectedTranslation);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Test whether translator throws an error. This should not be part of our
|
||||
// * regular testing, because we are limited to only 2MB of free translations
|
||||
// * ever.
|
||||
// * @param translator A BingTranslator
|
||||
// * @param input Text to translate
|
||||
// * @param expectedTranslation Not used unless you uncomment those lines.
|
||||
// */
|
||||
// public void runTest(BingTranslator translator, String input, String expectedTranslation) {
|
||||
// String translation;
|
||||
// try {
|
||||
// translation = translator.translate(input);
|
||||
// }
|
||||
// catch (Throwable e) {
|
||||
// fail("Bing translation produced an exception: " + e.getMessage());
|
||||
// return;
|
||||
// };
|
||||
//
|
||||
// /*
|
||||
// //It's unrealistic to expect the same answer every time, but sometimes
|
||||
// //it's helpful to have this in your debug process.
|
||||
// System.out.println(translation);
|
||||
// assertEquals(expectedTranslation, translation);
|
||||
// */
|
||||
// }
|
||||
}
|
||||
Reference in New Issue
Block a user