mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 18:17:43 +00:00
Merge remote-tracking branch 'upstream/TL-list-view' into 788-lazy-contet-menus
This commit is contained in:
commit
838fccfb8c
@ -57,4 +57,26 @@ public class TextUtil {
|
||||
|
||||
return orientation;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method determines if a passed-in Java char (16 bits) is a valid
|
||||
* UTF-8 printable character, returning true if so, false if not.
|
||||
*
|
||||
* Note that this method can have ramifications for characters outside the
|
||||
* Unicode Base Multilingual Plane (BMP), which require more than 16 bits.
|
||||
* We are using Java characters (16 bits) to look at the data and this will
|
||||
* not accurately identify any non-BMP character (larger than 16 bits)
|
||||
* ending with 0xFFFF and 0xFFFE. In the interest of a fast solution, we
|
||||
* have chosen to ignore the extended planes above Unicode BMP for the time
|
||||
* being. The net result of this is some non-BMP characters may be
|
||||
* interspersed with '^' characters in Autopsy.
|
||||
*
|
||||
* @param ch the character to test
|
||||
*
|
||||
* @return Returns true if the character is valid UTF-8, false if not.
|
||||
*/
|
||||
public static boolean isValidSolrUTF8(char ch) {
|
||||
return ((ch <= 0xFDD0 || ch >= 0xFDEF) && (ch > 0x1F || ch == 0x9 || ch == 0xA || ch == 0xD) && (ch != 0xFFFF) && (ch != 0xFFFE));
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import java.awt.Dimension;
|
||||
import java.awt.Toolkit;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
@ -30,6 +31,7 @@ import org.apache.commons.io.FilenameUtils;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
|
||||
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb.KnownFilesType;
|
||||
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDbManagerException;
|
||||
@ -273,12 +275,17 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
|
||||
|
||||
private void saveAsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveAsButtonActionPerformed
|
||||
try {
|
||||
String lastBaseDirectory = "";
|
||||
String lastBaseDirectory = Paths.get(PlatformUtil.getUserConfigDirectory(), "HashDatabases").toString();
|
||||
if (ModuleSettings.settingExists(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY)) {
|
||||
lastBaseDirectory = ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY);
|
||||
}
|
||||
StringBuilder path = new StringBuilder();
|
||||
path.append(lastBaseDirectory);
|
||||
File hashDbFolder = new File(path.toString());
|
||||
// create the folder if it doesn't exist
|
||||
if (!hashDbFolder.exists()){
|
||||
hashDbFolder.mkdir();
|
||||
}
|
||||
if (!hashSetNameTextField.getText().isEmpty()) {
|
||||
path.append(File.separator).append(hashSetNameTextField.getText());
|
||||
} else {
|
||||
|
@ -179,7 +179,7 @@ ReportHTML.addThumbRows.dataType.msg=Tagged Results and Contents that contain im
|
||||
ReportHTML.thumbLink.tags=Tags\:
|
||||
ReportHTML.getName.text=Results - HTML
|
||||
ReportHTML.getDesc.text=A report about results and tagged items in HTML format.
|
||||
ReportHTML.writeIndex.title=Autopsy Report for case {0}
|
||||
ReportHTML.writeIndex.title=for case {0}
|
||||
ReportHTML.writeIndex.noFrames.msg=Your browser is not compatible with our frame setup.
|
||||
ReportHTML.writeIndex.noFrames.seeNav=Please see <a href\="nav.html">the navigation page</a> for artifact links,
|
||||
ReportHTML.writeIndex.seeSum=and <a href\="summary.html">the summary page</a> for a case summary.
|
||||
|
@ -838,7 +838,8 @@ class ReportHTML implements TableReportModule {
|
||||
try {
|
||||
indexOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(indexFilePath), "UTF-8")); //NON-NLS
|
||||
StringBuilder index = new StringBuilder();
|
||||
index.append("<head>\n<title>").append( //NON-NLS
|
||||
final String reportTitle = reportBranding.getReportTitle();
|
||||
index.append("<head>\n<title>").append(reportTitle).append(" ").append(
|
||||
NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.title", currentCase.getName())).append(
|
||||
"</title>\n"); //NON-NLS
|
||||
index.append("<link rel=\"icon\" type=\"image/ico\" href=\"favicon.ico\" />\n"); //NON-NLS
|
||||
|
@ -23,9 +23,10 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.StringBinding;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ObservableList;
|
||||
@ -57,7 +58,7 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* The inner component that makes up the Lsit view. Manages the table.
|
||||
* The inner component that makes up the List view. Manages the TableView.
|
||||
*/
|
||||
class ListTimeline extends BorderPane {
|
||||
|
||||
@ -100,6 +101,9 @@ class ListTimeline extends BorderPane {
|
||||
}
|
||||
|
||||
@FXML
|
||||
@NbBundle.Messages({
|
||||
"# {0} - the number of events",
|
||||
"ListTimeline.evetnCountLabel.text={0} events"})
|
||||
void initialize() {
|
||||
assert eventCountLabel != null : "fx:id=\"eventCountLabel\" was not injected: check your FXML file 'ListViewPane.fxml'.";
|
||||
assert table != null : "fx:id=\"table\" was not injected: check your FXML file 'ListViewPane.fxml'.";
|
||||
@ -114,31 +118,44 @@ class ListTimeline extends BorderPane {
|
||||
//override default row with one that provides context menu.S
|
||||
table.setRowFactory(tableView -> new EventRow());
|
||||
|
||||
//remove idColumn (can be used for debugging).
|
||||
//remove idColumn (can be restored for debugging).
|
||||
table.getColumns().remove(idColumn);
|
||||
|
||||
// set up cell and cell-value factories for columns
|
||||
//
|
||||
///// set up cell and cell-value factories for columns
|
||||
millisColumn.setCellValueFactory(CELL_VALUE_FACTORY);
|
||||
millisColumn.setCellFactory(col -> new EpochMillisCell());
|
||||
millisColumn.setCellFactory(col -> new TextEventTableCell(singleEvent ->
|
||||
TimeLineController.getZonedFormatter().print(singleEvent.getStartMillis())));
|
||||
|
||||
iconColumn.setCellValueFactory(CELL_VALUE_FACTORY);
|
||||
iconColumn.setCellFactory(col -> new ImageCell());
|
||||
|
||||
descriptionColumn.setCellValueFactory(CELL_VALUE_FACTORY);
|
||||
descriptionColumn.setCellFactory(col -> new DescriptionCell());
|
||||
descriptionColumn.setCellFactory(col -> new TextEventTableCell(singleEvent ->
|
||||
singleEvent.getDescription(DescriptionLoD.FULL)));
|
||||
|
||||
baseTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY);
|
||||
baseTypeColumn.setCellFactory(col -> new BaseTypeCell());
|
||||
baseTypeColumn.setCellFactory(col -> new TextEventTableCell(singleEvent ->
|
||||
singleEvent.getEventType().getBaseType().getDisplayName()));
|
||||
|
||||
subTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY);
|
||||
subTypeColumn.setCellFactory(col -> new EventTypeCell());
|
||||
subTypeColumn.setCellFactory(col -> new TextEventTableCell(singleEvent ->
|
||||
singleEvent.getEventType().getDisplayName()));
|
||||
|
||||
knownColumn.setCellValueFactory(CELL_VALUE_FACTORY);
|
||||
knownColumn.setCellFactory(col -> new KnownCell());
|
||||
knownColumn.setCellFactory(col -> new TextEventTableCell(singleEvent ->
|
||||
singleEvent.getKnown().getName()));
|
||||
|
||||
//bind event count lable no number of items in table
|
||||
eventCountLabel.textProperty().bind(Bindings.size(table.getItems()).asString().concat(" events"));
|
||||
//bind event count label to number of items in the table
|
||||
eventCountLabel.textProperty().bind(new StringBinding() {
|
||||
{
|
||||
bind(table.getItems());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String computeValue() {
|
||||
return Bundle.ListTimeline_evetnCountLabel_text(table.getItems().size());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,6 +166,15 @@ class ListTimeline extends BorderPane {
|
||||
table.getItems().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected event ID.
|
||||
*
|
||||
* @return The selected event ID.
|
||||
*/
|
||||
Long getSelectedEventID() {
|
||||
return table.getSelectionModel().getSelectedItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Collection of events (by ID) to show in the table.
|
||||
*
|
||||
@ -169,6 +195,18 @@ class ListTimeline extends BorderPane {
|
||||
return table.getSelectionModel().getSelectedItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ID of the event that is selected.
|
||||
*
|
||||
* @param selectedEventID The ID of the event that should be selected.
|
||||
*/
|
||||
void selectEventID(Long selectedEventID) {
|
||||
//restore selection.
|
||||
table.scrollTo(selectedEventID);
|
||||
table.getSelectionModel().select(selectedEventID);
|
||||
table.requestFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* TableCell to show the icon for the type of an event.
|
||||
*/
|
||||
@ -187,86 +225,23 @@ class ListTimeline extends BorderPane {
|
||||
}
|
||||
|
||||
/**
|
||||
* TableCell to show the full description for an event.
|
||||
* TableCell to show text derived from a SingleEvent by the given Funtion.
|
||||
*/
|
||||
private class DescriptionCell extends EventTableCell {
|
||||
private class TextEventTableCell extends EventTableCell {
|
||||
|
||||
private final Function<SingleEvent, String> textSupplier;
|
||||
|
||||
TextEventTableCell(Function<SingleEvent, String> textSupplier) {
|
||||
this.textSupplier = textSupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateItem(Long item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (empty || item == null) {
|
||||
setText("");
|
||||
setText(null);
|
||||
} else {
|
||||
setText(getEvent().getDescription(DescriptionLoD.FULL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TableCell to show the base type of an event.
|
||||
*/
|
||||
private class BaseTypeCell extends EventTableCell {
|
||||
|
||||
@Override
|
||||
protected void updateItem(Long item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (empty || item == null) {
|
||||
setText("");
|
||||
} else {
|
||||
setText(getEvent().getEventType().getBaseType().getDisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TableCell to show the sub type of an event.
|
||||
*/
|
||||
private class EventTypeCell extends EventTableCell {
|
||||
|
||||
@Override
|
||||
protected void updateItem(Long item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (empty || item == null) {
|
||||
setText("");
|
||||
} else {
|
||||
setText(getEvent().getEventType().getDisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TableCell to show the known state of the file backing an event.
|
||||
*/
|
||||
private class KnownCell extends EventTableCell {
|
||||
|
||||
@Override
|
||||
protected void updateItem(Long item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (empty || item == null) {
|
||||
setText("");
|
||||
} else {
|
||||
setText(getEvent().getKnown().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TableCell to show the (start) time of an event.
|
||||
*/
|
||||
private class EpochMillisCell extends EventTableCell {
|
||||
|
||||
@Override
|
||||
protected void updateItem(Long item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (empty || item == null) {
|
||||
setText("");
|
||||
} else {
|
||||
setText(TimeLineController.getZonedFormatter().print(getEvent().getStartMillis()));
|
||||
setText(textSupplier.apply(getEvent()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,23 +29,11 @@ import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||
import org.sleuthkit.autopsy.timeline.ui.AbstractTimeLineView;
|
||||
|
||||
/**
|
||||
* @param <X> The type of data plotted along the x axis
|
||||
* @param <Y> The type of data plotted along the y axis
|
||||
* @param <NodeType> The type of nodes used to represent data items
|
||||
* @param <ChartType> The type of the TimeLineChart<X> this class uses to plot
|
||||
* the data. Must extend Region.
|
||||
*
|
||||
* TODO: this is becoming (too?) closely tied to the notion that there is a
|
||||
* XYChart doing the rendering. Is this a good idea? -jm
|
||||
*
|
||||
* TODO: pull up common history context menu items out of derived classes? -jm
|
||||
*
|
||||
* public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node,
|
||||
* ChartType extends Region & TimeLineChart<X>> extends BorderPane {
|
||||
* An AbstractTimeLineView that uses a TableView to represent the events.
|
||||
*/
|
||||
public class ListViewPane extends AbstractTimeLineView {
|
||||
|
||||
private final ListTimeline listChart;
|
||||
private final ListTimeline listTimeline;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -54,15 +42,15 @@ public class ListViewPane extends AbstractTimeLineView {
|
||||
*/
|
||||
public ListViewPane(TimeLineController controller) {
|
||||
super(controller);
|
||||
listChart = new ListTimeline(controller);
|
||||
listTimeline = new ListTimeline(controller);
|
||||
|
||||
//initialize chart;
|
||||
setCenter(listChart);
|
||||
setCenter(listTimeline);
|
||||
setSettingsNodes(new ListViewPane.ListViewSettingsPane().getChildrenUnmodifiable());
|
||||
|
||||
//keep controller's list of selected event IDs in sync with this list's
|
||||
listChart.getSelectedEventIDs().addListener((Observable selectedIDs) -> {
|
||||
controller.selectEventIDs(listChart.getSelectedEventIDs());
|
||||
listTimeline.getSelectedEventIDs().addListener((Observable selectedIDs) -> {
|
||||
controller.selectEventIDs(listTimeline.getSelectedEventIDs());
|
||||
});
|
||||
}
|
||||
|
||||
@ -73,13 +61,10 @@ public class ListViewPane extends AbstractTimeLineView {
|
||||
|
||||
@Override
|
||||
protected void clearData() {
|
||||
listChart.clear();
|
||||
listTimeline.clear();
|
||||
}
|
||||
|
||||
private static class ListViewSettingsPane extends Parent {
|
||||
|
||||
ListViewSettingsPane() {
|
||||
}
|
||||
}
|
||||
|
||||
private class ListUpdateTask extends ViewRefreshTask<Interval> {
|
||||
@ -90,21 +75,30 @@ public class ListViewPane extends AbstractTimeLineView {
|
||||
|
||||
@Override
|
||||
protected Boolean call() throws Exception {
|
||||
super.call(); //To change body of generated methods, choose Tools | Templates.
|
||||
super.call();
|
||||
if (isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
FilteredEventsModel eventsModel = getEventsModel();
|
||||
|
||||
//clear the chart and set the horixontal axis
|
||||
//grab the currently selected event
|
||||
Long selectedEventID = listTimeline.getSelectedEventID();
|
||||
|
||||
//clear the chart and set the time range.
|
||||
resetView(eventsModel.getTimeRange());
|
||||
|
||||
updateMessage("Querying db for events");
|
||||
//get the event stripes to be displayed
|
||||
updateMessage("Querying DB for events");
|
||||
//get the IDs of th events to be displayed
|
||||
List<Long> eventIDs = eventsModel.getEventIDs();
|
||||
Platform.runLater(() -> listChart.setEventIDs(eventIDs));
|
||||
updateMessage("Updating UI");
|
||||
Platform.runLater(() -> {
|
||||
//put the event IDs into the table.
|
||||
listTimeline.setEventIDs(eventIDs);
|
||||
//restore the selected event
|
||||
listTimeline.selectEventID(selectedEventID);
|
||||
});
|
||||
|
||||
updateMessage("updating ui");
|
||||
return eventIDs.isEmpty() == false;
|
||||
}
|
||||
|
||||
@ -117,6 +111,5 @@ public class ListViewPane extends AbstractTimeLineView {
|
||||
@Override
|
||||
protected void setDateValues(Interval timeRange) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import org.apache.solr.common.util.ContentStream;
|
||||
import org.apache.solr.common.SolrInputDocument;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.TextUtil;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractContent;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
@ -298,6 +299,21 @@ class Ingester {
|
||||
String s = "";
|
||||
try {
|
||||
s = new String(docChunkContentBuf, 0, read, docContentEncoding);
|
||||
// Sanitize by replacing non-UTF-8 characters with caret '^' before adding to index
|
||||
char[] chars = null;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (!TextUtil.isValidSolrUTF8(s.charAt(i))) {
|
||||
// only convert string to char[] if there is a non-UTF8 character
|
||||
if (chars == null) {
|
||||
chars = s.toCharArray();
|
||||
}
|
||||
chars[i] = '^';
|
||||
}
|
||||
}
|
||||
// check if the string was modified (i.e. there was a non-UTF8 character found)
|
||||
if (chars != null) {
|
||||
s = new String(chars);
|
||||
}
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
logger.log(Level.SEVERE, "Unsupported encoding", ex); //NON-NLS
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import org.apache.solr.client.solrj.impl.HttpSolrServer;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException;
|
||||
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
|
||||
import org.apache.solr.common.util.ContentStreamBase.StringStream;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
|
@ -32,6 +32,7 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.sleuthkit.autopsy.coreutils.TextUtil;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@ -188,7 +189,7 @@ class TikaTextExtractor implements TextExtractor {
|
||||
|
||||
// Sanitize by replacing non-UTF-8 characters with caret '^'
|
||||
for (int i = 0; i < totalRead; ++i) {
|
||||
if (!isValidSolrUTF8(textChunkBuf[i])) {
|
||||
if (!TextUtil.isValidSolrUTF8(textChunkBuf[i])) {
|
||||
textChunkBuf[i] = '^';
|
||||
}
|
||||
}
|
||||
@ -255,27 +256,6 @@ class TikaTextExtractor implements TextExtractor {
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method determines if a passed-in Java char (16 bits) is a valid
|
||||
* UTF-8 printable character, returning true if so, false if not.
|
||||
*
|
||||
* Note that this method can have ramifications for characters outside the
|
||||
* Unicode Base Multilingual Plane (BMP), which require more than 16 bits.
|
||||
* We are using Java characters (16 bits) to look at the data and this will
|
||||
* not accurately identify any non-BMP character (larger than 16 bits)
|
||||
* ending with 0xFFFF and 0xFFFE. In the interest of a fast solution, we
|
||||
* have chosen to ignore the extended planes above Unicode BMP for the time
|
||||
* being. The net result of this is some non-BMP characters may be
|
||||
* interspersed with '^' characters in Autopsy.
|
||||
*
|
||||
* @param ch the character to test
|
||||
*
|
||||
* @return Returns true if the character is valid UTF-8, false if not.
|
||||
*/
|
||||
private static boolean isValidSolrUTF8(char ch) {
|
||||
return ((ch <= 0xFDD0 || ch >= 0xFDEF) && (ch > 0x1F || ch == 0x9 || ch == 0xA || ch == 0xD) && (ch != 0xFFFF) && (ch != 0xFFFE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isContentTypeSpecific() {
|
||||
return true;
|
||||
|
Loading…
x
Reference in New Issue
Block a user