Merge remote-tracking branch 'upstream/develop' into 5130_exportCSV

This commit is contained in:
Ann Priestman 2019-06-06 14:00:36 -04:00
commit 29bdc2ee52
24 changed files with 1370 additions and 83 deletions

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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();
}

View File

@ -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(),

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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(

View File

@ -0,0 +1,175 @@
/*
* 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.
// HTML files were producing lots of white space at the end
//Google Translate required us to replace (\r\n|\n) with <br />
//but Bing Translator doesn not have that requirement.
//The free account has a maximum file size. If you have a paid account,
//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);
}
}
}

View File

@ -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);
}
}

View File

@ -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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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="&lt;LanguageWrapper&gt;"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -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;
}
}

View File

@ -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:

View File

@ -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:

View File

@ -32,8 +32,10 @@ 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;
@ -58,26 +60,33 @@ public final class GoogleTranslator implements TextTranslator {
settingsPanel = new GoogleTranslatorSettingsPanel(settings.getCredentialPath(), settings.getTargetLanguageCode());
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) {
} 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.
@ -88,7 +97,7 @@ public final class GoogleTranslator implements TextTranslator {
// 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,
@ -100,11 +109,15 @@ public final class GoogleTranslator implements TextTranslator {
Translation translation
= googleTranslate.translate(substring);
String translatedString = translation.getTranslatedText();
// 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);
}
@ -112,7 +125,7 @@ public final class GoogleTranslator implements TextTranslator {
throw new TranslationException("Google Translator has not been configured, credentials need to be specified");
}
}
@Messages({"GoogleTranslator.name.text=Google Translate"})
@Override
public String getName() {
@ -125,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 {

View File

@ -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;
}
}

View File

@ -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="&lt;org.sleuthkit.autopsy.texttranslation.translators.GoogleLanguageWrapper&gt;"/>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.texttranslation.translators.LanguageWrapper&gt;"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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>

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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);
// */
// }
}