mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge pull request #4609 from kellykelly3/1213-com-view-image-report
1213 communications snapshot report
This commit is contained in:
commit
9f5f586aae
@ -38,3 +38,4 @@ VisualizationPanel.organicLayoutButton.text=Organic
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic
|
||||
VisualizationPanel.hierarchyLayoutButton.text=Hierarchical
|
||||
VisualizationPanel.clearVizButton.text_1=Clear Viz.
|
||||
VisualizationPanel.snapshotButton.text_1=Snapshot Report
|
||||
|
@ -32,6 +32,23 @@ ResetAndPinAccountsAction.singularText=Visualize Only Selected Account
|
||||
UnpinAccountsAction.pluralText=Remove Selected Accounts
|
||||
UnpinAccountsAction.singularText=Remove Selected Account
|
||||
VisalizationPanel.paintingError=Problem painting visualization.
|
||||
# {0} - default name
|
||||
VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}
|
||||
VisualizationPane_blank_report_title=Blank Report Name
|
||||
VisualizationPane_DisplayName=Open Report
|
||||
VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:
|
||||
VisualizationPane_MessageBoxTitle=Open Report Failure
|
||||
VisualizationPane_MissingReportFileMessage=The report file no longer exists.
|
||||
VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.
|
||||
VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.
|
||||
VisualizationPane_Open_Report=Open Report
|
||||
# {0} - report name
|
||||
VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}
|
||||
VisualizationPane_Report_OK_Button=OK
|
||||
# {0} - report path
|
||||
VisualizationPane_Report_Success=Report Successfully create at:\n{0}
|
||||
VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.
|
||||
VisualizationPane_reportName=Communications Snapshot
|
||||
VisualizationPanel.cancelButton.text=Cancel
|
||||
VisualizationPanel.computingLayout=Computing Layout
|
||||
VisualizationPanel.jButton1.text=Fast Organic
|
||||
@ -65,3 +82,8 @@ VisualizationPanel.organicLayoutButton.text=Organic
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic
|
||||
VisualizationPanel.hierarchyLayoutButton.text=Hierarchical
|
||||
VisualizationPanel.clearVizButton.text_1=Clear Viz.
|
||||
VisualizationPanel.snapshotButton.text_1=Snapshot Report
|
||||
VisualizationPanel_action_dialogs_title=Communications
|
||||
VisualizationPanel_action_name_text=Snapshot Report
|
||||
VisualizationPanel_module_name=Communications
|
||||
VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation.
|
||||
|
@ -11,7 +11,7 @@
|
||||
<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"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-93,0,0,3,71"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-93,0,0,4,-19"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
@ -49,9 +49,9 @@
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace pref="71" max="32767" attributes="0"/>
|
||||
<EmptySpace pref="268" max="32767" attributes="0"/>
|
||||
<Component id="jTextArea1" min="-2" pref="424" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="248" max="32767" attributes="0"/>
|
||||
<EmptySpace pref="445" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -120,6 +120,10 @@
|
||||
<Component id="zoomActualButton" min="-2" pref="33" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="fitZoomButton" min="-2" pref="32" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jSeparator3" min="-2" pref="10" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="snapshotButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
@ -143,6 +147,8 @@
|
||||
<Component id="zoomLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="clearVizButton" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jSeparator2" alignment="2" max="32767" attributes="0"/>
|
||||
<Component id="snapshotButton" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jSeparator3" alignment="2" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
@ -310,6 +316,24 @@
|
||||
<Property name="orientation" type="int" value="1"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="snapshotButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/report/images/image.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="VisualizationPanel.snapshotButton.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="snapshotButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JToolBar$Separator" name="jSeparator3">
|
||||
<Properties>
|
||||
<Property name="orientation" type="int" value="1"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javafx.embed.swing.JFXPanel" name="notificationsJFXPanel">
|
||||
|
@ -28,6 +28,7 @@ import com.mxgraph.model.mxCell;
|
||||
import com.mxgraph.model.mxICell;
|
||||
import com.mxgraph.swing.handler.mxRubberband;
|
||||
import com.mxgraph.swing.mxGraphComponent;
|
||||
import com.mxgraph.util.mxCellRenderer;
|
||||
import com.mxgraph.util.mxEvent;
|
||||
import com.mxgraph.util.mxEventObject;
|
||||
import com.mxgraph.util.mxEventSource;
|
||||
@ -41,19 +42,28 @@ import com.mxgraph.view.mxGraphView;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyVetoException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -75,14 +85,17 @@ import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.JToolBar;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.controlsfx.control.Notifications;
|
||||
import org.jdesktop.layout.GroupLayout;
|
||||
import org.jdesktop.layout.LayoutStyle;
|
||||
@ -92,8 +105,11 @@ import org.openide.nodes.Node;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.ProxyLookup;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.communications.snapshot.CommSnapShotReportWriter;
|
||||
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
|
||||
@ -101,7 +117,6 @@ import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* A panel that goes in the Visualize tab of the Communications Visualization
|
||||
* Tool. Hosts an JGraphX mxGraphComponent that implements the communications
|
||||
@ -387,6 +402,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
zoomLabel = new JLabel();
|
||||
clearVizButton = new JButton();
|
||||
jSeparator2 = new JToolBar.Separator();
|
||||
snapshotButton = new JButton();
|
||||
jSeparator3 = new JToolBar.Separator();
|
||||
notificationsJFXPanel = new JFXPanel();
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
@ -406,9 +423,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
placeHolderPanel.setLayout(placeHolderPanelLayout);
|
||||
placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
|
||||
.add(placeHolderPanelLayout.createSequentialGroup()
|
||||
.addContainerGap(71, Short.MAX_VALUE)
|
||||
.addContainerGap(268, Short.MAX_VALUE)
|
||||
.add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(248, Short.MAX_VALUE))
|
||||
.addContainerGap(445, Short.MAX_VALUE))
|
||||
);
|
||||
placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
|
||||
.add(placeHolderPanelLayout.createSequentialGroup()
|
||||
@ -505,6 +522,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
|
||||
jSeparator2.setOrientation(SwingConstants.VERTICAL);
|
||||
|
||||
snapshotButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/report/images/image.png"))); // NOI18N
|
||||
snapshotButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.snapshotButton.text_1")); // NOI18N
|
||||
snapshotButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
snapshotButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jSeparator3.setOrientation(SwingConstants.VERTICAL);
|
||||
|
||||
GroupLayout toolbarLayout = new GroupLayout(toolbar);
|
||||
toolbar.setLayout(toolbarLayout);
|
||||
toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
|
||||
@ -537,6 +564,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
.add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(jSeparator3, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(snapshotButton)
|
||||
.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
toolbarLayout.setVerticalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
|
||||
@ -556,7 +587,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
.add(jLabel2)
|
||||
.add(zoomLabel)
|
||||
.add(clearVizButton)
|
||||
.add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(snapshotButton)
|
||||
.add(jSeparator3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.add(3, 3, 3))
|
||||
);
|
||||
|
||||
@ -648,6 +681,24 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
setCursor(Cursor.getDefaultCursor());
|
||||
}//GEN-LAST:event_clearVizButtonActionPerformed
|
||||
|
||||
@NbBundle.Messages({
|
||||
"VisualizationPanel_snapshot_report_failure=Snapshot report not created. An error occurred during creation."
|
||||
})
|
||||
private void snapshotButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_snapshotButtonActionPerformed
|
||||
try {
|
||||
handleSnapshotEvent();
|
||||
} catch (NoCurrentCaseException | IOException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to create communications snapsot report", ex); //NON-NLS
|
||||
|
||||
Platform.runLater(()
|
||||
-> Notifications.create().owner(notificationsJFXPanel.getScene().getWindow())
|
||||
.text(Bundle.VisualizationPanel_snapshot_report_failure())
|
||||
.showWarning());
|
||||
} catch( TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to add report to currenct case", ex); //NON-NLS
|
||||
}
|
||||
}//GEN-LAST:event_snapshotButtonActionPerformed
|
||||
|
||||
private void fitGraph() {
|
||||
graphComponent.zoomTo(1, true);
|
||||
mxPoint translate = graph.getView().getTranslate();
|
||||
@ -675,6 +726,128 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
graphComponent.zoom((heightFactor + widthFactor) / 2.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ActionPerformed event from the Snapshot button.
|
||||
*
|
||||
* @throws NoCurrentCaseException
|
||||
* @throws IOException
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"VisualizationPanel_action_dialogs_title=Communications",
|
||||
"VisualizationPanel_module_name=Communications",
|
||||
"VisualizationPanel_action_name_text=Snapshot Report",
|
||||
"VisualizationPane_fileName_prompt=Enter name for the Communications Snapshot Report:",
|
||||
"VisualizationPane_reportName=Communications Snapshot",
|
||||
"# {0} - default name",
|
||||
"VisualizationPane_accept_defaultName=Report name was empty. Press OK to accept default report name: {0}",
|
||||
"VisualizationPane_blank_report_title=Blank Report Name",
|
||||
"# {0} - report name",
|
||||
"VisualizationPane_overrite_exiting=Overwrite existing report?\n{0}"
|
||||
})
|
||||
private void handleSnapshotEvent() throws NoCurrentCaseException, IOException, TskCoreException {
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
Date generationDate = new Date();
|
||||
|
||||
final String defaultReportName = FileUtil.escapeFileName(currentCase.getDisplayName() + " " + new SimpleDateFormat("MMddyyyyHHmmss").format(generationDate)); //NON_NLS
|
||||
|
||||
final JTextField text = new JTextField(50);
|
||||
final JPanel panel = new JPanel(new GridLayout(2, 1));
|
||||
panel.add(new JLabel(Bundle.VisualizationPane_fileName_prompt()));
|
||||
panel.add(text);
|
||||
|
||||
text.setText(defaultReportName);
|
||||
|
||||
int result = JOptionPane.showConfirmDialog(graphComponent, panel,
|
||||
Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
|
||||
|
||||
if (result == JOptionPane.OK_OPTION) {
|
||||
String enteredReportName = text.getText();
|
||||
|
||||
if(enteredReportName.trim().isEmpty()){
|
||||
result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_accept_defaultName(defaultReportName), Bundle.VisualizationPane_blank_report_title(), JOptionPane.OK_CANCEL_OPTION);
|
||||
if(result != JOptionPane.OK_OPTION) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String reportName = StringUtils.defaultIfBlank(enteredReportName, defaultReportName);
|
||||
Path reportPath = Paths.get(currentCase.getReportDirectory(), reportName);
|
||||
if (Files.exists(reportPath)) {
|
||||
result = JOptionPane.showConfirmDialog(graphComponent, Bundle.VisualizationPane_overrite_exiting(reportName),
|
||||
Bundle.VisualizationPanel_action_dialogs_title(), JOptionPane.OK_CANCEL_OPTION);
|
||||
|
||||
if (result == JOptionPane.OK_OPTION) {
|
||||
FileUtil.deleteFileDir(reportPath.toFile());
|
||||
createReport(currentCase, reportName);
|
||||
}
|
||||
} else {
|
||||
createReport(currentCase, reportName);
|
||||
currentCase.addReport(reportPath.toString(), Bundle.VisualizationPanel_module_name(), reportName);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the Snapshot Report.
|
||||
*
|
||||
* @param currentCase The current case
|
||||
* @param reportName User selected name for the report
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"VisualizationPane_DisplayName=Open Report",
|
||||
"VisualizationPane_NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch.",
|
||||
"VisualizationPane_MessageBoxTitle=Open Report Failure",
|
||||
"VisualizationPane_NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way.",
|
||||
"VisualizationPane_MissingReportFileMessage=The report file no longer exists.",
|
||||
"VisualizationPane_ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied.",
|
||||
"# {0} - report path",
|
||||
"VisualizationPane_Report_Success=Report Successfully create at:\n{0}",
|
||||
"VisualizationPane_Report_OK_Button=OK",
|
||||
"VisualizationPane_Open_Report=Open Report",})
|
||||
private void createReport(Case currentCase, String reportName) throws IOException {
|
||||
|
||||
// Create the report.
|
||||
Path reportFolderPath = Paths.get(currentCase.getReportDirectory(), reportName, Bundle.VisualizationPane_reportName()); //NON_NLS
|
||||
BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, graph.getView().getScale(), Color.WHITE, true, null);
|
||||
Path reportPath = new CommSnapShotReportWriter(currentCase, reportFolderPath, reportName, new Date(), image, currentFilter).writeReport();
|
||||
|
||||
// Report success to the user and offer to open the report.
|
||||
String message = Bundle.VisualizationPane_Report_Success(reportPath.toAbsolutePath());
|
||||
String[] buttons = {Bundle.VisualizationPane_Open_Report(), Bundle.VisualizationPane_Report_OK_Button()};
|
||||
|
||||
int result = JOptionPane.showOptionDialog(graphComponent, message,
|
||||
Bundle.VisualizationPanel_action_dialogs_title(),
|
||||
JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
|
||||
null, buttons, buttons[1]);
|
||||
if (result == JOptionPane.YES_NO_OPTION) {
|
||||
try {
|
||||
Desktop.getDesktop().open(reportPath.toFile());
|
||||
} catch (IOException ex) {
|
||||
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
|
||||
Bundle.VisualizationPane_NoAssociatedEditorMessage(),
|
||||
Bundle.VisualizationPane_MessageBoxTitle(),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
|
||||
Bundle.VisualizationPane_NoOpenInEditorSupportMessage(),
|
||||
Bundle.VisualizationPane_MessageBoxTitle(),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
|
||||
Bundle.VisualizationPane_MissingReportFileMessage(),
|
||||
Bundle.VisualizationPane_MessageBoxTitle(),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
} catch (SecurityException ex) {
|
||||
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
|
||||
Bundle.VisualizationPane_ReportFileOpenPermissionDeniedMessage(),
|
||||
Bundle.VisualizationPane_MessageBoxTitle(),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private JPanel borderLayoutPanel;
|
||||
@ -687,10 +860,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
private JLabel jLabel2;
|
||||
private JToolBar.Separator jSeparator1;
|
||||
private JToolBar.Separator jSeparator2;
|
||||
private JToolBar.Separator jSeparator3;
|
||||
private JTextArea jTextArea1;
|
||||
private JFXPanel notificationsJFXPanel;
|
||||
private JButton organicLayoutButton;
|
||||
private JPanel placeHolderPanel;
|
||||
private JButton snapshotButton;
|
||||
private JSplitPane splitPane;
|
||||
private JPanel toolbar;
|
||||
private JButton zoomActualButton;
|
||||
|
@ -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.communications.snapshot;
|
||||
|
||||
import java.util.List;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
import javax.imageio.ImageIO;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.report.uisnapshot.UiSnapShotReportWriter;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter.SubFilter;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Generate and write the Communication snapshot report to disk.
|
||||
*/
|
||||
public class CommSnapShotReportWriter extends UiSnapShotReportWriter {
|
||||
|
||||
private final BufferedImage image;
|
||||
private final CommunicationsFilter filter;
|
||||
|
||||
/**
|
||||
* 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 generationDate The generation Date of the report.
|
||||
* @param snapshot A snapshot of the view to include in the report.
|
||||
*/
|
||||
public CommSnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, Date generationDate, BufferedImage snapshot, CommunicationsFilter filter) {
|
||||
|
||||
super(currentCase, reportFolderPath, reportName, generationDate);
|
||||
|
||||
this.image = snapshot;
|
||||
this.filter = filter;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and write the html page that shows the snapshot and the state of
|
||||
* the CommunicationFilters
|
||||
*
|
||||
* @throws IOException If there is a problem writing the html file to disk.
|
||||
*/
|
||||
@Override
|
||||
protected void writeSnapShotHTMLFile() throws IOException {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("MMMMM dd, yyyy"); //NON-NLS
|
||||
|
||||
ImageIO.write(image, "png", getReportFolderPath().resolve("snapshot.png").toFile()); //NON-NLS
|
||||
|
||||
//make a map of context objects to resolve template paramaters against
|
||||
HashMap<String, Object> snapShotContext = new HashMap<>();
|
||||
snapShotContext.put("reportTitle", getReportName()); //NON-NLS
|
||||
|
||||
List<SubFilter> filters = filter.getAndFilters();
|
||||
|
||||
for (SubFilter filter : filters) {
|
||||
if (filter instanceof DateRangeFilter) {
|
||||
long startDate = ((DateRangeFilter) filter).getStartDate();
|
||||
long endDate = ((DateRangeFilter) filter).getEndDate();
|
||||
|
||||
if (startDate > 0) {
|
||||
|
||||
snapShotContext.put("startTime", formatter.format(new Date((Instant.ofEpochSecond(startDate)).toEpochMilli()))); //NON-NLS
|
||||
}
|
||||
|
||||
if (endDate > 0) {
|
||||
snapShotContext.put("endTime", formatter.format(new Date((Instant.ofEpochSecond(endDate)).toEpochMilli()))); //NON-NLS
|
||||
}
|
||||
} else if (filter instanceof AccountTypeFilter) {
|
||||
|
||||
Set<Account.Type> selectedAccounts = ((AccountTypeFilter) filter).getAccountTypes();
|
||||
ArrayList<ReportWriterHelper> fullAccountList = new ArrayList<>();
|
||||
for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) {
|
||||
if (type == Account.Type.CREDIT_CARD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fullAccountList.add(new ReportWriterHelper(type.getDisplayName(), selectedAccounts.contains(type)));
|
||||
}
|
||||
|
||||
snapShotContext.put("accounts", fullAccountList);
|
||||
} else if (filter instanceof DeviceFilter) {
|
||||
Collection<String> ids = ((DeviceFilter) filter).getDevices();
|
||||
ArrayList<ReportWriterHelper> list = new ArrayList<>();
|
||||
try {
|
||||
final SleuthkitCase sleuthkitCase = getCurrentCase().getSleuthkitCase();
|
||||
for (DataSource dataSource : sleuthkitCase.getDataSources()) {
|
||||
boolean selected = ids.contains(dataSource.getDeviceId());
|
||||
String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName();
|
||||
list.add(new ReportWriterHelper(dsName, selected));
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
|
||||
}
|
||||
|
||||
snapShotContext.put("devices", list);
|
||||
}
|
||||
}
|
||||
|
||||
fillTemplateAndWrite("/org/sleuthkit/autopsy/communications/snapshot/comm_snapshot_template.html", "Snapshot", snapShotContext, getReportFolderPath().resolve("snapshot.html")); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for use with the html template
|
||||
*/
|
||||
private final class ReportWriterHelper {
|
||||
|
||||
private final String label;
|
||||
private final boolean selected;
|
||||
|
||||
/**
|
||||
* Helper class for use with the html template.
|
||||
*
|
||||
* @param label Display label
|
||||
* @param selected Boolean selected state
|
||||
*/
|
||||
ReportWriterHelper(String label, boolean selected) {
|
||||
this.label = label;
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display label
|
||||
*
|
||||
* @return The display label
|
||||
*/
|
||||
public String getLabel(){
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selection state
|
||||
*
|
||||
* @return The selection state
|
||||
*/
|
||||
public boolean isSelected(){
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Communications Snapshot: {{reportTitle}}</title>
|
||||
<link rel="stylesheet" type="text/css" href="index.css" />
|
||||
<link rel="icon" type="image/ico" href="favicon.ico" />
|
||||
</head>
|
||||
<body><div id="header">Communications Snapshot</div>
|
||||
<div id="content">
|
||||
<img id="snapshot" src="snapshot.png" alt="Snapshot" >
|
||||
<table>
|
||||
<tr><td><b>Date Range</b></td><td></td></tr>
|
||||
<tr><td>Start:</td><td>{{startTime}}</td></tr>
|
||||
<tr><td>End:</td><td>{{endTime}}</td></tr>
|
||||
<tr><td><b>Devices:</b></td><td></td></tr>
|
||||
{{#devices}}<tr><td>{{label}}</td><td><input type="checkbox" {{#selected}}checked{{/selected}}{{^selected}} {{/selected}} disabled></td></tr>{{/devices}}
|
||||
<tr><td><b>Account Types</b></td><td></td></tr>
|
||||
{{#accounts}}<tr><td>{{label}}</td><td><input type="checkbox" {{#selected}}checked{{/selected}}{{^selected}} {{/selected}} disabled></td></tr>{{/accounts}}
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
Core/src/org/sleuthkit/autopsy/report/images/image.png
Executable file
BIN
Core/src/org/sleuthkit/autopsy/report/images/image.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 516 B |
241
Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java
Executable file
241
Core/src/org/sleuthkit/autopsy/report/uisnapshot/UiSnapShotReportWriter.java
Executable file
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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.report.uisnapshot;
|
||||
|
||||
import com.github.mustachejava.DefaultMustacheFactory;
|
||||
import com.github.mustachejava.Mustache;
|
||||
import com.github.mustachejava.MustacheFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.report.ReportBranding;
|
||||
|
||||
/**
|
||||
* Generate and write the snapshot report to disk.
|
||||
*/
|
||||
public abstract class UiSnapShotReportWriter {
|
||||
|
||||
/**
|
||||
* 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 Date generationDate;
|
||||
|
||||
/**
|
||||
* 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 generationDate The generation Date of the report.
|
||||
* @param snapshot A snapshot of the view to include in the
|
||||
* report.
|
||||
*/
|
||||
protected UiSnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, Date generationDate) {
|
||||
this.currentCase = currentCase;
|
||||
this.reportFolderPath = reportFolderPath;
|
||||
this.reportName = reportName;
|
||||
this.generationDate = generationDate;
|
||||
|
||||
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 {
|
||||
//ensure directory exists
|
||||
Files.createDirectories(reportFolderPath);
|
||||
|
||||
copyResources();
|
||||
|
||||
writeSummaryHTML();
|
||||
writeSnapShotHTMLFile();
|
||||
return writeIndexHTML();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name for the report.
|
||||
*
|
||||
* @return Returns the reportName
|
||||
*/
|
||||
protected String getReportName() {
|
||||
return reportName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the folder path for the report.
|
||||
*
|
||||
* @return Report folder path
|
||||
*/
|
||||
protected Path getReportFolderPath() {
|
||||
return reportFolderPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the case for this report.
|
||||
*
|
||||
* @return Current case object
|
||||
*/
|
||||
protected Case getCurrentCase() {
|
||||
return currentCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and write the html page that shows the snapshot and the state of
|
||||
* any filters.
|
||||
*
|
||||
* @throws IOException If there is a problem writing the html file to disk.
|
||||
*/
|
||||
protected abstract void writeSnapShotHTMLFile() throws IOException ;
|
||||
|
||||
/**
|
||||
* 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("reportBranding", reportBranding); //NON-NLS
|
||||
indexContext.put("reportName", reportName); //NON-NLS
|
||||
Path reportIndexFile = reportFolderPath.resolve("index.html"); //NON-NLS
|
||||
|
||||
fillTemplateAndWrite("/org/sleuthkit/autopsy/report/uisnapshot/index_template.html", "Index", indexContext, reportIndexFile); //NON-NLS
|
||||
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); //NON-NLS
|
||||
summaryContext.put("reportBranding", reportBranding); //NON-NLS
|
||||
summaryContext.put("generationDateTime", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(generationDate)); //NON-NLS
|
||||
summaryContext.put("ingestRunning", IngestManager.getInstance().isIngestRunning()); //NON-NLS
|
||||
summaryContext.put("currentCase", currentCase); //NON-NLS
|
||||
String agencyLogo = "agency_logo.png"; //default name for agency logo.
|
||||
if (StringUtils.isNotBlank(reportBranding.getAgencyLogoPath())){
|
||||
agencyLogo = Paths.get(reportBranding.getAgencyLogoPath()).getFileName().toString();
|
||||
}
|
||||
summaryContext.put("agencyLogoFileName", agencyLogo);
|
||||
fillTemplateAndWrite("/org/sleuthkit/autopsy/report/uisnapshot/summary_template.html", "Summary", summaryContext, reportFolderPath.resolve("summary.html")); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
protected void fillTemplateAndWrite(final String templateLocation, final String templateName, Object context, final Path outPutFile) throws IOException {
|
||||
|
||||
Mustache summaryMustache = mf.compile(new InputStreamReader(UiSnapShotReportWriter.class.getResourceAsStream(templateLocation)), templateName);
|
||||
try (Writer writer = Files.newBufferedWriter(outPutFile, Charset.forName("UTF-8"))) { //NON-NLS
|
||||
summaryMustache.execute(writer, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy static resources (static html, css, images, etc) to the reports
|
||||
* folder.
|
||||
*
|
||||
* @throws IOException If there is a problem copying the resources.
|
||||
*/
|
||||
private void copyResources() throws IOException {
|
||||
|
||||
//pull generator and agency logos from branding
|
||||
String generatorLogoPath = reportBranding.getGeneratorLogoPath();
|
||||
if (StringUtils.isNotBlank(generatorLogoPath)) {
|
||||
Files.copy(Files.newInputStream(Paths.get(generatorLogoPath)), reportFolderPath.resolve("generator_logo.png")); //NON-NLS
|
||||
}
|
||||
String agencyLogoPath = reportBranding.getAgencyLogoPath();
|
||||
if (StringUtils.isNotBlank(agencyLogoPath)) {
|
||||
Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve(Paths.get(reportBranding.getAgencyLogoPath()).getFileName())); //NON-NLS
|
||||
}
|
||||
|
||||
//copy favicon
|
||||
if (StringUtils.isBlank(agencyLogoPath)) {
|
||||
copyInternalResource("/org/sleuthkit/autopsy/report/images/favicon.ico", "favicon.ico");
|
||||
} else {
|
||||
Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve("favicon.ico")); //NON-NLS
|
||||
}
|
||||
|
||||
copyInternalResource("/org/sleuthkit/autopsy/report/uisnapshot/navigation.html", "nav.html");
|
||||
copyInternalResource("/org/sleuthkit/autopsy/report/images/summary.png", "summary.png");
|
||||
copyInternalResource("/org/sleuthkit/autopsy/report/images/image.png", "snapshot_icon.png");
|
||||
copyInternalResource("/org/sleuthkit/autopsy/report/uisnapshot/index.css", "index.css");
|
||||
copyInternalResource("/org/sleuthkit/autopsy/report/uisnapshot/summary.css", "summary.css");
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies internal resource to the report folder.
|
||||
*
|
||||
* @param internalPath Location in jar of the image
|
||||
* @param fileName Name to give resource in new location
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void copyInternalResource(String internalPath, String fileName) throws IOException{
|
||||
try (InputStream resource = UiSnapShotReportWriter.class.getResourceAsStream(internalPath)) { //NON-NLS
|
||||
Files.copy(resource, reportFolderPath.resolve(fileName)); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
0
Core/src/org/sleuthkit/autopsy/timeline/snapshot/index.css → Core/src/org/sleuthkit/autopsy/report/uisnapshot/index.css
Normal file → Executable file
0
Core/src/org/sleuthkit/autopsy/timeline/snapshot/index.css → Core/src/org/sleuthkit/autopsy/report/uisnapshot/index.css
Normal file → Executable file
@ -9,7 +9,7 @@
|
||||
<h1>Report Navigation</h1>
|
||||
<ul class="nav">
|
||||
<li style="background: url(summary.png) left center no-repeat;"><a href="summary.html" target="content">Case Summary</a></li>
|
||||
<li style="background: url(snapshot_icon.png) left center no-repeat;"><a href="snapshot.html" target="content">Timeline Snapshot</a></li>
|
||||
<li style="background: url(snapshot_icon.png) left center no-repeat;"><a href="snapshot.html" target="content">Snapshot</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
0
Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary.css → Core/src/org/sleuthkit/autopsy/report/uisnapshot/summary.css
Normal file → Executable file
0
Core/src/org/sleuthkit/autopsy/timeline/snapshot/summary.css → Core/src/org/sleuthkit/autopsy/report/uisnapshot/summary.css
Normal file → Executable file
@ -9,7 +9,7 @@
|
||||
<div id="wrapper">
|
||||
<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>
|
||||
<p class="subheadding">Report generated on {{generationDateTime}}</p>
|
||||
<div class="title">
|
||||
{{#reportBranding.getAgencyLogoPath}}
|
||||
<div class="left">
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2016 Basis Technology Corp.
|
||||
* Copyright 2016 - 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,46 +18,23 @@
|
||||
*/
|
||||
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;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import javax.imageio.ImageIO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.report.ReportBranding;
|
||||
import org.sleuthkit.autopsy.report.uisnapshot.UiSnapShotReportWriter;
|
||||
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;
|
||||
public class SnapShotReportWriter extends UiSnapShotReportWriter{
|
||||
|
||||
private final ZoomParams zoomParams;
|
||||
private final Date generationDate;
|
||||
private final BufferedImage image;
|
||||
|
||||
/**
|
||||
@ -74,37 +51,9 @@ public class SnapShotReportWriter {
|
||||
* report.
|
||||
*/
|
||||
public SnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, ZoomParams zoomParams, Date generationDate, BufferedImage snapshot) {
|
||||
this.currentCase = currentCase;
|
||||
this.reportFolderPath = reportFolderPath;
|
||||
this.reportName = reportName;
|
||||
super(currentCase, reportFolderPath, reportName, generationDate);
|
||||
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 {
|
||||
//ensure directory exists
|
||||
Files.createDirectories(reportFolderPath);
|
||||
|
||||
//save the snapshot in the report directory
|
||||
ImageIO.write(image, "png", reportFolderPath.resolve("snapshot.png").toFile()); //NON-NLS
|
||||
|
||||
copyResources();
|
||||
|
||||
writeSummaryHTML();
|
||||
writeSnapShotHTMLFile();
|
||||
return writeIndexHTML();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,128 +62,18 @@ public class SnapShotReportWriter {
|
||||
*
|
||||
* @throws IOException If there is a problem writing the html file to disk.
|
||||
*/
|
||||
private void writeSnapShotHTMLFile() throws IOException {
|
||||
@Override
|
||||
protected void writeSnapShotHTMLFile() throws IOException {
|
||||
//save the snapshot in the report directory
|
||||
ImageIO.write(image, "png", getReportFolderPath().resolve("snapshot.png").toFile()); //NON-NLS
|
||||
|
||||
//make a map of context objects to resolve template paramaters against
|
||||
HashMap<String, Object> snapShotContext = new HashMap<>();
|
||||
snapShotContext.put("reportTitle", reportName); //NON-NLS
|
||||
snapShotContext.put("reportTitle", getReportName()); //NON-NLS
|
||||
snapShotContext.put("startTime", zoomParams.getTimeRange().getStart().toString(DateTimeFormat.fullDateTime())); //NON-NLS
|
||||
snapShotContext.put("endTime", zoomParams.getTimeRange().getEnd().toString(DateTimeFormat.fullDateTime())); //NON-NLS
|
||||
snapShotContext.put("zoomParams", zoomParams); //NON-NLS
|
||||
|
||||
fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/snapshot_template.html", "Snapshot", snapShotContext, reportFolderPath.resolve("snapshot.html")); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* 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("reportBranding", reportBranding); //NON-NLS
|
||||
indexContext.put("reportName", reportName); //NON-NLS
|
||||
Path reportIndexFile = reportFolderPath.resolve("index.html"); //NON-NLS
|
||||
|
||||
fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/index_template.html", "Index", indexContext, reportIndexFile); //NON-NLS
|
||||
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); //NON-NLS
|
||||
summaryContext.put("reportBranding", reportBranding); //NON-NLS
|
||||
summaryContext.put("generationDateTime", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(generationDate)); //NON-NLS
|
||||
summaryContext.put("ingestRunning", IngestManager.getInstance().isIngestRunning()); //NON-NLS
|
||||
summaryContext.put("currentCase", currentCase); //NON-NLS
|
||||
String agencyLogo = "agency_logo.png"; //default name for agency logo.
|
||||
if (StringUtils.isNotBlank(reportBranding.getAgencyLogoPath())){
|
||||
agencyLogo = Paths.get(reportBranding.getAgencyLogoPath()).getFileName().toString();
|
||||
}
|
||||
summaryContext.put("agencyLogoFileName", agencyLogo);
|
||||
fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/summary_template.html", "Summary", summaryContext, reportFolderPath.resolve("summary.html")); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* 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"))) { //NON-NLS
|
||||
summaryMustache.execute(writer, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy static resources (static html, css, images, etc) to the reports
|
||||
* folder.
|
||||
*
|
||||
* @throws IOException If there is a problem copying the resources.
|
||||
*/
|
||||
private void copyResources() throws IOException {
|
||||
|
||||
//pull generator and agency logos from branding
|
||||
String generatorLogoPath = reportBranding.getGeneratorLogoPath();
|
||||
if (StringUtils.isNotBlank(generatorLogoPath)) {
|
||||
Files.copy(Files.newInputStream(Paths.get(generatorLogoPath)), reportFolderPath.resolve("generator_logo.png")); //NON-NLS
|
||||
}
|
||||
String agencyLogoPath = reportBranding.getAgencyLogoPath();
|
||||
if (StringUtils.isNotBlank(agencyLogoPath)) {
|
||||
Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve(Paths.get(reportBranding.getAgencyLogoPath()).getFileName())); //NON-NLS
|
||||
}
|
||||
|
||||
//copy navigation html
|
||||
try (InputStream navStream = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/snapshot/navigation.html")) { //NON-NLS
|
||||
Files.copy(navStream, reportFolderPath.resolve("nav.html")); //NON-NLS
|
||||
}
|
||||
//copy favicon
|
||||
if (StringUtils.isBlank(agencyLogoPath)) {
|
||||
// use default Autopsy icon if custom icon is not set
|
||||
try (InputStream faviconStream = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/favicon.ico")) { //NON-NLS
|
||||
Files.copy(faviconStream, reportFolderPath.resolve("favicon.ico")); //NON-NLS
|
||||
}
|
||||
} else {
|
||||
Files.copy(Files.newInputStream(Paths.get(agencyLogoPath)), reportFolderPath.resolve("favicon.ico")); //NON-NLS
|
||||
}
|
||||
|
||||
//copy report summary icon
|
||||
try (InputStream summaryStream = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/report/images/summary.png")) { //NON-NLS
|
||||
Files.copy(summaryStream, reportFolderPath.resolve("summary.png")); //NON-NLS
|
||||
}
|
||||
//copy snapshot icon
|
||||
try (InputStream snapshotIconStream = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/images/image.png")) { //NON-NLS
|
||||
Files.copy(snapshotIconStream, reportFolderPath.resolve("snapshot_icon.png")); //NON-NLS
|
||||
}
|
||||
//copy main report css
|
||||
try (InputStream resource = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/snapshot/index.css")) { //NON-NLS
|
||||
Files.copy(resource, reportFolderPath.resolve("index.css")); //NON-NLS
|
||||
}
|
||||
//copy summary css
|
||||
try (InputStream resource = SnapShotReportWriter.class.getResourceAsStream("/org/sleuthkit/autopsy/timeline/snapshot/summary.css")) { //NON-NLS
|
||||
Files.copy(resource, reportFolderPath.resolve("summary.css")); //NON-NLS
|
||||
}
|
||||
fillTemplateAndWrite("/org/sleuthkit/autopsy/timeline/snapshot/snapshot_template.html", "Snapshot", snapShotContext, getReportFolderPath().resolve("snapshot.html")); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,8 @@ KeywordSearchIngestModule.startupMessage.failedToGetIndexSchema=Failed to get sc
|
||||
KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found.
|
||||
KeywordSearchResultFactory.query.exception.msg=Could not perform the query
|
||||
OpenIDE-Module-Display-Category=Ingest Module
|
||||
OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\n\The module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found.
|
||||
|
||||
OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\nThe module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found.
|
||||
OpenIDE-Module-Name=KeywordSearch
|
||||
OptionsCategory_Name_KeywordSearchOptions=Keyword Search
|
||||
OptionsCategory_Keywords_KeywordSearchOptions=Keyword Search
|
||||
|
Loading…
x
Reference in New Issue
Block a user