mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 18:17:43 +00:00
more cleanup in SaveSnapshotAsReport and SnapShotReportWriter and templates
This commit is contained in:
parent
3c68a55ea2
commit
5ddd5dc26f
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014-15 Basis Technology Corp.
|
||||
* Copyright 2014-16 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -19,12 +19,15 @@
|
||||
package org.sleuthkit.autopsy.timeline.actions;
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import javafx.embed.swing.SwingFXUtils;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.ButtonBar;
|
||||
@ -32,8 +35,6 @@ import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.TextInputDialog;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.StageStyle;
|
||||
import javax.swing.JOptionPane;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.controlsfx.control.HyperlinkLabel;
|
||||
@ -47,19 +48,27 @@ import org.sleuthkit.autopsy.timeline.snapshot.SnapShotReportWriter;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Save a snapshot of the given node as an autopsy report.
|
||||
* Action that saves a snapshot of the given node as an autopsy report.
|
||||
* Delegates to SnapsHotReportWrite to actually generate and write the report.
|
||||
*/
|
||||
public class SaveSnapshotAsReport extends Action {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(SaveSnapshotAsReport.class.getName());
|
||||
private static final Image SNAP_SHOT = new Image("org/sleuthkit/autopsy/timeline/images/image.png", 16, 16, true, true); //
|
||||
private static final Image SNAP_SHOT = new Image("org/sleuthkit/autopsy/timeline/images/image.png", 16, 16, true, true);
|
||||
private static final ButtonType OPEN = new ButtonType(Bundle.OpenReportAction_DisplayName(), ButtonBar.ButtonData.NO);
|
||||
private static final ButtonType OK = new ButtonType(ButtonType.OK.getText(), ButtonBar.ButtonData.CANCEL_CLOSE);
|
||||
|
||||
private final TimeLineController controller;
|
||||
private final Case currentCase;
|
||||
|
||||
@NbBundle.Messages({"SaveSnapshot.action.name.text=Snapshot Report",
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param controller The controller for this timeline action
|
||||
* @param nodeSupplier The Supplier of the node to snapshot.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"SaveSnapshot.action.name.text=Snapshot Report",
|
||||
"SaveSnapshot.action.longText=Save a screen capture of the visualization as a report.",
|
||||
"SaveSnapshot.fileChoose.title.text=Save snapshot to",
|
||||
"# {0} - report file path",
|
||||
@ -67,10 +76,10 @@ public class SaveSnapshotAsReport extends Action {
|
||||
"Timeline.ModuleName=Timeline", "SaveSnapShotAsReport.Success=Success",
|
||||
"# {0} - uniqueness identifier, local date time at report creation time",
|
||||
"SaveSnapsHotAsReport.ReportName=timeline-report-{0}",
|
||||
"SaveSnapShotAsReport.FailedToAddReport=Failed to add snaphot as a report. See log for details",
|
||||
"SaveSnapShotAsReport.FailedToAddReport=Failed to add snaphot to case as a report.",
|
||||
"# {0} - report name",
|
||||
"SaveSnapShotAsReport.ErrorWritingReport=Error writing report {0} to disk. See log for details",})
|
||||
public SaveSnapshotAsReport(TimeLineController controller, Node node) {
|
||||
"SaveSnapShotAsReport.ErrorWritingReport=Error writing report {0} to disk.",})
|
||||
public SaveSnapshotAsReport(TimeLineController controller, Supplier<Node> nodeSupplier) {
|
||||
super(Bundle.SaveSnapshot_action_name_text());
|
||||
setLongText(Bundle.SaveSnapshot_action_longText());
|
||||
setGraphic(new ImageView(SNAP_SHOT));
|
||||
@ -79,9 +88,13 @@ public class SaveSnapshotAsReport extends Action {
|
||||
this.currentCase = controller.getAutopsyCase();
|
||||
|
||||
setEventHandler(actionEvent -> {
|
||||
//capture generation date and use to make default report name
|
||||
Date generationDate = new Date();
|
||||
final String defaultReportName = FileUtil.escapeFileName(currentCase.getName() + " " + new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss").format(generationDate));
|
||||
|
||||
BufferedImage snapshot = SwingFXUtils.fromFXImage(nodeSupplier.get().snapshot(null, null), null);
|
||||
|
||||
//prompt user to pick report name
|
||||
TextInputDialog textInputDialog = new TextInputDialog();
|
||||
textInputDialog.setTitle("Timeline");
|
||||
textInputDialog.getEditor().setPromptText("leave empty for default report name: " + defaultReportName);
|
||||
@ -89,48 +102,54 @@ public class SaveSnapshotAsReport extends Action {
|
||||
textInputDialog.setHeaderText("Enter a report name for the Timeline Snapshot Report.");
|
||||
|
||||
textInputDialog.showAndWait().ifPresent(enteredReportName -> {
|
||||
//reportName defaults to case name + timestamp if left blank
|
||||
String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
|
||||
Path reportFolderPath = Paths.get(currentCase.getReportDirectory(), reportName, "Timeline Snapshot");
|
||||
Path reportIndexFilePath;
|
||||
try {
|
||||
Path reportMainFilePath;
|
||||
|
||||
reportIndexFilePath = new SnapShotReportWriter(currentCase, reportFolderPath, reportName, controller.getEventsModel().getZoomParamaters(), generationDate, node).writeReport();
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Error writing report " + reportFolderPath + " to disk", e); //
|
||||
try {
|
||||
//generate and write report
|
||||
reportMainFilePath = new SnapShotReportWriter(currentCase,
|
||||
reportFolderPath,
|
||||
reportName,
|
||||
controller.getEventsModel().getZoomParamaters(),
|
||||
generationDate, snapshot).writeReport();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error writing report to disk at " + reportFolderPath, ex);
|
||||
new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_ErrorWritingReport(reportFolderPath)).show();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
//add html file as report to case
|
||||
Case.getCurrentCase().addReport(reportIndexFilePath.toString(), Bundle.Timeline_ModuleName(), reportName);
|
||||
//add main file as report to case
|
||||
Case.getCurrentCase().addReport(reportMainFilePath.toString(), Bundle.Timeline_ModuleName(), reportName);
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "failed to add html wrapper as a report", ex); //
|
||||
LOGGER.log(Level.WARNING, "Failed to add " + reportMainFilePath.toString() + " to case as a report", ex); //
|
||||
new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_FailedToAddReport()).show();
|
||||
return;
|
||||
}
|
||||
|
||||
//create alert to notify user of report location
|
||||
//notify user of report location
|
||||
final Alert alert = new Alert(Alert.AlertType.INFORMATION, null, OPEN, OK);
|
||||
alert.setTitle(Bundle.SaveSnapshot_action_name_text());
|
||||
alert.setHeaderText(Bundle.SaveSnapShotAsReport_Success());
|
||||
alert.initStyle(StageStyle.UTILITY);
|
||||
alert.initOwner(node.getScene().getWindow());
|
||||
alert.initModality(Modality.APPLICATION_MODAL);
|
||||
|
||||
//make action to open report, and hyperlinklable to invoke action
|
||||
final OpenReportAction openReportAction = new OpenReportAction(reportIndexFilePath);
|
||||
HyperlinkLabel hyperlinkLabel = new HyperlinkLabel(Bundle.SaveSnapShotAsReport_ReportSavedAt(reportIndexFilePath.toString()));
|
||||
final OpenReportAction openReportAction = new OpenReportAction(reportMainFilePath);
|
||||
HyperlinkLabel hyperlinkLabel = new HyperlinkLabel(Bundle.SaveSnapShotAsReport_ReportSavedAt(reportMainFilePath.toString()));
|
||||
hyperlinkLabel.setOnAction(openReportAction);
|
||||
alert.getDialogPane().setContent(hyperlinkLabel);
|
||||
|
||||
alert.showAndWait().filter(OPEN::equals)
|
||||
.ifPresent(buttonType -> openReportAction.handle(null));
|
||||
|
||||
alert.showAndWait().filter(OPEN::equals).ifPresent(buttonType -> openReportAction.handle(null));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"OpenReportAction.DisplayName=Open Report",
|
||||
/**
|
||||
* Action that opens the given Path in the system default application.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"OpenReportAction.DisplayName=Open Report",
|
||||
"OpenReportAction.NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
|
||||
"OpenReportAction.MessageBoxTitle=Open Report Failure",
|
||||
"OpenReportAction.NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
|
||||
|
@ -1,16 +0,0 @@
|
||||
body {margin: 0px; padding: 0px; background: #FFFFFF; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353;}
|
||||
#content {padding: 30px;}
|
||||
#header {width:100%; padding: 10px; line-height: 25px; background: #07A; color: #FFF; font-size: 20px;}
|
||||
h1 {font-size: 20px; font-weight: normal; color: #07A; padding: 0 0 7px 0; margin-top: 25px; border-bottom: 1px solid #D6D6D6;}
|
||||
h2 {font-size: 20px; font-weight: bolder; color: #07A;}
|
||||
h3 {font-size: 16px; color: #07A;}
|
||||
h4 {background: #07A; color: #FFF; font-size: 16px; margin: 0 0 0 25px; padding: 0; padding-left: 15px;}
|
||||
ul.nav {list-style-type: none; line-height: 35px; padding: 0px; margin-left: 15px;}
|
||||
ul li a {font-size: 14px; color: #444; text-decoration: none; padding-left: 25px;}
|
||||
ul li a:hover {text-decoration: underline;}
|
||||
p {margin: 0 0 20px 0;}
|
||||
table {max-width: 100%; min-width: 700px; padding: 0; margin: 0; border-collapse: collapse; border-bottom: 2px solid #e5e5e5;}
|
||||
.keyword_list table {width: 100%; margin: 0 0 25px 25px; border-bottom: 2px solid #dedede;}
|
||||
table th {display: table-cell; text-align: left; padding: 8px 16px; background: #e5e5e5; color: #777; font-size: 11px; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #e5e5e5;}
|
||||
table td {display: table-cell; padding: 8px 16px; font: 13px/20px Arial, Helvetica, sans-serif; max-width: 500px; min-width: 125px; word-break: break-all; overflow: auto;}
|
||||
table tr:nth-child(even) td {background: #f3f3f3;}
|
@ -1,13 +1,27 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2016 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.timeline.snapshot;
|
||||
|
||||
import com.github.mustachejava.DefaultMustacheFactory;
|
||||
import com.github.mustachejava.Mustache;
|
||||
import com.github.mustachejava.MustacheFactory;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@ -19,156 +33,197 @@ import java.nio.file.Paths;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import javafx.embed.swing.SwingFXUtils;
|
||||
import javafx.scene.Node;
|
||||
import javax.imageio.ImageIO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.Interval;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.report.ReportBranding;
|
||||
import org.sleuthkit.autopsy.timeline.actions.SaveSnapshotAsReport;
|
||||
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
|
||||
|
||||
/**
|
||||
*
|
||||
* Generate and write the Timeline snapshot report to disk.
|
||||
*/
|
||||
public class SnapShotReportWriter {
|
||||
|
||||
/**
|
||||
* mustache.java template factory.
|
||||
*/
|
||||
private final static MustacheFactory mf = new DefaultMustacheFactory();
|
||||
|
||||
private final Case currentCase;
|
||||
private final Path reportFolderPath;
|
||||
private final String reportName;
|
||||
private final ReportBranding reportBranding;
|
||||
|
||||
private final ZoomParams zoomParams;
|
||||
private final Date generationDate;
|
||||
private final BufferedImage image;
|
||||
|
||||
public SnapShotReportWriter(Case currentCase, Path reportFolderPath ,String reportName, ZoomParams zoomParams, Date generationDate, Node node) {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param currentCase The Case to write a report for.
|
||||
* @param reportFolderPath The Path to the folder that will contain the
|
||||
* report.
|
||||
* @param reportName The name of the report.
|
||||
* @param zoomParams The ZoomParams in effect when the snapshot was
|
||||
* taken.
|
||||
* @param generationDate The generation Date of the report.
|
||||
* @param snapshot A snapshot of the visualization to include in the
|
||||
* report.
|
||||
*/
|
||||
public SnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, ZoomParams zoomParams, Date generationDate, BufferedImage snapshot) {
|
||||
this.currentCase = currentCase;
|
||||
this.reportFolderPath = reportFolderPath;
|
||||
this.reportName = reportName;
|
||||
this.zoomParams = zoomParams;
|
||||
this.generationDate = generationDate;
|
||||
this.image = snapshot;
|
||||
|
||||
this.reportBranding = new ReportBranding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and write the report to disk.
|
||||
*
|
||||
* @return The Path to the "main file" of the report. This is the file that
|
||||
* Autopsy shows in the results view when the Reports Node is
|
||||
* selected in the DirectoryTree.
|
||||
*
|
||||
* @throws IOException If there is a problem writing the report.
|
||||
*/
|
||||
public Path writeReport() throws IOException {
|
||||
ReportBranding reportBranding = new ReportBranding();
|
||||
|
||||
|
||||
Path reportIndexFilePath = reportFolderPath.resolve("index.html");
|
||||
|
||||
//ensure directory exists and write html files
|
||||
//ensure directory exists
|
||||
Files.createDirectories(reportFolderPath);
|
||||
writeSummary(reportFolderPath, generationDate, reportBranding, reportName);
|
||||
writeIndexHTML(reportFolderPath);
|
||||
writeSnapShotHTMLFile(reportFolderPath, reportName, zoomParams);
|
||||
|
||||
//take snapshot and save in report directory
|
||||
ImageIO.write(SwingFXUtils.fromFXImage(node.snapshot(null, null), null), "png",
|
||||
reportFolderPath.resolve("snapshot.png").toFile());
|
||||
//save the snapshot in the report directory
|
||||
ImageIO.write(image, "png", reportFolderPath.resolve("snapshot.png").toFile());
|
||||
|
||||
copyResources(reportFolderPath, reportBranding);
|
||||
copyResources();
|
||||
|
||||
return reportIndexFilePath;
|
||||
writeSummaryHTML();
|
||||
writeSnapShotHTMLFile();
|
||||
return writeIndexHTML();
|
||||
}
|
||||
|
||||
private static void writeSnapShotHTMLFile(Path reportPath, String reportTitle, ZoomParams zoomParams) throws IOException {
|
||||
/**
|
||||
* Generate and write the html page that shows the snapshot and the state of
|
||||
* the ZoomParams
|
||||
*
|
||||
* @throws IOException If there is a problem writing the html file to disk.
|
||||
*/
|
||||
private void writeSnapShotHTMLFile() throws IOException {
|
||||
//make a map of context objects to resolve template paramaters against
|
||||
HashMap<String, Object> snapShotContext = new HashMap<>();
|
||||
snapShotContext.put("reportTitle", reportName);
|
||||
snapShotContext.put("startTime", zoomParams.getTimeRange().getStart().toString(DateTimeFormat.fullDateTime()));
|
||||
snapShotContext.put("endTime", zoomParams.getTimeRange().getEnd().toString(DateTimeFormat.fullDateTime()));
|
||||
snapShotContext.put("zoomParams", zoomParams);
|
||||
|
||||
HashMap<String, Object> context = new HashMap<>();
|
||||
fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/snapshot_template.html", "Snapshot", snapShotContext, reportFolderPath.resolve("snapshot.html"));
|
||||
}
|
||||
|
||||
context.put("reportTitle", reportTitle);
|
||||
Interval timeRange = zoomParams.getTimeRange();
|
||||
context.put("startTime", timeRange.getStart().toString(DateTimeFormat.fullDateTime()));
|
||||
context.put("endTime", timeRange.getEnd().toString(DateTimeFormat.fullDateTime()));
|
||||
context.put("zoomParams", zoomParams);
|
||||
/**
|
||||
* Generate and write the main html page with frames for navigation on the
|
||||
* left and content on the right.
|
||||
*
|
||||
* @return The Path of the written html file.
|
||||
*
|
||||
* @throws IOException If there is a problem writing the html file to disk.
|
||||
*/
|
||||
private Path writeIndexHTML() throws IOException {
|
||||
//make a map of context objects to resolve template paramaters against
|
||||
HashMap<String, Object> indexContext = new HashMap<>();
|
||||
indexContext.put("currentCase", currentCase);
|
||||
Path reportIndexFile = reportFolderPath.resolve("index.html");
|
||||
|
||||
try (Writer writer = Files.newBufferedWriter(reportPath.resolve("snapshot.html"), Charset.forName("UTF-8"))) {
|
||||
Mustache summaryMustache = mf.compile(new InputStreamReader(SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/snapshot_template.html")), "Snapshot");
|
||||
fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/index_template.html", "Index", indexContext, reportIndexFile);
|
||||
return reportIndexFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* * Generate and write the summary of the current case for this report.
|
||||
*
|
||||
* @throws IOException If there is a problem writing the html file to disk.
|
||||
*/
|
||||
private void writeSummaryHTML() throws IOException {
|
||||
//make a map of context objects to resolve template paramaters against
|
||||
HashMap<String, Object> summaryContext = new HashMap<>();
|
||||
summaryContext.put("reportName", reportName);
|
||||
summaryContext.put("reportBranding", reportBranding);
|
||||
summaryContext.put("generationDateTime", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(generationDate));
|
||||
summaryContext.put("ingestRunning", IngestManager.getInstance().isIngestRunning());
|
||||
summaryContext.put("currentCase", currentCase);
|
||||
|
||||
fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/summary_template.html", "Summary", summaryContext, reportFolderPath.resolve("summary.html"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in the mustache template at the given location using the values from
|
||||
* the given context object and save it to the given outPutFile.
|
||||
*
|
||||
* @param templateLocation The location of the template. suitible for use
|
||||
* with Class.getResourceAsStream
|
||||
* @param templateName The name of the tempalte. (Used by mustache to
|
||||
* cache templates?)
|
||||
* @param context The contect to use to fill in the template
|
||||
* values.
|
||||
* @param outPutFile The filled in tempalte will be saced at this
|
||||
* Path.
|
||||
*
|
||||
* @throws IOException If there is a problem saving the filled in template
|
||||
* to disk.
|
||||
*/
|
||||
private void fillTemplateAndWrite(final String templateLocation, final String templateName, Object context, final Path outPutFile) throws IOException {
|
||||
|
||||
Mustache summaryMustache = mf.compile(new InputStreamReader(SnapShotReportWriter.class.getResourceAsStream(templateLocation)), templateName);
|
||||
try (Writer writer = Files.newBufferedWriter(outPutFile, Charset.forName("UTF-8"))) {
|
||||
summaryMustache.execute(writer, context);
|
||||
writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeIndexHTML(Path reportPath) throws IOException {
|
||||
|
||||
HashMap<String, Object> context = new HashMap<>();
|
||||
|
||||
context.put("currentCase", currentCase);
|
||||
|
||||
try (Writer writer = Files.newBufferedWriter(reportPath.resolve("index.html"), Charset.forName("UTF-8"))) {
|
||||
Mustache summaryMustache = mf.compile(new InputStreamReader(SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/index_template.html")), "Index");
|
||||
summaryMustache.execute(writer, context);
|
||||
writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the summary of the current case for this report.
|
||||
* Copy static resources (static html, css, images, etc) to the reports
|
||||
* folder.
|
||||
*
|
||||
* @param reportPath the value of reportPath
|
||||
* @param generationDate the value of generationDate
|
||||
* @throws IOException If there is a problem copying the resources.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"ReportHTML.writeSum.caseName=Case:",
|
||||
"ReportHTML.writeSum.caseNum=Case Number:",
|
||||
"ReportHTML.writeSum.noCaseNum=<i>No case number</i>",
|
||||
"ReportHTML.writeSum.noExaminer=<i>No examiner</i>",
|
||||
"ReportHTML.writeSum.examiner=Examiner:",
|
||||
"ReportHTML.writeSum.timezone=Timezone:",
|
||||
"ReportHTML.writeSum.path=Path:"})
|
||||
private void writeSummary(Path reportPath, final Date generationDate, ReportBranding reportBranding, String reportName) throws IOException {
|
||||
HashMap<String, Object> context = new HashMap<>();
|
||||
private void copyResources() throws IOException {
|
||||
|
||||
context.put("reportName", reportName);
|
||||
context.put("reportBranding", reportBranding);
|
||||
context.put("generationDateTime", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(generationDate));
|
||||
context.put("ingestRunning", IngestManager.getInstance().isIngestRunning());
|
||||
context.put("currentCase", currentCase);
|
||||
|
||||
try (Writer writer = Files.newBufferedWriter(reportPath.resolve("summary.html"), Charset.forName("UTF-8"))) {
|
||||
|
||||
Mustache summaryMustache = mf.compile(new InputStreamReader(SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/summary_template.html")), "Summary");
|
||||
summaryMustache.execute(writer, context);
|
||||
writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
private void copyResources(Path reportPath, ReportBranding reportBranding) throws IOException {
|
||||
//copy navigation html
|
||||
try (InputStream navStream = SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/navigation_template.html")) {
|
||||
Files.copy(navStream, reportPath.resolve("nav.html"));
|
||||
}
|
||||
|
||||
//pull generator and agency logo from branding
|
||||
//pull generator and agency logos from branding
|
||||
String generatorLogoPath = reportBranding.getGeneratorLogoPath();
|
||||
if (StringUtils.isNotBlank(generatorLogoPath)) {
|
||||
Files.copy(Paths.get(generatorLogoPath), reportPath.resolve("generator_logo.png"));
|
||||
Files.copy(Files.newInputStream(Paths.get(generatorLogoPath)), reportFolderPath.resolve("generator_logo.png"));
|
||||
}
|
||||
|
||||
String agencyLogoPath = reportBranding.getAgencyLogoPath();
|
||||
if (StringUtils.isNotBlank(agencyLogoPath)) {
|
||||
Files.copy(Paths.get(agencyLogoPath), reportPath.resolve("agency_logo.png"));
|
||||
Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve("agency_logo.png"));
|
||||
}
|
||||
|
||||
//copy navigation html
|
||||
try (InputStream navStream = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/snapshot/navigation.html")) {
|
||||
Files.copy(navStream, reportFolderPath.resolve("nav.html"));
|
||||
}
|
||||
//copy favicon
|
||||
try (InputStream faviconStream = SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/favicon.ico")) {
|
||||
Files.copy(faviconStream, reportPath.resolve("favicon.ico"));
|
||||
try (InputStream faviconStream = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/favicon.ico")) {
|
||||
Files.copy(faviconStream, reportFolderPath.resolve("favicon.ico"));
|
||||
}
|
||||
try (InputStream summaryStream = SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/summary.png")) {
|
||||
Files.copy(summaryStream, reportPath.resolve("summary.png"));
|
||||
//copy report summary icon
|
||||
try (InputStream summaryStream = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/summary.png")) {
|
||||
Files.copy(summaryStream, reportFolderPath.resolve("summary.png"));
|
||||
}
|
||||
try (InputStream snapshotIconStream = SaveSnapshotAsReport.class.getResourceAsStream("org/sleuthkit/autopsy/timeline/images/image.png")) {
|
||||
Files.copy(snapshotIconStream, reportPath.resolve("snapshot_icon.png"));
|
||||
//copy snapshot icon
|
||||
try (InputStream snapshotIconStream = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/images/image.png")) {
|
||||
Files.copy(snapshotIconStream, reportFolderPath.resolve("snapshot_icon.png"));
|
||||
}
|
||||
|
||||
//copy report css
|
||||
try (InputStream resource = SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/index.css")) { //
|
||||
Files.copy(resource, reportPath.resolve("index.css")); //
|
||||
//copy main report css
|
||||
try (InputStream resource = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/snapshot/index.css")) { //
|
||||
Files.copy(resource, reportFolderPath.resolve("index.css")); //
|
||||
}
|
||||
//copy summary css
|
||||
try (InputStream resource = SaveSnapshotAsReport.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/actions/summary.css")) { //
|
||||
Files.copy(resource, reportPath.resolve("summary.css")); //
|
||||
try (InputStream resource = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/snapshot/summary.css")) { //
|
||||
Files.copy(resource, reportFolderPath.resolve("summary.css")); //
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
Core/src/org/sleuthkit/autopsy/timeline/snapshot/index.css
Normal file
18
Core/src/org/sleuthkit/autopsy/timeline/snapshot/index.css
Normal file
@ -0,0 +1,18 @@
|
||||
body {margin: 0px; padding: 0px; background: #FFFFFF; font: 13px/20px Arial, Helvetica, sans-serif; color: #535353;}
|
||||
#content {padding: 30px;}
|
||||
#header {width:100%; padding: 10px; line-height: 25px; background: #07A; color: #FFF; font-size: 20px;}
|
||||
h1 {font-size: 20px; font-weight: normal; color: #07A; padding: 0 0 7px 0; margin-top: 25px; border-bottom: 1px solid #D6D6D6;}
|
||||
h2 {font-size: 20px; font-weight: bolder; color: #07A;}
|
||||
h3 {font-size: 16px; color: #07A;}
|
||||
h4 {background: #07A; color: #FFF; font-size: 16px; margin: 0 0 0 25px; padding: 0; padding-left: 15px;}
|
||||
ul.nav {list-style-type: none; line-height: 35px; padding: 0px; margin-left: 15px;}
|
||||
ul li a {font-size: 14px; color: #444; text-decoration: none; padding-left: 25px;}
|
||||
ul li a:hover {text-decoration: underline;}
|
||||
p {margin: 0 0 20px 0;}
|
||||
table {white-space:nowrap; min-width: 700px; padding: 2; margin: 0; border-collapse: collapse; border-bottom: 2px solid #e5e5e5;}
|
||||
.keyword_list table {margin: 0 0 25px 25px; border-bottom: 2px solid #dedede;}
|
||||
table th {white-space:nowrap; display: table-cell; text-align: center; padding: 2px 4px; background: #e5e5e5; color: #777; font-size: 11px; text-shadow: #e9f9fd 0 1px 0; border-top: 1px solid #dedede; border-bottom: 2px solid #e5e5e5;}
|
||||
table .left_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: left; }
|
||||
table .right_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: right; }
|
||||
table td {white-space:nowrap; display: table-cell; padding: 2px 3px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align:left; }
|
||||
table tr:nth-child(even) td {background: #f3f3f3;}
|
@ -1,14 +1,14 @@
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Autopsy Timeline Snapshot:{{reportTitle}}</title>
|
||||
<title>Timeline Snapshot: {{reportTitle}}</title>
|
||||
<link rel="stylesheet" type="text/css" href="index.css" />
|
||||
<link rel="icon" type="image/ico" href="favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<body><div id="header">Timeline Snapshot</div>
|
||||
<div id="content">
|
||||
<h1>{{reportTitle}}</h1>
|
||||
<img src = "snapshot.png" alt = "snaphot"/>
|
||||
|
||||
<img src = "snapshot.png" alt = "snaphot">
|
||||
<table>
|
||||
<tr><td>Time Range: </td><td>{{startTime}}
|
||||
to
|
||||
@ -17,15 +17,16 @@
|
||||
<tr><td>Event Type Zoom Level: </td><td>{{zoomParams.getTypeZoomLevel.getDisplayName}}</td></tr>
|
||||
{{#zoomParams.getFilter}}
|
||||
<tr>
|
||||
<td>Filters: </td>><td>
|
||||
<td>Filters: </td>
|
||||
<td>
|
||||
<ul>
|
||||
{{#getTextFilter}}<li>text = "{{getText}}"[{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]</li> {{/getTextFilter}}
|
||||
{{#getTextFilter}}<li>text = "{{getText}}" [{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]</li> {{/getTextFilter}}
|
||||
{{#getKnownFilter}}<li>Hide Known Files [{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]</li> {{/getKnownFilter}}
|
||||
{{#getDataSourcesFilter}}
|
||||
<li>{{getDisplayName}} [{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}] :
|
||||
<ul>
|
||||
{{#getSubFilters}}
|
||||
<li>{{getDisplayName}}[{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]</li>
|
||||
<li>{{getDisplayName}} [{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]</li>
|
||||
{{/getSubFilters}}
|
||||
</ul>
|
||||
</li>
|
||||
@ -34,7 +35,7 @@
|
||||
<li>{{getDisplayName}} [{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}] :
|
||||
<ul>
|
||||
{{#getSubFilters}}
|
||||
<li>{{getDisplayName}}[{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]</li>
|
||||
<li>{{getDisplayName}} [{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]</li>
|
||||
{{/getSubFilters}}
|
||||
</ul>
|
||||
</li>
|
||||
@ -43,7 +44,7 @@
|
||||
<li>{{getDisplayName}} [{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}] :
|
||||
<ul>
|
||||
{{#getSubFilters}}
|
||||
<li>{{getDisplayName}}[{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]</li>
|
||||
<li>{{getDisplayName}} [{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]</li>
|
||||
{{/getSubFilters}}
|
||||
</ul>
|
||||
</li>
|
||||
@ -52,10 +53,10 @@
|
||||
<li>{{getDisplayName}} [{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}] :
|
||||
<ul>
|
||||
{{#getSubFilters}}
|
||||
<li>{{getDisplayName}}[{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]
|
||||
<li>{{getDisplayName}} [{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]
|
||||
<ul>
|
||||
{{#getSubFilters}}
|
||||
<li>{{getDisplayName}}[{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]</li>
|
||||
<li>{{getDisplayName}} [{{#isSelected}}x{{/isSelected}}{{^isSelected}} {{/isSelected}}]</li>
|
||||
{{/getSubFilters}}
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -7,7 +7,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
<h1>{{reportBranding.getReportTitle}}:{{reportName}}{{#ingestRunning}}<span>Warning, this report was run before ingest services completed!</span>{{/ingestRunning}}</h1>
|
||||
<h1>{{reportBranding.getReportTitle}}: {{reportName}}{{#ingestRunning}}<span>Warning, this report was run before ingest services completed!</span>{{/ingestRunning}}</h1>
|
||||
|
||||
<p class="subheadding">Timeline Report generated on {{generationDateTime}}</p>
|
||||
<div class="title">
|
||||
@ -33,7 +33,6 @@
|
||||
{{#currentCase.getDataSources}}
|
||||
<p>{{getName}}</p>
|
||||
{{#getTimeZone}}
|
||||
|
||||
<table>
|
||||
<tr><td>Timezone:</td><td>{{getTimeZone}}</td></tr>
|
||||
{{#getPaths}}
|
||||
|
@ -288,7 +288,7 @@ final public class VisualizationPanel extends BorderPane {
|
||||
setViewMode(controller.viewModeProperty().get());
|
||||
|
||||
//configure snapshor button / action
|
||||
ActionUtils.configureButton(new SaveSnapshotAsReport(controller, VisualizationPanel.this), snapShotButton);
|
||||
ActionUtils.configureButton(new SaveSnapshotAsReport(controller, () -> VisualizationPanel.this), snapShotButton);
|
||||
|
||||
/////configure start and end pickers
|
||||
startLabel.setText(Bundle.VisualizationPanel_startLabel_text());
|
||||
|
Loading…
x
Reference in New Issue
Block a user