Merge remote-tracking branch 'upstream/develop' into version-number-updates

This commit is contained in:
rcordovano 2021-01-19 09:51:47 -05:00
commit 5af07ed9d2
122 changed files with 1939 additions and 1133 deletions

View File

@ -52,8 +52,8 @@ Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}.
Case.exceptionMessage.metadataUpdateError=Failed to update case metadata Case.exceptionMessage.metadataUpdateError=Failed to update case metadata
# {0} - exception message # {0} - exception message
Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}. Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.
Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a exclusive lock on the case. Case.lockingException.couldNotAcquireExclusiveLock=Failed to get an exclusive lock on the case.
Case.lockingException.couldNotAcquireSharedLock=Failed to get an shared lock on the case. Case.lockingException.couldNotAcquireSharedLock=Failed to get a shared lock on the case.
Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User. Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User.
# {0} - image # {0} - image
Case.openFileSystems.openingImage=Opening all filesystems for image: {0}... Case.openFileSystems.openingImage=Opening all filesystems for image: {0}...

View File

@ -2828,8 +2828,8 @@ public class Case {
* @throws CaseActionException If the lock cannot be acquired. * @throws CaseActionException If the lock cannot be acquired.
*/ */
@Messages({ @Messages({
"Case.lockingException.couldNotAcquireSharedLock=Failed to get an shared lock on the case.", "Case.lockingException.couldNotAcquireSharedLock=Failed to get a shared lock on the case.",
"Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a exclusive lock on the case." "Case.lockingException.couldNotAcquireExclusiveLock=Failed to get an exclusive lock on the case."
}) })
private void acquireCaseLock(CaseLockType lockType) throws CaseActionException { private void acquireCaseLock(CaseLockType lockType) throws CaseActionException {
String caseDir = metadata.getCaseDirectory(); String caseDir = metadata.getCaseDirectory();

View File

@ -39,9 +39,9 @@ GlobalSettingsPanel.askForCentralRepoDbChoice.sqliteChoice.text=Use SQLite
GlobalSettingsPanel.onMultiUserChange.disabledMu.description=The Central Repository will be reconfigured to use a local SQLite database. GlobalSettingsPanel.onMultiUserChange.disabledMu.description=The Central Repository will be reconfigured to use a local SQLite database.
GlobalSettingsPanel.onMultiUserChange.disabledMu.description2=Press Configure PostgreSQL to change to a PostgreSQL database. GlobalSettingsPanel.onMultiUserChange.disabledMu.description2=Press Configure PostgreSQL to change to a PostgreSQL database.
GlobalSettingsPanel.onMultiUserChange.disabledMu.title=Central Repository Change Necessary GlobalSettingsPanel.onMultiUserChange.disabledMu.title=Central Repository Change Necessary
GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL database? GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL server?
GlobalSettingsPanel.onMultiUserChange.enable.description2=The Central Repository stores hash values and accounts from past cases. GlobalSettingsPanel.onMultiUserChange.enable.description2=Any data in an existing SQLite Central Repository will not be transferred to the new database.
GlobalSettingsPanel.onMultiUserChange.enable.title=Use with Central Repository? GlobalSettingsPanel.onMultiUserChange.enable.title=Central Repository
GlobalSettingsPanel.testCurrentConfiguration.dbDoesNotExist.message=Database does not exist. GlobalSettingsPanel.testCurrentConfiguration.dbDoesNotExist.message=Database does not exist.
GlobalSettingsPanel.validationErrMsg.ingestRunning=You cannot change settings while ingest is running. GlobalSettingsPanel.validationErrMsg.ingestRunning=You cannot change settings while ingest is running.
GlobalSettingsPanel.validationerrMsg.mustConfigure=Configure the database to enable this module. GlobalSettingsPanel.validationerrMsg.mustConfigure=Configure the database to enable this module.

View File

@ -151,9 +151,9 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
* as of most recent change. * as of most recent change.
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"GlobalSettingsPanel.onMultiUserChange.enable.title=Use with Central Repository?", "GlobalSettingsPanel.onMultiUserChange.enable.title=Central Repository",
"GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL database?", "GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL server?",
"GlobalSettingsPanel.onMultiUserChange.enable.description2=The Central Repository stores hash values and accounts from past cases." "GlobalSettingsPanel.onMultiUserChange.enable.description2=Any data in an existing SQLite Central Repository will not be transferred to the new database."
}) })
public static void onMultiUserChange(Component parent, boolean muPreviouslySelected, boolean muCurrentlySelected) { public static void onMultiUserChange(Component parent, boolean muPreviouslySelected, boolean muCurrentlySelected) {
boolean crEnabled = CentralRepoDbUtil.allowUseOfCentralRepository(); boolean crEnabled = CentralRepoDbUtil.allowUseOfCentralRepository();

View File

@ -82,9 +82,9 @@ final public class CVTPersonaCache {
} }
/** /**
* Returns the list of PersonaAccounts for the given Account typeSpecificId. * Returns the list of PersonaAccounts for the given Account.
* *
* @param typeSpecificID Account typeSpecificId. * @param account The account.
* *
* @return List of PersonaAccounts for id or empty list if none were found. * @return List of PersonaAccounts for id or empty list if none were found.
* *

View File

@ -11,30 +11,32 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> <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,0,0,0,0,0"/>
</AuxValues> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/> <Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="detailsScrollPane" alignment="1" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="detailsScrollPane" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents> <SubComponents>
<Container class="javax.swing.JPanel" name="detailsPanel"> <Container class="javax.swing.JScrollPane" name="detailsScrollPane">
<Constraints> <Properties>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<BorderConstraints direction="First"/> <Dimension value="[300, 0]"/>
</Constraint> </Property>
</Constraints> </Properties>
<Layout> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
</Container> </Container>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -37,7 +37,6 @@ import java.util.logging.Level;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextPane; import javax.swing.JTextPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
@ -82,6 +81,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
private final GridBagLayout gridBagLayout = new GridBagLayout(); private final GridBagLayout gridBagLayout = new GridBagLayout();
private final GridBagConstraints gridBagConstraints = new GridBagConstraints(); private final GridBagConstraints gridBagConstraints = new GridBagConstraints();
private final Map<Integer, Integer[]> orderingMap = new HashMap<>(); private final Map<Integer, Integer[]> orderingMap = new HashMap<>();
private final javax.swing.JPanel detailsPanel = new javax.swing.JPanel();
/** /**
* Creates new form GeneralPurposeArtifactViewer. * Creates new form GeneralPurposeArtifactViewer.
@ -90,6 +90,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
public GeneralPurposeArtifactViewer() { public GeneralPurposeArtifactViewer() {
addOrderings(); addOrderings();
initComponents(); initComponents();
gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START;
detailsPanel.setLayout(gridBagLayout); detailsPanel.setLayout(gridBagLayout);
} }
@ -161,8 +162,9 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
} }
updateView(artifact, attributeMap, dataSourceName, sourceFileName); updateView(artifact, attributeMap, dataSourceName, sourceFileName);
} }
detailsScrollPane.setViewportView(detailsPanel);
detailsScrollPane.revalidate();
revalidate(); revalidate();
repaint();
} }
/** /**
@ -172,7 +174,8 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
private void resetComponent() { private void resetComponent() {
// clear the panel // clear the panel
detailsPanel.removeAll(); detailsPanel.removeAll();
gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START; detailsPanel.setLayout(gridBagLayout);
detailsPanel.revalidate();
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
gridBagConstraints.gridx = LABEL_COLUMN; gridBagConstraints.gridx = LABEL_COLUMN;
gridBagConstraints.weighty = 0.0; gridBagConstraints.weighty = 0.0;
@ -183,14 +186,6 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
public Component getComponent() {
// Slap a vertical scrollbar on the panel
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public boolean isSupported(BlackboardArtifact artifact) { public boolean isSupported(BlackboardArtifact artifact) {
return (artifact != null) return (artifact != null)
&& (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID() && (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()
@ -218,22 +213,22 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() { private void initComponents() {
detailsPanel = new javax.swing.JPanel(); detailsScrollPane = new javax.swing.JScrollPane();
setLayout(new java.awt.BorderLayout()); detailsScrollPane.setPreferredSize(new java.awt.Dimension(300, 0));
javax.swing.GroupLayout detailsPanelLayout = new javax.swing.GroupLayout(detailsPanel); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
detailsPanel.setLayout(detailsPanelLayout); this.setLayout(layout);
detailsPanelLayout.setHorizontalGroup( layout.setHorizontalGroup(
detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE) .addComponent(detailsScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
); );
detailsPanelLayout.setVerticalGroup( layout.setVerticalGroup(
detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup()
.addComponent(detailsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(0, 0, 0))
); );
add(detailsPanel, java.awt.BorderLayout.PAGE_START);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
/** /**
@ -243,7 +238,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
* @param attributeMap The map of attributes that exist for the artifact. * @param attributeMap The map of attributes that exist for the artifact.
* @param dataSourceName The name of the datasource that caused the creation * @param dataSourceName The name of the datasource that caused the creation
* of the artifact. * of the artifact.
* @param sourceFileName The name of the file that caused the creation of * @param sourceFilePath The path of the file that caused the creation of
* the artifact. * the artifact.
*/ */
@NbBundle.Messages({"GeneralPurposeArtifactViewer.dates.created=Created", @NbBundle.Messages({"GeneralPurposeArtifactViewer.dates.created=Created",
@ -257,7 +252,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
private void updateView(BlackboardArtifact artifact, Map<Integer, List<BlackboardAttribute>> attributeMap, String dataSourceName, String sourceFilePath) { private void updateView(BlackboardArtifact artifact, Map<Integer, List<BlackboardAttribute>> attributeMap, String dataSourceName, String sourceFilePath) {
final Integer artifactTypeId = artifact.getArtifactTypeID(); final Integer artifactTypeId = artifact.getArtifactTypeID();
if (!(artifactTypeId < 1 || artifactTypeId >= Integer.MAX_VALUE)) { if (!(artifactTypeId < 1 || artifactTypeId >= Integer.MAX_VALUE)) {
addDetailsHeader(artifactTypeId); JTextPane firstTextPane = addDetailsHeader(artifactTypeId);
Integer[] orderingArray = orderingMap.get(artifactTypeId); Integer[] orderingArray = orderingMap.get(artifactTypeId);
if (orderingArray == null) { if (orderingArray == null) {
orderingArray = DEFAULT_ORDERING; orderingArray = DEFAULT_ORDERING;
@ -282,7 +277,6 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
addNameValueRow(bba.getAttributeType().getDisplayName(), displayString); addNameValueRow(bba.getAttributeType().getDisplayName(), displayString);
} else { } else {
addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString()); addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString());
} }
} }
} }
@ -311,7 +305,11 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
addNameValueRow(Bundle.GeneralPurposeArtifactViewer_details_file(), sourceFilePath); addNameValueRow(Bundle.GeneralPurposeArtifactViewer_details_file(), sourceFilePath);
// add veritcal glue at the end // add veritcal glue at the end
addPageEndGlue(); addPageEndGlue();
if (firstTextPane != null) {
firstTextPane.setCaretPosition(0);
}
} }
detailsPanel.revalidate();
} }
/** /**
@ -353,7 +351,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
"GeneralPurposeArtifactViewer.details.searchHeader=Web Search", "GeneralPurposeArtifactViewer.details.searchHeader=Web Search",
"GeneralPurposeArtifactViewer.details.cachedHeader=Cached File", "GeneralPurposeArtifactViewer.details.cachedHeader=Cached File",
"GeneralPurposeArtifactViewer.details.cookieHeader=Cookie Details",}) "GeneralPurposeArtifactViewer.details.cookieHeader=Cookie Details",})
private void addDetailsHeader(int artifactTypeId) { private JTextPane addDetailsHeader(int artifactTypeId) {
String header; String header;
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) { if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) {
header = Bundle.GeneralPurposeArtifactViewer_details_historyHeader(); header = Bundle.GeneralPurposeArtifactViewer_details_historyHeader();
@ -370,7 +368,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
} else { } else {
header = Bundle.GeneralPurposeArtifactViewer_details_attrHeader(); header = Bundle.GeneralPurposeArtifactViewer_details_attrHeader();
} }
addHeader(header); return addHeader(header);
} }
/** /**
@ -381,16 +379,21 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
* @return JLabel Heading label added. * @return JLabel Heading label added.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private JLabel addHeader(String headerString) { private JTextPane addHeader(String headerString) {
// create label for heading // create label for heading
javax.swing.JLabel headingLabel = new javax.swing.JLabel(); javax.swing.JTextPane headingLabel = new javax.swing.JTextPane();
headingLabel.setOpaque(false);
headingLabel.setFocusable(false);
headingLabel.setEditable(false);
// add a blank line before the start of new section, unless it's // add a blank line before the start of new section, unless it's
// the first section // the first section
if (gridBagConstraints.gridy != 0) { if (gridBagConstraints.gridy != 0) {
gridBagConstraints.gridy++; gridBagConstraints.gridy++;
detailsPanel.add(new javax.swing.JLabel(" "), gridBagConstraints); // add to panel
addToPanel(new javax.swing.JLabel(" "));
addLineEndGlue(); addLineEndGlue();
} headingLabel.setFocusable(false);
}
gridBagConstraints.gridy++; gridBagConstraints.gridy++;
gridBagConstraints.gridx = LABEL_COLUMN;; gridBagConstraints.gridx = LABEL_COLUMN;;
// let the header span all of the row // let the header span all of the row
@ -401,7 +404,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
// make it large and bold // make it large and bold
headingLabel.setFont(headingLabel.getFont().deriveFont(Font.BOLD, headingLabel.getFont().getSize() + 2)); headingLabel.setFont(headingLabel.getFont().deriveFont(Font.BOLD, headingLabel.getFont().getSize() + 2));
// add to panel // add to panel
detailsPanel.add(headingLabel, gridBagConstraints); addToPanel(headingLabel);
// reset constraints to normal // reset constraints to normal
gridBagConstraints.gridwidth = LABEL_WIDTH; gridBagConstraints.gridwidth = LABEL_WIDTH;
// add line end glue // add line end glue
@ -417,9 +420,9 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
* @param keyString Key name to display. * @param keyString Key name to display.
* @param valueString Value string to display. * @param valueString Value string to display.
*/ */
private void addNameValueRow(String keyString, String valueString) { private JTextPane addNameValueRow(String keyString, String valueString) {
addKeyAtCol(keyString); addKeyAtCol(keyString);
addValueAtCol(valueString); return addValueAtCol(valueString);
} }
/** /**
@ -432,7 +435,8 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
gridBagConstraints.weightx = GLUE_WEIGHT_X; // take up all the horizontal space gridBagConstraints.weightx = GLUE_WEIGHT_X; // take up all the horizontal space
gridBagConstraints.fill = GridBagConstraints.BOTH; gridBagConstraints.fill = GridBagConstraints.BOTH;
javax.swing.Box.Filler horizontalFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(32767, 0)); javax.swing.Box.Filler horizontalFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(32767, 0));
detailsPanel.add(horizontalFiller, gridBagConstraints); // add to panel
addToPanel(horizontalFiller);
// restore fill & weight // restore fill & weight
gridBagConstraints.fill = GridBagConstraints.NONE; gridBagConstraints.fill = GridBagConstraints.NONE;
gridBagConstraints.weightx = TEXT_WEIGHT_X; gridBagConstraints.weightx = TEXT_WEIGHT_X;
@ -446,7 +450,8 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
gridBagConstraints.weighty = 1.0; // take up all the vertical space gridBagConstraints.weighty = 1.0; // take up all the vertical space
gridBagConstraints.fill = GridBagConstraints.VERTICAL; gridBagConstraints.fill = GridBagConstraints.VERTICAL;
javax.swing.Box.Filler vertFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(0, 32767)); javax.swing.Box.Filler vertFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(0, 32767));
detailsPanel.add(vertFiller, gridBagConstraints); // add to panel
addToPanel(vertFiller);
} }
/** /**
@ -459,16 +464,22 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
private JLabel addKeyAtCol(String keyString) { private JLabel addKeyAtCol(String keyString) {
// create label // create label
javax.swing.JLabel keyLabel = new javax.swing.JLabel(); javax.swing.JLabel keyLabel = new javax.swing.JLabel();
keyLabel.setFocusable(false);
gridBagConstraints.gridy++; gridBagConstraints.gridy++;
gridBagConstraints.gridx = LABEL_COLUMN; gridBagConstraints.gridx = LABEL_COLUMN;
gridBagConstraints.gridwidth = LABEL_WIDTH; gridBagConstraints.gridwidth = LABEL_WIDTH;
// set text // set text
keyLabel.setText(keyString + ": "); keyLabel.setText(keyString + ": ");
// add to panel // add to panel
detailsPanel.add(keyLabel, gridBagConstraints); addToPanel(keyLabel);
return keyLabel; return keyLabel;
} }
private void addToPanel(Component comp) {
detailsPanel.add(comp, gridBagConstraints);
detailsPanel.revalidate();
}
/** /**
* Adds a value string to the panel at specified column. * Adds a value string to the panel at specified column.
* *
@ -479,6 +490,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
private JTextPane addValueAtCol(String valueString) { private JTextPane addValueAtCol(String valueString) {
// create label, // create label,
JTextPane valueField = new JTextPane(); JTextPane valueField = new JTextPane();
valueField.setFocusable(false);
valueField.setEditable(false); valueField.setEditable(false);
valueField.setOpaque(false); valueField.setOpaque(false);
gridBagConstraints.gridx = VALUE_COLUMN; gridBagConstraints.gridx = VALUE_COLUMN;
@ -488,8 +500,6 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
cloneConstraints.fill = GridBagConstraints.BOTH; cloneConstraints.fill = GridBagConstraints.BOTH;
// set text // set text
valueField.setText(valueString); valueField.setText(valueString);
// scroll to start of text
valueField.setCaretPosition(0);
// attach a right click menu with Copy option // attach a right click menu with Copy option
valueField.addMouseListener(new java.awt.event.MouseAdapter() { valueField.addMouseListener(new java.awt.event.MouseAdapter() {
@Override @Override
@ -497,8 +507,9 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
valueLabelMouseClicked(evt, valueField); valueLabelMouseClicked(evt, valueField);
} }
}); });
// add label to panel // add label to panel with cloned contraintsF
detailsPanel.add(valueField, cloneConstraints); detailsPanel.add(valueField, cloneConstraints);
revalidate();
// end the line // end the line
addLineEndGlue(); addLineEndGlue();
return valueField; return valueField;
@ -531,6 +542,6 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel detailsPanel; private javax.swing.JScrollPane detailsScrollPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -586,6 +586,7 @@ final class MessageAccountPanel extends JPanel {
// Set up each matching account. We don't know what type of account we have, so check all the types to // Set up each matching account. We don't know what type of account we have, so check all the types to
// find any matches. // find any matches.
try { try {
boolean showErrorMessage = true;
for (CentralRepoAccount.CentralRepoAccountType type : CentralRepository.getInstance().getAllAccountTypes()) { for (CentralRepoAccount.CentralRepoAccountType type : CentralRepository.getInstance().getAllAccountTypes()) {
try { try {
// Try to load any matching accounts of this type. Throws an InvalidAccountIDException if the account is the // Try to load any matching accounts of this type. Throws an InvalidAccountIDException if the account is the
@ -593,13 +594,15 @@ final class MessageAccountPanel extends JPanel {
CentralRepoAccount account = CentralRepository.getInstance().getAccount(type, accountContainer.getAccount().getTypeSpecificID()); CentralRepoAccount account = CentralRepository.getInstance().getAccount(type, accountContainer.getAccount().getTypeSpecificID());
if (account != null) { if (account != null) {
personaPanel.addAccount(account, Bundle.MessageAccountPanel_account_justification(), Persona.Confidence.HIGH); personaPanel.addAccount(account, Bundle.MessageAccountPanel_account_justification(), Persona.Confidence.HIGH);
} else { showErrorMessage = false;
createPersonaDialog.setStartupPopupMessage(Bundle.MessageAccountPanel_id_not_found_in_cr(accountContainer.getAccount().getTypeSpecificID())); }
}
} catch (InvalidAccountIDException ex2) { } catch (InvalidAccountIDException ex2) {
// These are expected when the account identifier doesn't match the format of the account type. // These are expected when the account identifier doesn't match the format of the account type.
} }
} }
if(showErrorMessage) {
createPersonaDialog.setStartupPopupMessage(Bundle.MessageAccountPanel_id_not_found_in_cr(accountContainer.getAccount().getTypeSpecificID()));
}
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error looking up account types in the central repository", ex); logger.log(Level.SEVERE, "Error looking up account types in the central repository", ex);
} }

View File

@ -200,6 +200,7 @@ class PersonaAccountFetcher extends SwingWorker<Map<String, Collection<Persona>>
// Set up each matching account. We don't know what type of account we have, so check all the types to // Set up each matching account. We don't know what type of account we have, so check all the types to
// find any matches. // find any matches.
try { try {
boolean showErrorMessage = true;
for (CentralRepoAccount.CentralRepoAccountType type : CentralRepository.getInstance().getAllAccountTypes()) { for (CentralRepoAccount.CentralRepoAccountType type : CentralRepository.getInstance().getAllAccountTypes()) {
try { try {
// Try to load any matching accounts of this type. Throws an InvalidAccountIDException if the account is the // Try to load any matching accounts of this type. Throws an InvalidAccountIDException if the account is the
@ -207,16 +208,17 @@ class PersonaAccountFetcher extends SwingWorker<Map<String, Collection<Persona>>
CentralRepoAccount account = CentralRepository.getInstance().getAccount(type, personaSearcherData.getAccountIdentifer()); CentralRepoAccount account = CentralRepository.getInstance().getAccount(type, personaSearcherData.getAccountIdentifer());
if (account != null) { if (account != null) {
personaPanel.addAccount(account, Bundle.PersonaAccountFetcher_account_justification(), Persona.Confidence.HIGH); personaPanel.addAccount(account, Bundle.PersonaAccountFetcher_account_justification(), Persona.Confidence.HIGH);
showErrorMessage = false;
} }
if((personaSearcherData.getAccountIdentifer() != null &&
!personaSearcherData.getAccountIdentifer().isEmpty()) && account == null) {
dialog.setStartupPopupMessage(Bundle.PersonaAccountFetcher_not_account_in_cr(personaSearcherData.getAccountIdentifer()));
}
} catch (InvalidAccountIDException ex2) { } catch (InvalidAccountIDException ex2) {
// These are expected when the account identifier doesn't match the format of the account type. // These are expected when the account identifier doesn't match the format of the account type.
} }
} }
if ((personaSearcherData.getAccountIdentifer() != null
&& !personaSearcherData.getAccountIdentifer().isEmpty()) && showErrorMessage) {
dialog.setStartupPopupMessage(Bundle.PersonaAccountFetcher_not_account_in_cr(personaSearcherData.getAccountIdentifer()));
}
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error looking up account types in the central repository", ex); logger.log(Level.SEVERE, "Error looking up account types in the central repository", ex);
} }

View File

@ -25,6 +25,7 @@ import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.TextViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.TextViewer;
import org.sleuthkit.autopsy.corecomponents.DataContentViewerUtility; import org.sleuthkit.autopsy.corecomponents.DataContentViewerUtility;
import org.sleuthkit.autopsy.datamodel.StringContent; import org.sleuthkit.autopsy.datamodel.StringContent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
/** /**
@ -81,7 +82,7 @@ public class StringsTextViewer implements TextViewer {
return false; return false;
} }
Content content = DataContentViewerUtility.getDefaultContent(node); Content content = DataContentViewerUtility.getDefaultContent(node);
return (content != null && content.getSize() > 0); return (content != null && !(content instanceof BlackboardArtifact) && content.getSize() > 0);
} }
@Override @Override

View File

@ -84,6 +84,7 @@ public final class UserPreferences {
private static final boolean DISPLAY_TRANSLATED_NAMES_DEFAULT = true; private static final boolean DISPLAY_TRANSLATED_NAMES_DEFAULT = true;
public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath"; public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath";
public static final String SOLR_MAX_JVM_SIZE = "SolrMaxJVMSize"; public static final String SOLR_MAX_JVM_SIZE = "SolrMaxJVMSize";
private static final int DEFAULT_SOLR_HEAP_SIZE_MB = 2048;
public static final String RESULTS_TABLE_PAGE_SIZE = "ResultsTablePageSize"; public static final String RESULTS_TABLE_PAGE_SIZE = "ResultsTablePageSize";
private static final String GEO_TILE_OPTION = "GeolocationTileOption"; private static final String GEO_TILE_OPTION = "GeolocationTileOption";
private static final String GEO_OSM_TILE_ZIP_PATH = "GeolocationOsmZipPath"; private static final String GEO_OSM_TILE_ZIP_PATH = "GeolocationOsmZipPath";
@ -535,10 +536,10 @@ public final class UserPreferences {
/** /**
* Get the maximum JVM heap size (in MB) for the embedded Solr server. * Get the maximum JVM heap size (in MB) for the embedded Solr server.
* *
* @return Saved value or default (512) * @return Saved value or default (2 GB)
*/ */
public static int getMaxSolrVMSize() { public static int getMaxSolrVMSize() {
return preferences.getInt(SOLR_MAX_JVM_SIZE, 512); return preferences.getInt(SOLR_MAX_JVM_SIZE, DEFAULT_SOLR_HEAP_SIZE_MB);
} }
/** /**

View File

@ -82,6 +82,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private static final String CONFIG_FILE_EXTENSION = ".conf"; private static final String CONFIG_FILE_EXTENSION = ".conf";
private static final long ONE_BILLION = 1000000000L; //used to roughly convert system memory from bytes to gigabytes private static final long ONE_BILLION = 1000000000L; //used to roughly convert system memory from bytes to gigabytes
private static final int MEGA_IN_GIGA = 1024; //used to convert memory settings saved as megabytes to gigabytes private static final int MEGA_IN_GIGA = 1024; //used to convert memory settings saved as megabytes to gigabytes
private static final int DEFAULT_SOLR_HEAP_SIZE_MB = 2048;
private static final int MIN_MEMORY_IN_GB = 2; //the enforced minimum memory in gigabytes private static final int MIN_MEMORY_IN_GB = 2; //the enforced minimum memory in gigabytes
private static final Logger logger = Logger.getLogger(AutopsyOptionsPanel.class.getName()); private static final Logger logger = Logger.getLogger(AutopsyOptionsPanel.class.getName());
private String initialMemValue = Long.toString(Runtime.getRuntime().maxMemory() / ONE_BILLION); private String initialMemValue = Long.toString(Runtime.getRuntime().maxMemory() / ONE_BILLION);
@ -113,7 +114,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
// The cast to int in the following is to ensure that the correct SpinnerNumberModel // The cast to int in the following is to ensure that the correct SpinnerNumberModel
// constructor is called. // constructor is called.
solrMaxHeapSpinner.setModel(new javax.swing.SpinnerNumberModel(UserPreferences.getMaxSolrVMSize(), solrMaxHeapSpinner.setModel(new javax.swing.SpinnerNumberModel(UserPreferences.getMaxSolrVMSize(),
512, ((int) getSystemMemoryInGB()) * MEGA_IN_GIGA, 512)); DEFAULT_SOLR_HEAP_SIZE_MB, ((int) getSystemMemoryInGB()) * MEGA_IN_GIGA, DEFAULT_SOLR_HEAP_SIZE_MB));
textFieldListener = new TextFieldListener(); textFieldListener = new TextFieldListener();
agencyLogoPathField.getDocument().addDocumentListener(textFieldListener); agencyLogoPathField.getDocument().addDocumentListener(textFieldListener);

View File

@ -46,6 +46,7 @@ import static org.sleuthkit.autopsy.corecomponents.Bundle.*;
import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.datamodel.DataConversion; import org.sleuthkit.autopsy.datamodel.DataConversion;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -606,7 +607,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
return false; return false;
} }
Content content = DataContentViewerUtility.getDefaultContent(node); Content content = DataContentViewerUtility.getDefaultContent(node);
return content != null && content.getSize() > 0; return content != null && !(content instanceof BlackboardArtifact) && content.getSize() > 0;
} }
@Override @Override

View File

@ -32,9 +32,6 @@ public class DataContentViewerUtility {
* preferring to return any Content object other than a BlackboardArtifact * preferring to return any Content object other than a BlackboardArtifact
* object. * object.
* *
* This method was written with the needs of the hex and strings content
* viewers in mind - the algorithm is exactly what those viewers require.
*
* @param node A display Node object. * @param node A display Node object.
* *
* @return If there are multiple Content objects associated with the Node, * @return If there are multiple Content objects associated with the Node,

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import com.google.common.annotations.Beta;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
@ -234,7 +235,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @param iconPath The path to the icon for the artifact type. * @param iconPath The path to the icon for the artifact type.
*/ */
public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) { public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
super(artifact, createLookup(artifact)); super(artifact, createLookup(artifact, false));
this.artifact = artifact; this.artifact = artifact;
for (Content lookupContent : this.getLookup().lookupAll(Content.class)) { for (Content lookupContent : this.getLookup().lookupAll(Content.class)) {
if ((lookupContent != null) && (!(lookupContent instanceof BlackboardArtifact))) { if ((lookupContent != null) && (!(lookupContent instanceof BlackboardArtifact))) {
@ -264,6 +265,49 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener); Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener);
} }
/**
* Constructs a BlackboardArtifactNode, an AbstractNode implementation that
* can be used to represent an artifact of any type.
*
* @param artifact The artifact to represent.
* @param lookupIsAssociatedFile True if the Content lookup should be made
* for the associated file instead of the
* parent file.
*/
@Beta
public BlackboardArtifactNode(BlackboardArtifact artifact, boolean lookupIsAssociatedFile) {
super(artifact, createLookup(artifact, lookupIsAssociatedFile));
this.artifact = artifact;
try {
//The lookup for a file may or may not exist so we define the srcContent as the parent.
srcContent = artifact.getParent();
} catch (TskCoreException ex) {
logger.log(Level.WARNING, MessageFormat.format("Error getting the parent of the artifact for (artifact objID={0})", artifact.getId()), ex);
}
if (srcContent != null) {
try {
/*
* Calling this getter causes the unique path of the source
* content to be cached in the Content object. This is
* advantageous as long as this node is constructed in a
* background thread instead of a UI thread.
*/
srcContent.getUniquePath();
} catch (TskCoreException ex) {
logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
}
} else {
throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
}
setName(Long.toString(artifact.getArtifactID()));
String displayName = srcContent.getName();
setDisplayName(displayName);
setShortDescription(displayName);
String iconPath = IconsUtil.getIconFilePath(artifact.getArtifactTypeID());
setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener);
}
/** /**
* Constructs a BlackboardArtifactNode, an AbstractNode implementation that * Constructs a BlackboardArtifactNode, an AbstractNode implementation that
* can be used to represent an artifact of any type. * can be used to represent an artifact of any type.
@ -284,22 +328,46 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
*/ */
private static Lookup createLookup(BlackboardArtifact artifact) { private static Lookup createLookup(BlackboardArtifact artifact) {
final long objectID = artifact.getObjectID(); final long objectID = artifact.getObjectID();
Content content = null;
try { try {
if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() || artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID()) { Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
content = getPathIdFile(artifact);
}
if (content == null) { if (content == null) {
content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID)); return Lookups.fixed(artifact);
} else {
return Lookups.fixed(artifact, content);
} }
} catch (ExecutionException ex) { } catch (ExecutionException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
content = null;
}
if (content == null) {
return Lookups.fixed(artifact); return Lookups.fixed(artifact);
}
}
/**
* Creates a Lookup object for this node and populates it with both the
* artifact this node represents and its source content.
*
* @param artifact The artifact this node represents.
* @param lookupIsAssociatedFile True if the Content lookup should be made
* for the associated file instead of the
* parent file.
*
* @return The Lookup.
*/
private static Lookup createLookup(BlackboardArtifact artifact, boolean lookupIsAssociatedFile) {
Content content = null;
if (lookupIsAssociatedFile) {
try {
content = getPathIdFile(artifact);
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
content = null;
}
if (content == null) {
return Lookups.fixed(artifact);
} else {
return Lookups.fixed(artifact, content);
}
} else { } else {
return Lookups.fixed(artifact, content); return createLookup(artifact);
} }
} }
@ -982,7 +1050,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} }
map.put(attribute.getAttributeType().getDisplayName(), value); map.put(attribute.getAttributeType().getDisplayName(), value);
} else { } else {
switch(attribute.getAttributeType().getValueType()) { switch (attribute.getAttributeType().getValueType()) {
case INTEGER: case INTEGER:
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueInt()); map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueInt());
break; break;
@ -990,13 +1058,13 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueDouble()); map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueDouble());
break; break;
case LONG: case LONG:
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong()); map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong());
break; break;
default: default:
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString()); map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
} }
} }
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {

View File

@ -22,6 +22,7 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Level; import java.util.logging.Level;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -46,7 +47,8 @@ class DataSourceGroupingNode extends DisplayableItemNode {
DataSourceGroupingNode(DataSource dataSource) { DataSourceGroupingNode(DataSource dataSource) {
super (Optional.ofNullable(createDSGroupingNodeChildren(dataSource)) super (Optional.ofNullable(createDSGroupingNodeChildren(dataSource))
.orElse(new RootContentChildren(Arrays.asList(Collections.EMPTY_LIST)))); .orElse(new RootContentChildren(Arrays.asList(Collections.EMPTY_LIST))),
Lookups.singleton(dataSource));
if (dataSource instanceof Image) { if (dataSource instanceof Image) {
Image image = (Image) dataSource; Image image = (Image) dataSource;

View File

@ -125,6 +125,8 @@ public final class IconsUtil {
imageFile = "domain-16.png"; //NON-NLS imageFile = "domain-16.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_GPS_AREA.getTypeID()) { } else if (typeID == ARTIFACT_TYPE.TSK_GPS_AREA.getTypeID()) {
imageFile = "gps-area.png"; //NON-NLS imageFile = "gps-area.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_YARA_HIT.getTypeID()) {
imageFile = "yara_16.png"; //NON-NLS
} else { } else {
imageFile = "artifact-icon.png"; //NON-NLS imageFile = "artifact-icon.png"; //NON-NLS
} }

View File

@ -28,18 +28,21 @@ public class CityRecord extends KdTree.XYZPoint {
private final String cityName; private final String cityName;
private final String country; private final String country;
private final String state;
/** /**
* Main constructor. * Main constructor.
* *
* @param cityName The name of the city. * @param cityName The name of the city.
* @param state The state of the city.
* @param country The country of that city. * @param country The country of that city.
* @param latitude Latitude for the city. * @param latitude Latitude for the city.
* @param longitude Longitude for the city. * @param longitude Longitude for the city.
*/ */
CityRecord(String cityName, String country, double latitude, double longitude) { CityRecord(String cityName, String state, String country, double latitude, double longitude) {
super(latitude, longitude); super(latitude, longitude);
this.cityName = cityName; this.cityName = cityName;
this.state = state;
this.country = country; this.country = country;
} }
@ -50,6 +53,13 @@ public class CityRecord extends KdTree.XYZPoint {
return cityName; return cityName;
} }
/**
* @return The state of the city.
*/
public String getState() {
return state;
}
/** /**
* @return The country of that city. * @return The country of that city.
*/ */

View File

@ -42,6 +42,7 @@ class ClosestCityMapper {
// index within a csv row of pertinent data // index within a csv row of pertinent data
private static final int CITY_NAME_IDX = 0; private static final int CITY_NAME_IDX = 0;
private static final int STATE_NAME_IDX = 7;
private static final int COUNTRY_NAME_IDX = 4; private static final int COUNTRY_NAME_IDX = 4;
private static final int LAT_IDX = 2; private static final int LAT_IDX = 2;
private static final int LONG_IDX = 3; private static final int LONG_IDX = 3;
@ -52,7 +53,7 @@ class ClosestCityMapper {
// Identifies if cities are in last, first format like "Korea, South" // Identifies if cities are in last, first format like "Korea, South"
private static final Pattern COUNTRY_WITH_COMMA = Pattern.compile("^\\s*([^,]*)\\s*,\\s*([^,]*)\\s*$"); private static final Pattern COUNTRY_WITH_COMMA = Pattern.compile("^\\s*([^,]*)\\s*,\\s*([^,]*)\\s*$");
private static final int MAX_IDX = Stream.of(CITY_NAME_IDX, COUNTRY_NAME_IDX, LAT_IDX, LONG_IDX) private static final int MAX_IDX = Stream.of(CITY_NAME_IDX, STATE_NAME_IDX, COUNTRY_NAME_IDX, LAT_IDX, LONG_IDX)
.max(Integer::compare) .max(Integer::compare)
.get(); .get();
@ -169,12 +170,15 @@ class ClosestCityMapper {
return null; return null;
} }
// city is required
String cityName = csvRow.get(CITY_NAME_IDX); String cityName = csvRow.get(CITY_NAME_IDX);
if (StringUtils.isBlank(cityName)) { if (StringUtils.isBlank(cityName)) {
logger.log(Level.WARNING, String.format("No city name determined for line %d.", lineNum)); logger.log(Level.WARNING, String.format("No city name determined for line %d.", lineNum));
return null; return null;
} }
// state and country can be optional
String stateName = csvRow.get(STATE_NAME_IDX);
String countryName = parseCountryName(csvRow.get(COUNTRY_NAME_IDX), lineNum); String countryName = parseCountryName(csvRow.get(COUNTRY_NAME_IDX), lineNum);
Double lattitude = tryParse(csvRow.get(LAT_IDX)); Double lattitude = tryParse(csvRow.get(LAT_IDX));
@ -189,7 +193,7 @@ class ClosestCityMapper {
return null; return null;
} }
return new CityRecord(cityName, countryName, lattitude, longitude); return new CityRecord(cityName, stateName, countryName, lattitude, longitude);
} }
/** /**

View File

@ -309,7 +309,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
Long mostRecent = null; Long mostRecent = null;
for (MapWaypoint pt : dataSourcePoints) { for (MapWaypoint pt : dataSourcePoints) {
CityRecord city = closestCityMapper.findClosest(new CityRecord(null, null, pt.getX(), pt.getY())); CityRecord city = closestCityMapper.findClosest(new CityRecord(null, null, null, pt.getX(), pt.getY()));
Long curTime = pt.getTimestamp(); Long curTime = pt.getTimestamp();
if (curTime != null && (mostRecent == null || curTime > mostRecent)) { if (curTime != null && (mostRecent == null || curTime > mostRecent)) {
mostRecent = curTime; mostRecent = curTime;

View File

@ -1,145 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.datamodel;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.IngestJobInfo;
import org.sleuthkit.datamodel.IngestModuleInfo;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Utilities for checking if an ingest module has been run on a datasource.
*/
@Messages({
"IngestModuleCheckUtil_recentActivityModuleName=Recent Activity",
})
public class IngestModuleCheckUtil {
public static final String RECENT_ACTIVITY_FACTORY = "org.sleuthkit.autopsy.recentactivity.RecentActivityExtracterModuleFactory";
public static final String RECENT_ACTIVITY_MODULE_NAME = Bundle.IngestModuleCheckUtil_recentActivityModuleName();
// IngestModuleInfo separator for unique_name
private static final String UNIQUE_NAME_SEPARATOR = "-";
private final SleuthkitCaseProvider caseProvider;
/**
* Main constructor.
*/
public IngestModuleCheckUtil() {
this(SleuthkitCaseProvider.DEFAULT);
}
/**
* Main constructor with external dependencies specified. This constructor
* is designed with unit testing in mind since mocked dependencies can be
* utilized.
*
* @param provider The object providing the current SleuthkitCase.
*/
public IngestModuleCheckUtil(SleuthkitCaseProvider provider) {
this.caseProvider = provider;
}
/**
* Gets the fully qualified factory from the IngestModuleInfo.
* @param info The IngestJobInfo.
* @return The fully qualified factory.
*/
private static String getFullyQualifiedFactory(IngestModuleInfo info) {
if (info == null) {
return null;
}
String qualifiedName = info.getUniqueName();
if (StringUtils.isBlank(qualifiedName)) {
return null;
}
return qualifiedName.split(UNIQUE_NAME_SEPARATOR)[0];
}
/**
* Whether or not the ingest job info contains the ingest modulename.
* @param info The IngestJobInfo.
* @param fullyQualifiedFactory The fully qualified classname of the relevant factory.
* @return True if the ingest module name is contained in the data.
*/
private static boolean hasIngestModule(IngestJobInfo info, String fullyQualifiedFactory) {
if (info == null || info.getIngestModuleInfo() == null || StringUtils.isBlank(fullyQualifiedFactory)) {
return false;
}
return info.getIngestModuleInfo().stream()
.anyMatch((moduleInfo) -> {
String thisQualifiedFactory = getFullyQualifiedFactory(moduleInfo);
return fullyQualifiedFactory.equalsIgnoreCase(thisQualifiedFactory);
});
}
/**
* Whether or not a data source has been ingested with a particular ingest module.
* @param dataSource The datasource.
* @param fullyQualifiedFactory The fully qualified classname of the relevant factory.
* @return Whether or not a data source has been ingested with a particular ingest module.
* @throws TskCoreException
* @throws SleuthkitCaseProviderException
*/
public boolean isModuleIngested(DataSource dataSource, String fullyQualifiedFactory)
throws TskCoreException, SleuthkitCaseProviderException {
if (dataSource == null) {
return false;
}
long dataSourceId = dataSource.getId();
return caseProvider.get().getIngestJobs().stream()
.anyMatch((ingestJob) -> {
return ingestJob != null
&& ingestJob.getObjectId() == dataSourceId
&& hasIngestModule(ingestJob, fullyQualifiedFactory);
});
}
/**
* Get a mapping of fully qualified factory name to display name.
* @param skCase The SleuthkitCase.
* @return The mapping of fully qualified factory name to display name.
* @throws TskCoreException
*/
public static Map<String, String> getFactoryDisplayNames(SleuthkitCase skCase) throws TskCoreException {
return skCase.getIngestJobs().stream()
.flatMap(ingestJob -> ingestJob.getIngestModuleInfo().stream())
.collect(Collectors.toMap(
(moduleInfo) -> getFullyQualifiedFactory(moduleInfo),
(moduleInfo) -> moduleInfo.getDisplayName(),
(a,b) -> a));
}
}

View File

@ -29,8 +29,6 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory;
import org.sleuthkit.autopsy.modules.interestingitems.InterestingItemsIngestModuleFactory;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
/** /**
@ -46,15 +44,6 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String KEYWORD_SEARCH_MODULE_NAME = Bundle.AnalysisPanel_keywordSearchModuleName();
private static final String KEYWORD_SEARCH_FACTORY = "org.sleuthkit.autopsy.keywordsearch.KeywordSearchModuleFactory";
private static final String INTERESTING_ITEM_MODULE_NAME = new InterestingItemsIngestModuleFactory().getModuleDisplayName();
private static final String INTERESTING_ITEM_FACTORY = InterestingItemsIngestModuleFactory.class.getCanonicalName();
private static final String HASHSET_MODULE_NAME = HashLookupModuleFactory.getModuleName();
private static final String HASHSET_FACTORY = HashLookupModuleFactory.class.getCanonicalName();
/** /**
* Default Column definitions for each table * Default Column definitions for each table
*/ */
@ -90,9 +79,8 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
keywordHitsTable, keywordHitsTable,
interestingItemsTable interestingItemsTable
); );
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
/** /**
* All of the components necessary for data fetch swing workers to load data * All of the components necessary for data fetch swing workers to load data
@ -115,28 +103,26 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
// hashset hits loading components // hashset hits loading components
new DataFetchWorker.DataFetchComponents<>( new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> analysisData.getHashsetCounts(dataSource), (dataSource) -> analysisData.getHashsetCounts(dataSource),
(result) -> showResultWithModuleCheck(hashsetHitsTable, result, HASHSET_FACTORY, HASHSET_MODULE_NAME)), (result) -> hashsetHitsTable.showDataFetchResult(result)),
// keyword hits loading components // keyword hits loading components
new DataFetchWorker.DataFetchComponents<>( new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> analysisData.getKeywordCounts(dataSource), (dataSource) -> analysisData.getKeywordCounts(dataSource),
(result) -> showResultWithModuleCheck(keywordHitsTable, result, KEYWORD_SEARCH_FACTORY, KEYWORD_SEARCH_MODULE_NAME)), (result) -> keywordHitsTable.showDataFetchResult(result)),
// interesting item hits loading components // interesting item hits loading components
new DataFetchWorker.DataFetchComponents<>( new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> analysisData.getInterestingItemCounts(dataSource), (dataSource) -> analysisData.getInterestingItemCounts(dataSource),
(result) -> showResultWithModuleCheck(interestingItemsTable, result, INTERESTING_ITEM_FACTORY, INTERESTING_ITEM_MODULE_NAME)) (result) -> interestingItemsTable.showDataFetchResult(result))
); );
initComponents(); initComponents();
} }
@Override @Override
public void close() { public void close() {
ingestRunningLabel.unregister(); ingestRunningLabel.unregister();
super.close(); super.close();
} }
@Override @Override
protected void fetchInformation(DataSource dataSource) { protected void fetchInformation(DataSource dataSource) {
fetchInformation(dataFetchComponents, dataSource); fetchInformation(dataFetchComponents, dataSource);

View File

@ -25,7 +25,6 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -39,11 +38,8 @@ import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler; import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler;
@ -73,7 +69,6 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
private static final Logger logger = Logger.getLogger(BaseDataSourceSummaryPanel.class.getName()); private static final Logger logger = Logger.getLogger(BaseDataSourceSummaryPanel.class.getName());
private final SwingWorkerSequentialExecutor executor = new SwingWorkerSequentialExecutor(); private final SwingWorkerSequentialExecutor executor = new SwingWorkerSequentialExecutor();
private final IngestModuleCheckUtil ingestModuleCheck = new IngestModuleCheckUtil();
private final EventUpdateHandler updateHandler; private final EventUpdateHandler updateHandler;
private final List<UpdateGovernor> governors; private final List<UpdateGovernor> governors;
@ -319,8 +314,8 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
} }
/** /**
* Given the relevant file, navigates to the file in the * Given the relevant file, navigates to the file in the tree and closes
* tree and closes data source summary dialog if open. * data source summary dialog if open.
* *
* @param file The file. * @param file The file.
* @return The menu item list for a go to artifact menu item. * @return The menu item list for a go to artifact menu item.
@ -471,76 +466,4 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
fetchInformation(dataSource); fetchInformation(dataSource);
} }
} }
/**
* Get default message when there is a NotIngestedWithModuleException.
*
* @param moduleName The moduleName.
*
* @return Message specifying that the ingest module was not run.
*/
@Messages({
"# {0} - module name",
"BaseDataSourceSummaryPanel_defaultNotIngestMessage=The {0} ingest module has not been run on this data source."
})
protected String getDefaultNoIngestMessage(String moduleName) {
return Bundle.BaseDataSourceSummaryPanel_defaultNotIngestMessage(moduleName);
}
/**
* Utility method to return the IngestModuleCheckUtil.
*
* @return The IngestModuleCheckUtil.
*/
protected IngestModuleCheckUtil getIngestModuleCheckUtil() {
return this.ingestModuleCheck;
}
/**
* Utility method that in the event of a) there are no results and b) a
* relevant ingest module has not been run on this datasource, then a
* message indicating the unrun ingest module will be shown. Otherwise, the
* default LoadableComponent.showDataFetchResult behavior will be used.
*
* @param component The component.
* @param result The data result.
* @param factoryClass The fully qualified class name of the relevant
* factory.
* @param moduleName The name of the ingest module (i.e. 'Keyword Search').
*/
protected <T> void showResultWithModuleCheck(LoadableComponent<List<T>> component, DataFetchResult<List<T>> result, String factoryClass, String moduleName) {
Predicate<List<T>> hasResults = (lst) -> lst != null && !lst.isEmpty();
showResultWithModuleCheck(component, result, hasResults, factoryClass, moduleName);
}
/**
* Utility method that in the event of a) there are no results and b) a
* relevant ingest module has not been run on this datasource, then a
* message indicating the unrun ingest module will be shown. Otherwise, the
* default LoadableComponent.showDataFetchResult behavior will be used.
*
* @param component The component.
* @param result The data result.
* @param hasResults Given the data type, will provide whether or not the
* data contains any actual results.
* @param factoryClass The fully qualified class name of the relevant
* factory.
* @param moduleName The name of the ingest module (i.e. 'Keyword Search').
*/
protected <T> void showResultWithModuleCheck(LoadableComponent<T> component, DataFetchResult<T> result,
Predicate<T> hasResults, String factoryClass, String moduleName) {
if (result != null && result.getResultType() == ResultType.SUCCESS && !hasResults.test(result.getData())) {
try {
if (!ingestModuleCheck.isModuleIngested(getDataSource(), factoryClass)) {
component.showMessage(getDefaultNoIngestMessage(moduleName));
return;
}
} catch (TskCoreException | SleuthkitCaseProviderException ex) {
logger.log(Level.WARNING, "There was an error while checking for ingest modules for datasource.", ex);
}
}
component.showDataFetchResult(result);
}
} }

View File

@ -63,8 +63,6 @@ import org.sleuthkit.datamodel.DataSource;
public class GeolocationPanel extends BaseDataSourceSummaryPanel { public class GeolocationPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String GPX_FACTORY = "org.python.proxies.GPX_Parser_Module$GPXParserFileIngestModuleFactory";
private static final String GPX_NAME = "GPX Parser";
private static final int DAYS_COUNT = 30; private static final int DAYS_COUNT = 30;
private static final int MAX_COUNT = 10; private static final int MAX_COUNT = 10;
@ -114,6 +112,8 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* @param whereUsedData The GeolocationSummary instance to use. * @param whereUsedData The GeolocationSummary instance to use.
*/ */
public GeolocationPanel(GeolocationSummary whereUsedData) { public GeolocationPanel(GeolocationSummary whereUsedData) {
super(whereUsedData);
this.whereUsedData = whereUsedData; this.whereUsedData = whereUsedData;
// set up data acquisition methods // set up data acquisition methods
dataFetchComponents = Arrays.asList( dataFetchComponents = Arrays.asList(
@ -145,11 +145,19 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
return null; return null;
} }
if (StringUtils.isBlank(record.getCountry())) { List<String> cityIdentifiers = Stream.of(record.getCityName(), record.getState(), record.getCountry())
return record.getCityName(); .filter(StringUtils::isNotBlank)
} .collect(Collectors.toList());
return String.format("%s, %s", record.getCityName(), record.getCountry()); if (cityIdentifiers.size() == 1) {
return cityIdentifiers.get(0);
} else if (cityIdentifiers.size() == 2) {
return String.format("%s, %s", cityIdentifiers.get(0), cityIdentifiers.get(1));
} else if (cityIdentifiers.size() >= 3) {
return String.format("%s, %s; %s", cityIdentifiers.get(0), cityIdentifiers.get(1), cityIdentifiers.get(2));
}
return null;
} }
/** /**
@ -211,7 +219,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
goToGeolocation.setEnabled(true); goToGeolocation.setEnabled(true);
} }
showResultWithModuleCheck(table, convertedData, GPX_FACTORY, GPX_NAME); table.showDataFetchResult(convertedData);
} }
/** /**
@ -225,7 +233,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
private void openGeolocationWindow(DataSource dataSource, Integer daysLimit) { private void openGeolocationWindow(DataSource dataSource, Integer daysLimit) {
// notify dialog (if in dialog) should close. // notify dialog (if in dialog) should close.
notifyParentClose(); notifyParentClose();
// set the filter // set the filter
TopComponent topComponent = WindowManager.getDefault().findTopComponent(GeolocationTopComponent.class.getSimpleName()); TopComponent topComponent = WindowManager.getDefault().findTopComponent(GeolocationTopComponent.class.getSimpleName());
if (topComponent instanceof GeolocationTopComponent) { if (topComponent instanceof GeolocationTopComponent) {

View File

@ -22,7 +22,6 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
@ -46,8 +45,6 @@ import org.sleuthkit.datamodel.DataSource;
public class PastCasesPanel extends BaseDataSourceSummaryPanel { public class PastCasesPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String CR_FACTORY = CentralRepoIngestModuleFactory.class.getName();
private static final String CR_NAME = CentralRepoIngestModuleFactory.getModuleName();
private static final ColumnModel<Pair<String, Long>> CASE_COL = new ColumnModel<>( private static final ColumnModel<Pair<String, Long>> CASE_COL = new ColumnModel<>(
Bundle.PastCasesPanel_caseColumn_title(), Bundle.PastCasesPanel_caseColumn_title(),
@ -84,6 +81,8 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
* Creates new form PastCasesPanel * Creates new form PastCasesPanel
*/ */
public PastCasesPanel(PastCasesSummary pastCaseData) { public PastCasesPanel(PastCasesSummary pastCaseData) {
super(pastCaseData);
// set up data acquisition methods // set up data acquisition methods
dataFetchComponents = Arrays.asList( dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>( new DataFetchWorker.DataFetchComponents<>(
@ -101,8 +100,8 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
* @param result The result. * @param result The result.
*/ */
private void handleResult(DataFetchResult<PastCasesResult> result) { private void handleResult(DataFetchResult<PastCasesResult> result) {
showResultWithModuleCheck(notableFileTable, DataFetchResult.getSubResult(result, (res) -> res.getTaggedNotable()), CR_FACTORY, CR_NAME); notableFileTable.showDataFetchResult(DataFetchResult.getSubResult(result, (res) -> res.getTaggedNotable()));
showResultWithModuleCheck(sameIdTable, DataFetchResult.getSubResult(result, (res) -> res.getSameIdsResults()), CR_FACTORY, CR_NAME); sameIdTable.showDataFetchResult(DataFetchResult.getSubResult(result, (res) -> res.getSameIdsResults()));
} }
@Override @Override

View File

@ -23,7 +23,6 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails;
@ -44,8 +43,6 @@ import org.sleuthkit.datamodel.DataSource;
public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String EMAIL_PARSER_FACTORY = "org.sleuthkit.autopsy.thunderbirdparser.EmailParserModuleFactory";
private static final String EMAIL_PARSER_MODULE_NAME = Bundle.RecentFilePanel_emailParserModuleName();
private final List<JTablePanel<?>> tablePanelList = new ArrayList<>(); private final List<JTablePanel<?>> tablePanelList = new ArrayList<>();
private final List<DataFetchWorker.DataFetchComponents<DataSource, ?>> dataFetchComponents = new ArrayList<>(); private final List<DataFetchWorker.DataFetchComponents<DataSource, ?>> dataFetchComponents = new ArrayList<>();
@ -167,11 +164,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
DataFetchWorker.DataFetchComponents<DataSource, List<RecentFileDetails>> worker DataFetchWorker.DataFetchComponents<DataSource, List<RecentFileDetails>> worker
= new DataFetchWorker.DataFetchComponents<>( = new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> dataHandler.getRecentlyOpenedDocuments(dataSource, 10), (dataSource) -> dataHandler.getRecentlyOpenedDocuments(dataSource, 10),
(result) -> { (result) -> pane.showDataFetchResult(result));
showResultWithModuleCheck(pane, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
});
dataFetchComponents.add(worker); dataFetchComponents.add(worker);
} }
@ -210,11 +203,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
DataFetchWorker.DataFetchComponents<DataSource, List<RecentDownloadDetails>> worker DataFetchWorker.DataFetchComponents<DataSource, List<RecentDownloadDetails>> worker
= new DataFetchWorker.DataFetchComponents<>( = new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> dataHandler.getRecentDownloads(dataSource, 10), (dataSource) -> dataHandler.getRecentDownloads(dataSource, 10),
(result) -> { (result) -> pane.showDataFetchResult(result));
showResultWithModuleCheck(pane, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
});
dataFetchComponents.add(worker); dataFetchComponents.add(worker);
} }
@ -253,7 +242,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
DataFetchWorker.DataFetchComponents<DataSource, List<RecentAttachmentDetails>> worker DataFetchWorker.DataFetchComponents<DataSource, List<RecentAttachmentDetails>> worker
= new DataFetchWorker.DataFetchComponents<>( = new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> dataHandler.getRecentAttachments(dataSource, 10), (dataSource) -> dataHandler.getRecentAttachments(dataSource, 10),
(result) -> showResultWithModuleCheck(pane, result, EMAIL_PARSER_FACTORY, EMAIL_PARSER_MODULE_NAME) (result) -> pane.showDataFetchResult(result)
); );
dataFetchComponents.add(worker); dataFetchComponents.add(worker);

View File

@ -103,6 +103,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
* Creates new form PastCasesPanel * Creates new form PastCasesPanel
*/ */
public TimelinePanel(TimelineSummary timelineData) { public TimelinePanel(TimelineSummary timelineData) {
super(timelineData);
// set up data acquisition methods // set up data acquisition methods
dataFetchComponents = Arrays.asList( dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>( new DataFetchWorker.DataFetchComponents<>(

View File

@ -26,16 +26,13 @@ import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory; import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
@ -251,25 +248,11 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
// usage label worker // usage label worker
new DataFetchWorker.DataFetchComponents<>( new DataFetchWorker.DataFetchComponents<>(
containerData::getDataSourceType, containerData::getDataSourceType,
(result) -> { (result) -> usageLabel.showDataFetchResult(result)),
showResultWithModuleCheck(
usageLabel,
result,
StringUtils::isNotBlank,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
// os label worker // os label worker
new DataFetchWorker.DataFetchComponents<>( new DataFetchWorker.DataFetchComponents<>(
containerData::getOperatingSystems, containerData::getOperatingSystems,
(result) -> { (result) -> osLabel.showDataFetchResult(result)),
showResultWithModuleCheck(
osLabel,
result,
StringUtils::isNotBlank,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
// size label worker // size label worker
new DataFetchWorker.DataFetchComponents<>( new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> { (dataSource) -> {
@ -379,50 +362,22 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
* @param result The result to be shown. * @param result The result to be shown.
*/ */
private void showMimeTypeCategories(DataFetchResult<TypesPieChartData> result) { private void showMimeTypeCategories(DataFetchResult<TypesPieChartData> result) {
// if result is null check for ingest module and show empty results.
if (result == null) { if (result == null) {
showPieResultWithModuleCheck(null); fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(null));
return; return;
} }
// if error, show error // if error, show error
if (result.getResultType() == ResultType.ERROR) { if (result.getResultType() == ResultType.ERROR) {
this.fileMimeTypesChart.showDataFetchResult(DataFetchResult.getErrorResult(result.getException())); fileMimeTypesChart.showDataFetchResult(DataFetchResult.getErrorResult(result.getException()));
return; return;
} }
TypesPieChartData data = result.getData(); TypesPieChartData data = result.getData();
if (data == null) { if (data == null) {
// if no data, do an ingest module check with empty results fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(null));
showPieResultWithModuleCheck(null);
} else if (!data.isUsefulContent()) {
// if no useful data, do an ingest module check and show data
showPieResultWithModuleCheck(data.getPieSlices());
} else { } else {
// otherwise, show the data fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(data.getPieSlices()));
this.fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(data.getPieSlices()));
}
}
/**
* Shows a message in the fileMimeTypesChart about the data source not being
* ingested with the file type ingest module if the data source has not been
* ingested with that module. Also shows data if present.
*
* @param items The list of items to show.
*/
private void showPieResultWithModuleCheck(List<PieChartItem> items) {
boolean hasBeenIngested = false;
try {
hasBeenIngested = this.getIngestModuleCheckUtil().isModuleIngested(getDataSource(), FILE_TYPE_FACTORY);
} catch (TskCoreException | SleuthkitCaseProviderException ex) {
logger.log(Level.WARNING, "There was an error fetching whether or not the current data source has been ingested with the file type ingest module.", ex);
}
if (hasBeenIngested) {
this.fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(items));
} else {
this.fileMimeTypesChart.showDataWithMessage(items, getDefaultNoIngestMessage(FILE_TYPE_MODULE_NAME));
} }
} }

View File

@ -27,7 +27,6 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.LastAccessedArtifact; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.LastAccessedArtifact;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult;
@ -287,43 +286,23 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
// top programs query // top programs query
new DataFetchComponents<DataSource, List<TopProgramsResult>>( new DataFetchComponents<DataSource, List<TopProgramsResult>>(
(dataSource) -> userActivityData.getTopPrograms(dataSource, TOP_PROGS_COUNT), (dataSource) -> userActivityData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
(result) -> { (result) -> topProgramsTable.showDataFetchResult(result)),
showResultWithModuleCheck(topProgramsTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
// top domains query // top domains query
new DataFetchComponents<DataSource, List<TopDomainsResult>>( new DataFetchComponents<DataSource, List<TopDomainsResult>>(
(dataSource) -> userActivityData.getRecentDomains(dataSource, TOP_DOMAINS_COUNT), (dataSource) -> userActivityData.getRecentDomains(dataSource, TOP_DOMAINS_COUNT),
(result) -> { (result) -> recentDomainsTable.showDataFetchResult(result)),
showResultWithModuleCheck(recentDomainsTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
// top web searches query // top web searches query
new DataFetchComponents<DataSource, List<TopWebSearchResult>>( new DataFetchComponents<DataSource, List<TopWebSearchResult>>(
(dataSource) -> userActivityData.getMostRecentWebSearches(dataSource, TOP_SEARCHES_COUNT), (dataSource) -> userActivityData.getMostRecentWebSearches(dataSource, TOP_SEARCHES_COUNT),
(result) -> { (result) -> topWebSearchesTable.showDataFetchResult(result)),
showResultWithModuleCheck(topWebSearchesTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
// top devices query // top devices query
new DataFetchComponents<DataSource, List<TopDeviceAttachedResult>>( new DataFetchComponents<DataSource, List<TopDeviceAttachedResult>>(
(dataSource) -> userActivityData.getRecentDevices(dataSource, TOP_DEVICES_COUNT), (dataSource) -> userActivityData.getRecentDevices(dataSource, TOP_DEVICES_COUNT),
(result) -> { (result) -> topDevicesAttachedTable.showDataFetchResult(result)),
showResultWithModuleCheck(topDevicesAttachedTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
// top accounts query // top accounts query
new DataFetchComponents<DataSource, List<TopAccountResult>>( new DataFetchComponents<DataSource, List<TopAccountResult>>(
(dataSource) -> userActivityData.getRecentAccounts(dataSource, TOP_ACCOUNTS_COUNT), (dataSource) -> userActivityData.getRecentAccounts(dataSource, TOP_ACCOUNTS_COUNT),
(result) -> { (result) -> topAccountsTable.showDataFetchResult(result))
showResultWithModuleCheck(topAccountsTable, result,
ANDROID_FACTORY,
ANDROID_MODULE_NAME);
})
); );
initComponents(); initComponents();

View File

@ -140,9 +140,10 @@ public class DiscoveryAttributes {
return new DiscoveryKeyUtils.FileTypeGroupKey(file); return new DiscoveryKeyUtils.FileTypeGroupKey(file);
} }
} }
/** /**
* Attribute for grouping/sorting by domain category (TSK_WEB_CATEGORY artifacts). * Attribute for grouping/sorting by domain category (TSK_WEB_CATEGORY
* artifacts).
*/ */
static class DomainCategoryAttribute extends AttributeType { static class DomainCategoryAttribute extends AttributeType {
@ -150,7 +151,7 @@ public class DiscoveryAttributes {
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) { public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.DomainCategoryGroupKey(result); return new DiscoveryKeyUtils.DomainCategoryGroupKey(result);
} }
@Override @Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb, public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException { CentralRepository centralRepoDb) throws DiscoveryException {
@ -167,10 +168,11 @@ public class DiscoveryAttributes {
throw new DiscoveryException("Error fetching TSK_WEB_CATEGORY artifacts from the database", ex); throw new DiscoveryException("Error fetching TSK_WEB_CATEGORY artifacts from the database", ex);
} }
} }
/** /**
* Loads all TSK_WEB_CATEGORY artifacts and maps the domain attribute to the category name attribute. * Loads all TSK_WEB_CATEGORY artifacts and maps the domain attribute to
* Each ResultDomain is then parsed and matched against this map of values. * the category name attribute. Each ResultDomain is then parsed and
* matched against this map of values.
*/ */
private Map<String, String> getDomainsWithWebCategories(SleuthkitCase caseDb) throws TskCoreException, InterruptedException { private Map<String, String> getDomainsWithWebCategories(SleuthkitCase caseDb) throws TskCoreException, InterruptedException {
Map<String, String> domainToCategory = new HashMap<>(); Map<String, String> domainToCategory = new HashMap<>();
@ -190,7 +192,7 @@ public class DiscoveryAttributes {
} }
} }
return domainToCategory; return domainToCategory;
} }
} }
@ -269,36 +271,36 @@ public class DiscoveryAttributes {
} }
} }
} }
/** /**
* Organizes the domain instances by normalized domain value. * Organizes the domain instances by normalized domain value. This helps
* This helps reduce the complexity of updating ResultDomain instances * reduce the complexity of updating ResultDomain instances after the query
* after the query has been executed. * has been executed.
* *
* Example: query for notable status of google.com. Result: notable * Example: query for notable status of google.com. Result: notable With
* With this map, all domain instances that represent google.com can * this map, all domain instances that represent google.com can be updated
* be updated after one simple lookup. * after one simple lookup.
*/ */
private static Map<String, List<ResultDomain>> organizeByValue(List<ResultDomain> domainsBatch, CorrelationAttributeInstance.Type attributeType) { private static Map<String, List<ResultDomain>> organizeByValue(List<ResultDomain> domainsBatch, CorrelationAttributeInstance.Type attributeType) {
final Map<String, List<ResultDomain>> resultDomainTable = new HashMap<>(); final Map<String, List<ResultDomain>> resultDomainTable = new HashMap<>();
for (ResultDomain domainInstance : domainsBatch) { for (ResultDomain domainInstance : domainsBatch) {
try { try {
final String domainValue = domainInstance.getDomain(); final String domainValue = domainInstance.getDomain();
final String normalizedDomain = CorrelationAttributeNormalizer.normalize(attributeType, domainValue); final String normalizedDomain = CorrelationAttributeNormalizer.normalize(attributeType, domainValue);
final List<ResultDomain> bucket = resultDomainTable.getOrDefault(normalizedDomain, new ArrayList<>()); final List<ResultDomain> bucket = resultDomainTable.getOrDefault(normalizedDomain, new ArrayList<>());
bucket.add(domainInstance); bucket.add(domainInstance);
resultDomainTable.put(normalizedDomain, bucket); resultDomainTable.put(normalizedDomain, bucket);
} catch (CorrelationAttributeNormalizationException ex) { } catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.INFO, String.format("Domain [%s] failed normalization, skipping...", domainInstance.getDomain())); logger.log(Level.INFO, String.format("Domain [%s] failed normalization, skipping...", domainInstance.getDomain()));
}
} }
return resultDomainTable; }
return resultDomainTable;
} }
/** /**
* Helper function to create a string of comma separated values. * Helper function to create a string of comma separated values. Each value
* Each value is wrapped in `'`. This method is used to bundle up * is wrapped in `'`. This method is used to bundle up a collection of
* a collection of values for use in a SQL WHERE IN (...) clause. * values for use in a SQL WHERE IN (...) clause.
*/ */
private static String createCSV(Set<String> values) { private static String createCSV(Set<String> values) {
StringJoiner joiner = new StringJoiner(", "); StringJoiner joiner = new StringJoiner(", ");
@ -307,30 +309,30 @@ public class DiscoveryAttributes {
} }
return joiner.toString(); return joiner.toString();
} }
/** /**
* Attribute for grouping/sorting by notability in the CR. * Attribute for grouping/sorting by notability in the CR.
*/ */
static class PreviouslyNotableAttribute extends AttributeType { static class PreviouslyNotableAttribute extends AttributeType {
static final int DOMAIN_BATCH_SIZE = 500; // Number of domains to look up at one time static final int DOMAIN_BATCH_SIZE = 500; // Number of domains to look up at one time
@Override @Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) { public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.PreviouslyNotableGroupKey(result); return new DiscoveryKeyUtils.PreviouslyNotableGroupKey(result);
} }
@Override @Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb, public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException { CentralRepository centralRepoDb) throws DiscoveryException {
if (centralRepoDb != null) { if (centralRepoDb != null) {
processFilesWithCr(results, centralRepoDb); processFilesWithCr(results, centralRepoDb);
} }
} }
private void processFilesWithCr(List<Result> results, CentralRepository centralRepo) throws DiscoveryException { private void processFilesWithCr(List<Result> results, CentralRepository centralRepo) throws DiscoveryException {
List<ResultDomain> domainsBatch = new ArrayList<>(); List<ResultDomain> domainsBatch = new ArrayList<>();
for (Result result : results) { for (Result result : results) {
if (result.getType() == SearchData.Type.DOMAIN) { if (result.getType() == SearchData.Type.DOMAIN) {
@ -341,15 +343,15 @@ public class DiscoveryAttributes {
} }
} }
} }
queryPreviouslyNotable(domainsBatch, centralRepo); queryPreviouslyNotable(domainsBatch, centralRepo);
} }
private void queryPreviouslyNotable(List<ResultDomain> domainsBatch, CentralRepository centralRepo) throws DiscoveryException { private void queryPreviouslyNotable(List<ResultDomain> domainsBatch, CentralRepository centralRepo) throws DiscoveryException {
if (domainsBatch.isEmpty()) { if (domainsBatch.isEmpty()) {
return; return;
} }
try { try {
final CorrelationAttributeInstance.Type attributeType = centralRepo.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID); final CorrelationAttributeInstance.Type attributeType = centralRepo.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID);
final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsBatch, attributeType); final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsBatch, attributeType);
@ -371,16 +373,16 @@ public class DiscoveryAttributes {
throw new DiscoveryException("Fatal exception encountered querying the CR.", ex); throw new DiscoveryException("Fatal exception encountered querying the CR.", ex);
} }
} }
private static class DomainPreviouslyNotableCallback implements InstanceTableCallback { private static class DomainPreviouslyNotableCallback implements InstanceTableCallback {
private final Map<String, List<ResultDomain>> domainLookup; private final Map<String, List<ResultDomain>> domainLookup;
private SQLException sqlCause; private SQLException sqlCause;
private DomainPreviouslyNotableCallback(Map<String, List<ResultDomain>> domainLookup) { private DomainPreviouslyNotableCallback(Map<String, List<ResultDomain>> domainLookup) {
this.domainLookup = domainLookup; this.domainLookup = domainLookup;
} }
@Override @Override
public void process(ResultSet resultSet) { public void process(ResultSet resultSet) {
try { try {
@ -401,7 +403,7 @@ public class DiscoveryAttributes {
*/ */
SQLException getCause() { SQLException getCause() {
return this.sqlCause; return this.sqlCause;
} }
} }
} }
@ -499,12 +501,13 @@ public class DiscoveryAttributes {
final CorrelationAttributeInstance.Type attributeType = centralRepository.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID); final CorrelationAttributeInstance.Type attributeType = centralRepository.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID);
final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsToQuery, attributeType); final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsToQuery, attributeType);
final String values = createCSV(resultDomainTable.keySet()); final String values = createCSV(resultDomainTable.keySet());
final String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType); final String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType);
final String domainFrequencyQuery = " value AS domain_name, COUNT(*) AS frequency " final String domainFrequencyQuery = " value AS domain_name, COUNT(value) AS frequency FROM"
+ "FROM " + tableName + " " + "(SELECT DISTINCT case_id, value FROM "
+ "WHERE value IN (" + values + ") " + tableName
+ "GROUP BY value"; + " WHERE value IN ("
+ values
+ ")) AS foo GROUP BY value";
final DomainFrequencyCallback frequencyCallback = new DomainFrequencyCallback(resultDomainTable); final DomainFrequencyCallback frequencyCallback = new DomainFrequencyCallback(resultDomainTable);
centralRepository.processSelectClause(domainFrequencyQuery, frequencyCallback); centralRepository.processSelectClause(domainFrequencyQuery, frequencyCallback);
@ -784,8 +787,8 @@ public class DiscoveryAttributes {
} }
/** /**
* Attribute for grouping/sorting domains by number of page views. * Attribute for grouping/sorting domains by number of page views. Page
* Page views is defined at the number of TSK_WEB_HISTORY artifacts. * views is defined at the number of TSK_WEB_HISTORY artifacts.
*/ */
static class PageViewsAttribute extends AttributeType { static class PageViewsAttribute extends AttributeType {
@ -1074,4 +1077,4 @@ public class DiscoveryAttributes {
private DiscoveryAttributes() { private DiscoveryAttributes() {
// Class should not be instantiated // Class should not be instantiated
} }
} }

View File

@ -277,17 +277,20 @@ public final class DiscoveryEventUtils {
public static final class MiniTimelineResultEvent { public static final class MiniTimelineResultEvent {
private final List<MiniTimelineResult> results = new ArrayList<>(); private final List<MiniTimelineResult> results = new ArrayList<>();
private final String domain;
/** /**
* Construct a new MiniTimelineResultEvent. * Construct a new MiniTimelineResultEvent.
* *
* @param results The list of MiniTimelineResults contained in this * @param results The list of MiniTimelineResults contained in this
* event. * event.
* @param domain The domain the results are for.
*/ */
public MiniTimelineResultEvent(List<MiniTimelineResult> results) { public MiniTimelineResultEvent(List<MiniTimelineResult> results, String domain) {
if (results != null) { if (results != null) {
this.results.addAll(results); this.results.addAll(results);
} }
this.domain = domain;
} }
/** /**
@ -298,6 +301,15 @@ public final class DiscoveryEventUtils {
public List<MiniTimelineResult> getResultList() { public List<MiniTimelineResult> getResultList() {
return Collections.unmodifiableList(results); return Collections.unmodifiableList(results);
} }
/**
* Get the domain this list of results is for.
*
* @return The domain the list of results is for.
*/
public String getDomain() {
return domain;
}
} }
/** /**

View File

@ -201,7 +201,7 @@ public class DomainSearch {
* @throws DiscoveryException if unable to get the artifacts or the date * @throws DiscoveryException if unable to get the artifacts or the date
* attributes from an artifact. * attributes from an artifact.
*/ */
public List<MiniTimelineResult> getAllArtifactsForDomain(SleuthkitCase sleuthkitCase, String domain) throws DiscoveryException, InterruptedException { public List<MiniTimelineResult> getAllArtifactsForDomain(SleuthkitCase sleuthkitCase, String domain) throws DiscoveryException {
List<BlackboardArtifact> artifacts = new ArrayList<>(); List<BlackboardArtifact> artifacts = new ArrayList<>();
Map<String, List<BlackboardArtifact>> dateMap = new HashMap<>(); Map<String, List<BlackboardArtifact>> dateMap = new HashMap<>();
if (!StringUtils.isBlank(domain)) { if (!StringUtils.isBlank(domain)) {

View File

@ -63,7 +63,7 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
// Grouping by CR Frequency, for example, will require further processing // Grouping by CR Frequency, for example, will require further processing
// in order to make the correct decision. The attribute types that require // in order to make the correct decision. The attribute types that require
// more information implement their logic by overriding `addAttributeToResults`. // more information implement their logic by overriding `addAttributeToResults`.
List<AttributeType> searchAttributes = new ArrayList<>(); Set<AttributeType> searchAttributes = new HashSet<>();
searchAttributes.add(key.getGroupAttributeType()); searchAttributes.add(key.getGroupAttributeType());
searchAttributes.addAll(key.getFileSortingMethod().getRequiredAttributes()); searchAttributes.addAll(key.getFileSortingMethod().getRequiredAttributes());
for (AttributeType attr : searchAttributes) { for (AttributeType attr : searchAttributes) {
@ -304,7 +304,7 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
Content dataSource = skc.getContentById(dataSourceID); Content dataSource = skc.getContentById(dataSourceID);
resultDomains.add(new ResultDomain(domain, activityStart, resultDomains.add(new ResultDomain(domain, activityStart,
activityEnd, totalPageViews, pageViewsInLast60, filesDownloaded, activityEnd, totalPageViews, pageViewsInLast60, filesDownloaded,
countOfKnownAccountTypes, dataSource)); countOfKnownAccountTypes, dataSource));
} }
} catch (SQLException ex) { } catch (SQLException ex) {

View File

@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.discovery.search;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
/** /**

View File

@ -257,60 +257,60 @@ public class ResultsSorter implements Comparator<Result> {
return compareStrings(first.getDomain().toLowerCase(), second.getDomain().toLowerCase()); return compareStrings(first.getDomain().toLowerCase(), second.getDomain().toLowerCase());
}; };
} }
/** /**
* Sorts domains by page view count. * Sorts domains by page view count.
* *
* This comparator sorts results in descending order (largest -> smallest). * This comparator sorts results in descending order (largest -> smallest).
*/ */
private static Comparator<Result> getPageViewComparator() { private static Comparator<Result> getPageViewComparator() {
return (Result domain1, Result domain2) -> { return (Result domain1, Result domain2) -> {
if (domain1.getType() != SearchData.Type.DOMAIN || if (domain1.getType() != SearchData.Type.DOMAIN
domain2.getType() != SearchData.Type.DOMAIN) { || domain2.getType() != SearchData.Type.DOMAIN) {
return 0; return 0;
} }
ResultDomain first = (ResultDomain) domain1; ResultDomain first = (ResultDomain) domain1;
ResultDomain second = (ResultDomain) domain2; ResultDomain second = (ResultDomain) domain2;
long firstPageViews = first.getTotalPageViews(); long firstPageViews = first.getTotalPageViews();
long secondPageViews = second.getTotalPageViews(); long secondPageViews = second.getTotalPageViews();
return Long.compare(secondPageViews, firstPageViews); return Long.compare(secondPageViews, firstPageViews);
}; };
} }
/** /**
* Sorts result domains by last activity date time. The results will be in * Sorts result domains by last activity date time. The results will be in
* descending order. * descending order.
*/ */
private static Comparator<Result> getLastActivityDateTimeComparator() { private static Comparator<Result> getLastActivityDateTimeComparator() {
return (Result domain1, Result domain2) -> { return (Result domain1, Result domain2) -> {
if (domain1.getType() != SearchData.Type.DOMAIN || if (domain1.getType() != SearchData.Type.DOMAIN
domain2.getType() != SearchData.Type.DOMAIN) { || domain2.getType() != SearchData.Type.DOMAIN) {
return 0; return 0;
} }
ResultDomain first = (ResultDomain) domain1; ResultDomain first = (ResultDomain) domain1;
ResultDomain second = (ResultDomain) domain2; ResultDomain second = (ResultDomain) domain2;
long firstActivityEnd = first.getActivityEnd(); long firstActivityEnd = first.getActivityEnd();
long secondActivityEnd = second.getActivityEnd(); long secondActivityEnd = second.getActivityEnd();
return Long.compare(secondActivityEnd, firstActivityEnd); return Long.compare(secondActivityEnd, firstActivityEnd);
}; };
} }
/** /**
* Sorts result domains by most file downloads. The results will be in * Sorts result domains by most file downloads. The results will be in
* descending order. * descending order.
*/ */
private static Comparator<Result> getWebDownloadsComparator() { private static Comparator<Result> getWebDownloadsComparator() {
return (Result domain1, Result domain2) -> { return (Result domain1, Result domain2) -> {
if (domain1.getType() != SearchData.Type.DOMAIN || if (domain1.getType() != SearchData.Type.DOMAIN
domain2.getType() != SearchData.Type.DOMAIN) { || domain2.getType() != SearchData.Type.DOMAIN) {
return 0; return 0;
} }
ResultDomain first = (ResultDomain) domain1; ResultDomain first = (ResultDomain) domain1;
ResultDomain second = (ResultDomain) domain2; ResultDomain second = (ResultDomain) domain2;
long firstFilesDownloaded = first.getFilesDownloaded(); long firstFilesDownloaded = first.getFilesDownloaded();
long secondFilesDownloaded = second.getFilesDownloaded(); long secondFilesDownloaded = second.getFilesDownloaded();
return Long.compare(secondFilesDownloaded, firstFilesDownloaded); return Long.compare(secondFilesDownloaded, firstFilesDownloaded);
@ -388,10 +388,10 @@ public class ResultsSorter implements Comparator<Result> {
Bundle.FileSorter_SortingMethod_keywordlist_displayName()), // Sort alphabetically by list of keyword list names found Bundle.FileSorter_SortingMethod_keywordlist_displayName()), // Sort alphabetically by list of keyword list names found
BY_FULL_PATH(new ArrayList<>(), BY_FULL_PATH(new ArrayList<>(),
Bundle.FileSorter_SortingMethod_fullPath_displayName()), // Sort alphabetically by path Bundle.FileSorter_SortingMethod_fullPath_displayName()), // Sort alphabetically by path
BY_DOMAIN_NAME(new ArrayList<>(),Bundle.FileSorter_SortingMethod_domain_displayName()), BY_DOMAIN_NAME(Arrays.asList(new DiscoveryAttributes.DomainCategoryAttribute()), Bundle.FileSorter_SortingMethod_domain_displayName()),
BY_PAGE_VIEWS(new ArrayList<>(), Bundle.FileSorter_SortingMethod_pageViews_displayName()), BY_PAGE_VIEWS(Arrays.asList(new DiscoveryAttributes.DomainCategoryAttribute()), Bundle.FileSorter_SortingMethod_pageViews_displayName()),
BY_DOWNLOADS(new ArrayList<>(), Bundle.FileSorter_SortingMethod_downloads_displayName()), BY_DOWNLOADS(Arrays.asList(new DiscoveryAttributes.DomainCategoryAttribute()), Bundle.FileSorter_SortingMethod_downloads_displayName()),
BY_LAST_ACTIVITY(new ArrayList<>(), Bundle.FileSorter_SortingMethod_activity_displayName()); BY_LAST_ACTIVITY(Arrays.asList(new DiscoveryAttributes.DomainCategoryAttribute()), Bundle.FileSorter_SortingMethod_activity_displayName());
private final String displayName; private final String displayName;
private final List<DiscoveryAttributes.AttributeType> requiredAttributes; private final List<DiscoveryAttributes.AttributeType> requiredAttributes;

View File

@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.discovery.ui;
import java.awt.Component; import java.awt.Component;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JScrollPane;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
@ -30,14 +29,13 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
*/ */
public abstract class AbstractArtifactDetailsPanel extends JPanel { public abstract class AbstractArtifactDetailsPanel extends JPanel {
private static final long serialVersionUID = 1L;
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public Component getComponent() { public Component getComponent() {
// Slap a vertical scrollbar on the panel. return this;
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
} }
private static final long serialVersionUID = 1L;
/** /**
* Called to display the contents of the given artifact. * Called to display the contents of the given artifact.
* *

View File

@ -4,7 +4,7 @@
<Properties> <Properties>
<Property name="opaque" type="boolean" value="false"/> <Property name="opaque" type="boolean" value="false"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 0]"/> <Dimension value="[350, 10]"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
@ -22,12 +22,12 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" pref="400" max="32767" attributes="0"/> <Component id="jScrollPane1" max="32767" attributes="0"/>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" alignment="0" pref="0" max="32767" attributes="0"/> <Component id="jScrollPane1" max="32767" attributes="0"/>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
</Layout> </Layout>
@ -37,9 +37,13 @@
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/> <Border info="null"/>
</Property> </Property>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/> <Dimension value="[0, 0]"/>
</Property> </Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[350, 10]"/>
</Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/> <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>

View File

@ -25,6 +25,7 @@ import java.util.logging.Level;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
@ -32,6 +33,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.guiutils.SimpleTableCellRenderer;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TimeUtilities;
@ -56,6 +58,11 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
ArtifactsListPanel(BlackboardArtifact.ARTIFACT_TYPE artifactType) { ArtifactsListPanel(BlackboardArtifact.ARTIFACT_TYPE artifactType) {
tableModel = new DomainArtifactTableModel(artifactType); tableModel = new DomainArtifactTableModel(artifactType);
initComponents(); initComponents();
// add the cell renderer to all columns
TableCellRenderer renderer = new SimpleTableCellRenderer();
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
}
artifactsTable.getRowSorter().toggleSortOrder(0); artifactsTable.getRowSorter().toggleSortOrder(0);
artifactsTable.getRowSorter().toggleSortOrder(0); artifactsTable.getRowSorter().toggleSortOrder(0);
} }
@ -94,11 +101,15 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
@Override @Override
BlackboardArtifact getSelectedArtifact() { BlackboardArtifact getSelectedArtifact() {
int selectedIndex = artifactsTable.getSelectionModel().getLeadSelectionIndex(); if (artifactsTable.getModel() instanceof DomainArtifactTableModel) {
if (selectedIndex < artifactsTable.getSelectionModel().getMinSelectionIndex() || artifactsTable.getSelectionModel().getMaxSelectionIndex() < 0 || selectedIndex > artifactsTable.getSelectionModel().getMaxSelectionIndex()) { int selectedIndex = artifactsTable.getSelectionModel().getLeadSelectionIndex();
if (selectedIndex < artifactsTable.getSelectionModel().getMinSelectionIndex() || artifactsTable.getSelectionModel().getMaxSelectionIndex() < 0 || selectedIndex > artifactsTable.getSelectionModel().getMaxSelectionIndex()) {
return null;
}
return tableModel.getArtifactByRow(artifactsTable.convertRowIndexToModel(selectedIndex));
} else {
return null; return null;
} }
return tableModel.getArtifactByRow(artifactsTable.convertRowIndexToModel(selectedIndex));
} }
@Override @Override
@ -124,7 +135,12 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
void addArtifacts(List<BlackboardArtifact> artifactList) { void addArtifacts(List<BlackboardArtifact> artifactList) {
tableModel.setContents(artifactList); if (!artifactList.isEmpty()) {
artifactsTable.setModel(tableModel);
tableModel.setContents(artifactList);
} else {
artifactsTable.setModel(new EmptyTableModel());
}
artifactsTable.validate(); artifactsTable.validate();
artifactsTable.repaint(); artifactsTable.repaint();
tableModel.fireTableDataChanged(); tableModel.fireTableDataChanged();
@ -152,10 +168,12 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
artifactsTable = new javax.swing.JTable(); artifactsTable = new javax.swing.JTable();
setOpaque(false); setOpaque(false);
setPreferredSize(new java.awt.Dimension(300, 0)); setPreferredSize(new java.awt.Dimension(350, 10));
jScrollPane1.setBorder(null); jScrollPane1.setBorder(null);
jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0)); jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0));
jScrollPane1.setPreferredSize(new java.awt.Dimension(350, 10));
artifactsTable.setAutoCreateRowSorter(true); artifactsTable.setAutoCreateRowSorter(true);
artifactsTable.setModel(tableModel); artifactsTable.setModel(tableModel);
@ -166,11 +184,11 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 0, Short.MAX_VALUE) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -357,6 +375,46 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
} }
} }
} }
/**
* Table model which displays only that no results were found.
*/
private class EmptyTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
@Override
public int getRowCount() {
return 1;
}
@Override
public int getColumnCount() {
return 1;
}
@NbBundle.Messages({"ArtifactsListPanel.noResultsFound.text=No results found"})
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return Bundle.ArtifactsListPanel_noResultsFound_text();
}
@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return Bundle.ArtifactsListPanel_dateColumn_name();
case 1:
return Bundle.ArtifactsListPanel_titleColumn_name();
case 2:
return Bundle.ArtifactsListPanel_mimeTypeColumn_name();
default:
return "";
}
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTable artifactsTable; private javax.swing.JTable artifactsTable;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables

View File

@ -61,7 +61,8 @@ class ArtifactsWorker extends SwingWorker<List<BlackboardArtifact>, Void> {
return domainSearch.getArtifacts(new DomainSearchArtifactsRequest(Case.getCurrentCase().getSleuthkitCase(), domain, artifactType)); return domainSearch.getArtifacts(new DomainSearchArtifactsRequest(Case.getCurrentCase().getSleuthkitCase(), domain, artifactType));
} catch (DiscoveryException ex) { } catch (DiscoveryException ex) {
if (ex.getCause() instanceof InterruptedException) { if (ex.getCause() instanceof InterruptedException) {
logger.log(Level.INFO, "MiniTimeline search was cancelled or interrupted for domain: {0}", domain); this.cancel(true);
//ignore the exception as it was cancelled while the cache was performing its get and we support cancellation
} else { } else {
throw ex; throw ex;
} }
@ -84,6 +85,6 @@ class ArtifactsWorker extends SwingWorker<List<BlackboardArtifact>, Void> {
//Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging //Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging
} }
} }
} }
} }

View File

@ -53,12 +53,11 @@ DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which
ObjectDetectedFilterPanel.text=Object Detected: ObjectDetectedFilterPanel.text=Object Detected:
DateFilterPanel.mostRecentRadioButton.text=Only last: DateFilterPanel.mostRecentRadioButton.text=Only last:
DateFilterPanel.dateFilterCheckBox.text=Date Filter: DateFilterPanel.dateFilterCheckBox.text=Date Filter:
DomainSummaryPanel.activityLabel.text=
DomainSummaryPanel.pagesLabel.text= DomainSummaryPanel.pagesLabel.text=
DomainSummaryPanel.filesDownloadedLabel.text= DomainSummaryPanel.filesDownloadedLabel.text=
DomainSummaryPanel.totalVisitsLabel.text=
FileDetailsPanel.instancesList.border.title=Instances FileDetailsPanel.instancesList.border.title=Instances
CookieDetailsPanel.jLabel1.text=Artifact: CookieDetailsPanel.jLabel1.text=Artifact:
CookieDetailsPanel.jLabel2.text= CookieDetailsPanel.jLabel2.text=
PreviouslyNotableFilterPanel.text_1=Include only previously notable domains PreviouslyNotableFilterPanel.text_1=Include only previously notable domains
KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type
LoadingPanel.detailsLabel.AccessibleContext.accessibleName=detailsLabel

View File

@ -3,6 +3,7 @@ ArtifactMenuMouseAdapter_label=Extract Files
ArtifactsListPanel.dateColumn.name=Date/Time ArtifactsListPanel.dateColumn.name=Date/Time
ArtifactsListPanel.fileNameColumn.name=Name ArtifactsListPanel.fileNameColumn.name=Name
ArtifactsListPanel.mimeTypeColumn.name=MIME Type ArtifactsListPanel.mimeTypeColumn.name=MIME Type
ArtifactsListPanel.noResultsFound.text=No results found
ArtifactsListPanel.termColumn.name=Term ArtifactsListPanel.termColumn.name=Term
ArtifactsListPanel.titleColumn.name=Title ArtifactsListPanel.titleColumn.name=Title
ArtifactsListPanel.urlColumn.name=URL ArtifactsListPanel.urlColumn.name=URL
@ -52,14 +53,20 @@ DocumentPanel.numberOfImages.noImages=No images
# {0} - numberOfImages # {0} - numberOfImages
DocumentPanel.numberOfImages.text=1 of {0} images DocumentPanel.numberOfImages.text=1 of {0} images
DocumentWrapper.previewInitialValue=Preview not generated yet. DocumentWrapper.previewInitialValue=Preview not generated yet.
DomainDetailsPanel.miniTimelineTitle.text=Mini Timeline DomainDetailsPanel.miniTimelineTitle.text=Timeline
# {0} - startDate # {0} - startDate
# {1} - endDate # {1} - endDate
DomainSummaryPanel.activity.text=Activity: {0} to {1} DomainSummaryPanel.activity.text=Activity: {0} to {1}
DomainSummaryPanel.category.text=Category:
DomainSummaryPanel.downloads.text=Files downloaded: DomainSummaryPanel.downloads.text=Files downloaded:
DomainSummaryPanel.known.text=User role: Known account type(s)
DomainSummaryPanel.loadingImages.text=Loading thumbnail... DomainSummaryPanel.loadingImages.text=Loading thumbnail...
DomainSummaryPanel.no.text=No
DomainSummaryPanel.notability.text=Previously tagged as notable:
DomainSummaryPanel.pages.text=Page views in past 60 days: DomainSummaryPanel.pages.text=Page views in past 60 days:
DomainSummaryPanel.totalPages.text=Total page views: DomainSummaryPanel.totalPages.text=Total page views:
DomainSummaryPanel.unknown.text=User role: Unknown
DomainSummaryPanel.yes.text=Yes
GroupsListPanel.noDomainResults.message.text=No domains were found for the selected filters.\n\nReminder:\n -The Recent Activity module must be run on each data source you want to find results in.\n -The Central Repository module must be run on each data source if you want to filter or sort by past occurrences.\n -The iOS Analyzer (iLEAPP) module must be run on each data source which contains data from an iOS device.\n GroupsListPanel.noDomainResults.message.text=No domains were found for the selected filters.\n\nReminder:\n -The Recent Activity module must be run on each data source you want to find results in.\n -The Central Repository module must be run on each data source if you want to filter or sort by past occurrences.\n -The iOS Analyzer (iLEAPP) module must be run on each data source which contains data from an iOS device.\n
GroupsListPanel.noFileResults.message.text=No files were found for the selected filters.\n\nReminder:\n -The File Type Identification module must be run on each data source you want to find results in.\n -The Hash Lookup module must be run on each data source if you want to filter by past occurrence.\n -The Picture Analyzer module must be run on each data source if you are filtering by User Created content. GroupsListPanel.noFileResults.message.text=No files were found for the selected filters.\n\nReminder:\n -The File Type Identification module must be run on each data source you want to find results in.\n -The Hash Lookup module must be run on each data source if you want to filter by past occurrence.\n -The Picture Analyzer module must be run on each data source if you are filtering by User Created content.
GroupsListPanel.noResults.title.text=No results found GroupsListPanel.noResults.title.text=No results found
@ -68,12 +75,16 @@ ImageThumbnailPanel.isDeleted.text=All instances of file are deleted.
# {0} - otherInstanceCount # {0} - otherInstanceCount
ImageThumbnailPanel.nameLabel.more.text=\ and {0} more ImageThumbnailPanel.nameLabel.more.text=\ and {0} more
InterestingItemsFilterPanel.error.text=At least one interesting file set name must be selected. InterestingItemsFilterPanel.error.text=At least one interesting file set name must be selected.
LoadingPanel.loading.text=Loading, please wait.
# {0} - resultInfo
LoadingPanel.retrieving.text=Retrieving results for {0}.
MiniTimelineArtifactListPanel.descriptionColumn.name=\ Description MiniTimelineArtifactListPanel.descriptionColumn.name=\ Description
MiniTimelineArtifactListPanel.typeColumn.name=Result Type MiniTimelineArtifactListPanel.typeColumn.name=Result Type
MiniTimelineArtifactListPanel.value.noValue=No value available. MiniTimelineArtifactListPanel.value.noValue=No value available.
MiniTimelineDateListPanel.countColumn.name=Count MiniTimelineDateListPanel.countColumn.name=Count
MiniTimelineDateListPanel.dateColumn.name=Date MiniTimelineDateListPanel.dateColumn.name=Date
MiniTimelineDateListPanel.value.noValue=No value available. MiniTimelineDateListPanel.value.noValue=No value available.
MiniTimelinePanel.loadingPanel.details=the Timeline view
MonthAbbreviation.aprilAbbrev=Apr MonthAbbreviation.aprilAbbrev=Apr
MonthAbbreviation.augustAbbrev=Aug MonthAbbreviation.augustAbbrev=Aug
MonthAbbreviation.decemberAbbrev=Dec MonthAbbreviation.decemberAbbrev=Dec
@ -156,15 +167,14 @@ DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which
ObjectDetectedFilterPanel.text=Object Detected: ObjectDetectedFilterPanel.text=Object Detected:
DateFilterPanel.mostRecentRadioButton.text=Only last: DateFilterPanel.mostRecentRadioButton.text=Only last:
DateFilterPanel.dateFilterCheckBox.text=Date Filter: DateFilterPanel.dateFilterCheckBox.text=Date Filter:
DomainSummaryPanel.activityLabel.text=
DomainSummaryPanel.pagesLabel.text= DomainSummaryPanel.pagesLabel.text=
DomainSummaryPanel.filesDownloadedLabel.text= DomainSummaryPanel.filesDownloadedLabel.text=
DomainSummaryPanel.totalVisitsLabel.text=
FileDetailsPanel.instancesList.border.title=Instances FileDetailsPanel.instancesList.border.title=Instances
CookieDetailsPanel.jLabel1.text=Artifact: CookieDetailsPanel.jLabel1.text=Artifact:
CookieDetailsPanel.jLabel2.text= CookieDetailsPanel.jLabel2.text=
PreviouslyNotableFilterPanel.text_1=Include only previously notable domains PreviouslyNotableFilterPanel.text_1=Include only previously notable domains
KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type
LoadingPanel.detailsLabel.AccessibleContext.accessibleName=detailsLabel
VideoThumbnailPanel.bytes.text=bytes VideoThumbnailPanel.bytes.text=bytes
VideoThumbnailPanel.deleted.text=All instances of file are deleted. VideoThumbnailPanel.deleted.text=All instances of file are deleted.
VideoThumbnailPanel.gigaBytes.text=GB VideoThumbnailPanel.gigaBytes.text=GB

View File

@ -3,7 +3,7 @@
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties> <Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[500, 0]"/> <Dimension value="[300, 0]"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues> <AuxValues>

View File

@ -50,7 +50,7 @@ final class ContentViewerDetailsPanel extends AbstractArtifactDetailsPanel {
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() { private void initComponents() {
setPreferredSize(new java.awt.Dimension(500, 0)); setPreferredSize(new java.awt.Dimension(300, 0));
setLayout(new java.awt.BorderLayout()); setLayout(new java.awt.BorderLayout());
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -59,7 +59,9 @@ final class ContentViewerDetailsPanel extends AbstractArtifactDetailsPanel {
public void setArtifact(BlackboardArtifact artifact) { public void setArtifact(BlackboardArtifact artifact) {
Node node = Node.EMPTY; Node node = Node.EMPTY;
if (artifact != null) { if (artifact != null) {
node = new BlackboardArtifactNode(artifact); boolean useAssociatedFile = artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID();
node = new BlackboardArtifactNode(artifact, useAssociatedFile);
} }
contentViewer.setNode(node); contentViewer.setNode(node);
} }

View File

@ -60,9 +60,11 @@ public final class DiscoveryTopComponent extends TopComponent {
private volatile static int previousDividerLocation = 250; private volatile static int previousDividerLocation = 250;
private final GroupListPanel groupListPanel; private final GroupListPanel groupListPanel;
private final ResultsPanel resultsPanel; private final ResultsPanel resultsPanel;
private JPanel detailsPanel = new JPanel();
private String selectedDomainTabName; private String selectedDomainTabName;
private Type searchType; private Type searchType;
private int dividerLocation = JSplitPane.UNDEFINED_CONDITION; private int dividerLocation = JSplitPane.UNDEFINED_CONDITION;
private SwingAnimator animator = null; private SwingAnimator animator = null;
/** /**
@ -83,25 +85,25 @@ public final class DiscoveryTopComponent extends TopComponent {
} }
}); });
rightSplitPane.setTopComponent(resultsPanel);
resetBottomComponent();
rightSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { rightSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)) { if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)
&& ((animator == null || !animator.isRunning())
&& evt.getNewValue() instanceof Integer
&& evt.getOldValue() instanceof Integer
&& ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize())
&& (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue())
&& ((int) evt.getOldValue() != JSplitPane.UNDEFINED_CONDITION))) {
//Only change the saved location when it was a manual change by the user and not the animation or the window opening initially //Only change the saved location when it was a manual change by the user and not the animation or the window opening initially
if ((animator == null || !animator.isRunning()) previousDividerLocation = (int) evt.getNewValue();
&& evt.getNewValue() instanceof Integer
&& evt.getOldValue() instanceof Integer
&& ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize())
&& (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue())
&& ((int) evt.getOldValue() != JSplitPane.UNDEFINED_CONDITION)) {
previousDividerLocation = (int) evt.getNewValue();
}
} }
} }
}); }
rightSplitPane.setTopComponent(resultsPanel); );
rightSplitPane.setBottomComponent(new JPanel());
} }
/** /**
@ -131,7 +133,8 @@ public final class DiscoveryTopComponent extends TopComponent {
* @return The open DiscoveryTopComponent or null if it has not been opened. * @return The open DiscoveryTopComponent or null if it has not been opened.
*/ */
public static DiscoveryTopComponent getTopComponent() { public static DiscoveryTopComponent getTopComponent() {
return (DiscoveryTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); DiscoveryTopComponent discoveryTopComp = (DiscoveryTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID);
return discoveryTopComp;
} }
/** /**
@ -153,6 +156,11 @@ public final class DiscoveryTopComponent extends TopComponent {
DiscoveryEventUtils.getDiscoveryEventBus().register(groupListPanel); DiscoveryEventUtils.getDiscoveryEventBus().register(groupListPanel);
} }
private void resetBottomComponent() {
rightSplitPane.setBottomComponent(new JPanel());
rightSplitPane.setDividerLocation(JSplitPane.UNDEFINED_CONDITION);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override @Override
protected void componentClosed() { protected void componentClosed() {
@ -162,11 +170,12 @@ public final class DiscoveryTopComponent extends TopComponent {
DiscoveryEventUtils.getDiscoveryEventBus().unregister(this); DiscoveryEventUtils.getDiscoveryEventBus().unregister(this);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(groupListPanel); DiscoveryEventUtils.getDiscoveryEventBus().unregister(groupListPanel);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(resultsPanel); DiscoveryEventUtils.getDiscoveryEventBus().unregister(resultsPanel);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(rightSplitPane.getBottomComponent()); DiscoveryEventUtils.getDiscoveryEventBus().unregister(detailsPanel);
if (rightSplitPane.getBottomComponent() instanceof DomainDetailsPanel) { if (detailsPanel instanceof DomainDetailsPanel) {
selectedDomainTabName = ((DomainDetailsPanel) rightSplitPane.getBottomComponent()).getSelectedTabName(); ((DomainDetailsPanel) detailsPanel).unregister();
selectedDomainTabName = ((DomainDetailsPanel) detailsPanel).getSelectedTabName();
} }
rightSplitPane.setDividerLocation(JSplitPane.UNDEFINED_CONDITION); resetBottomComponent();
super.componentClosed(); super.componentClosed();
} }
@ -336,12 +345,13 @@ public final class DiscoveryTopComponent extends TopComponent {
} }
selectedDomainTabName = validateLastSelectedType(searchCompleteEvent); selectedDomainTabName = validateLastSelectedType(searchCompleteEvent);
DomainDetailsPanel domainDetailsPanel = new DomainDetailsPanel(); DomainDetailsPanel domainDetailsPanel = new DomainDetailsPanel();
rightSplitPane.setBottomComponent(domainDetailsPanel);
domainDetailsPanel.configureArtifactTabs(selectedDomainTabName); domainDetailsPanel.configureArtifactTabs(selectedDomainTabName);
detailsPanel = domainDetailsPanel;
} else { } else {
rightSplitPane.setBottomComponent(new FileDetailsPanel()); detailsPanel = new FileDetailsPanel();
} }
DiscoveryEventUtils.getDiscoveryEventBus().register(rightSplitPane.getBottomComponent()); rightSplitPane.setBottomComponent(detailsPanel);
DiscoveryEventUtils.getDiscoveryEventBus().register(detailsPanel);
descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; ")); descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; "));
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText)); progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText));
progressMessageTextArea.setCaretPosition(0); progressMessageTextArea.setCaretPosition(0);

View File

@ -1,11 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
<Properties>
<Property name="dividerLocation" type="int" value="350"/>
<Property name="resizeWeight" type="double" value="0.1"/>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
</Container>
</NonVisualComponents>
<Properties> <Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Dimension value="[0, 0]"/> <Color id="Default Cursor"/>
</Property> </Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/> <Dimension value="[0, 0]"/>
</Property> </Property>
</Properties> </Properties>
@ -23,23 +33,4 @@
</AuxValues> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="jSplitPane1">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
</Container>
</SubComponents>
</Form> </Form>

View File

@ -22,7 +22,6 @@ import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifa
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
@ -64,7 +63,8 @@ final class DomainArtifactsTabPanel extends JPanel {
this.artifactType = type; this.artifactType = type;
listPanel = new ArtifactsListPanel(artifactType); listPanel = new ArtifactsListPanel(artifactType);
listPanel.addMouseListener(new ArtifactMenuMouseAdapter(listPanel)); listPanel.addMouseListener(new ArtifactMenuMouseAdapter(listPanel));
jSplitPane1.setLeftComponent(listPanel); mainSplitPane.setLeftComponent(listPanel);
add(mainSplitPane);
setRightComponent(); setRightComponent();
listPanel.addSelectionListener(listener); listPanel.addSelectionListener(listener);
} }
@ -91,7 +91,7 @@ final class DomainArtifactsTabPanel extends JPanel {
break; break;
} }
if (rightPanel != null) { if (rightPanel != null) {
jSplitPane1.setRightComponent(rightPanel.getComponent()); mainSplitPane.setRightComponent(rightPanel.getComponent());
} }
} }
@ -115,10 +115,14 @@ final class DomainArtifactsTabPanel extends JPanel {
this.status = status; this.status = status;
if (status == ArtifactRetrievalStatus.UNPOPULATED) { if (status == ArtifactRetrievalStatus.UNPOPULATED) {
listPanel.clearList(); listPanel.clearList();
if (rightPanel != null){ removeAll();
add(mainSplitPane);
if (rightPanel != null) {
rightPanel.setArtifact(null); rightPanel.setArtifact(null);
} }
} else if (status == ArtifactRetrievalStatus.POPULATING) {
removeAll();
add(new LoadingPanel(artifactType.getDisplayName()));
} }
} }
@ -130,7 +134,7 @@ final class DomainArtifactsTabPanel extends JPanel {
*/ */
@Subscribe @Subscribe
void handleArtifactSearchResultEvent(DiscoveryEventUtils.ArtifactSearchResultEvent artifactresultEvent) { void handleArtifactSearchResultEvent(DiscoveryEventUtils.ArtifactSearchResultEvent artifactresultEvent) {
if (artifactType == artifactresultEvent.getArtifactType()) { if (artifactType == artifactresultEvent.getArtifactType() && status == ArtifactRetrievalStatus.POPULATING) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
listPanel.removeSelectionListener(listener); listPanel.removeSelectionListener(listener);
listPanel.addArtifacts(artifactresultEvent.getListOfArtifacts()); listPanel.addArtifacts(artifactresultEvent.getListOfArtifacts());
@ -138,6 +142,8 @@ final class DomainArtifactsTabPanel extends JPanel {
setEnabled(!listPanel.isEmpty()); setEnabled(!listPanel.isEmpty());
listPanel.addSelectionListener(listener); listPanel.addSelectionListener(listener);
listPanel.selectFirst(); listPanel.selectFirst();
removeAll();
add(mainSplitPane);
revalidate(); revalidate();
repaint(); repaint();
try { try {
@ -169,20 +175,19 @@ final class DomainArtifactsTabPanel extends JPanel {
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() { private void initComponents() {
jSplitPane1 = new javax.swing.JSplitPane(); mainSplitPane = new javax.swing.JSplitPane();
mainSplitPane.setDividerLocation(350);
mainSplitPane.setResizeWeight(0.1);
setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
setMinimumSize(new java.awt.Dimension(0, 0)); setMinimumSize(new java.awt.Dimension(0, 0));
setPreferredSize(new java.awt.Dimension(0, 0));
setLayout(new java.awt.BorderLayout()); setLayout(new java.awt.BorderLayout());
jSplitPane1.setMinimumSize(new java.awt.Dimension(0, 0));
jSplitPane1.setPreferredSize(new java.awt.Dimension(0, 0));
add(jSplitPane1, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JSplitPane jSplitPane1; private javax.swing.JSplitPane mainSplitPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
/** /**

View File

@ -39,7 +39,6 @@ final class DomainDetailsPanel extends JPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private ArtifactsWorker singleArtifactDomainWorker; private ArtifactsWorker singleArtifactDomainWorker;
private MiniTimelineWorker miniTimelineWorker;
private String domain; private String domain;
private String selectedTabName = null; private String selectedTabName = null;
@ -51,7 +50,9 @@ final class DomainDetailsPanel extends JPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainDetailsPanel() { DomainDetailsPanel() {
initComponents(); initComponents();
jTabbedPane1.add(Bundle.DomainDetailsPanel_miniTimelineTitle_text(), new MiniTimelinePanel()); MiniTimelinePanel timelinePanel = new MiniTimelinePanel();
DiscoveryEventUtils.getDiscoveryEventBus().register(timelinePanel);
jTabbedPane1.add(Bundle.DomainDetailsPanel_miniTimelineTitle_text(), timelinePanel);
for (BlackboardArtifact.ARTIFACT_TYPE type : SearchData.Type.DOMAIN.getArtifactTypes()) { for (BlackboardArtifact.ARTIFACT_TYPE type : SearchData.Type.DOMAIN.getArtifactTypes()) {
jTabbedPane1.add(type.getDisplayName(), new DomainArtifactsTabPanel(type)); jTabbedPane1.add(type.getDisplayName(), new DomainArtifactsTabPanel(type));
} }
@ -63,7 +64,7 @@ final class DomainDetailsPanel extends JPanel {
* *
* @param tabName The name of the tab to select initially. * @param tabName The name of the tab to select initially.
*/ */
@NbBundle.Messages({"DomainDetailsPanel.miniTimelineTitle.text=Mini Timeline"}) @NbBundle.Messages({"DomainDetailsPanel.miniTimelineTitle.text=Timeline"})
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void configureArtifactTabs(String tabName) { void configureArtifactTabs(String tabName) {
selectedTabName = tabName; selectedTabName = tabName;
@ -104,6 +105,20 @@ final class DomainDetailsPanel extends JPanel {
} }
} }
/**
* Get the status of the currently selected tab.
*
* @return The loading status of the currently selected tab.
*/
DomainArtifactsTabPanel.ArtifactRetrievalStatus getCurrentTabStatus() {
if (jTabbedPane1.getSelectedComponent() instanceof MiniTimelinePanel) {
return ((MiniTimelinePanel) jTabbedPane1.getSelectedComponent()).getStatus();
} else if (jTabbedPane1.getSelectedComponent() instanceof DomainArtifactsTabPanel) {
return ((DomainArtifactsTabPanel) jTabbedPane1.getSelectedComponent()).getStatus();
}
return null;
}
/** /**
* Run the worker which retrieves the list of artifacts for the domain to * Run the worker which retrieves the list of artifacts for the domain to
* populate the details area. * populate the details area.
@ -127,14 +142,9 @@ final class DomainDetailsPanel extends JPanel {
* mini timeline view to populate. * mini timeline view to populate.
*/ */
private void runMiniTimelineWorker(MiniTimelinePanel miniTimelinePanel) { private void runMiniTimelineWorker(MiniTimelinePanel miniTimelinePanel) {
if (miniTimelineWorker != null && !miniTimelineWorker.isDone()) {
miniTimelineWorker.cancel(true);
}
if (miniTimelinePanel.getStatus() == DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED) { if (miniTimelinePanel.getStatus() == DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED) {
DiscoveryEventUtils.getDiscoveryEventBus().register(miniTimelinePanel); miniTimelinePanel.setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING, domain);
miniTimelinePanel.setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING); new MiniTimelineWorker(domain).execute();
miniTimelineWorker = new MiniTimelineWorker(domain);
miniTimelineWorker.execute();
} }
} }
@ -146,20 +156,19 @@ final class DomainDetailsPanel extends JPanel {
*/ */
@Subscribe @Subscribe
void handlePopulateDomainTabsEvent(DiscoveryEventUtils.PopulateDomainTabsEvent populateEvent) { void handlePopulateDomainTabsEvent(DiscoveryEventUtils.PopulateDomainTabsEvent populateEvent) {
domain = populateEvent.getDomain();
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
resetTabsStatus(); if (StringUtils.isBlank(populateEvent.getDomain())) {
selectTab(); resetTabsStatus();
Component selectedComponent = jTabbedPane1.getSelectedComponent();
if (selectedComponent instanceof DomainArtifactsTabPanel) {
runDomainWorker((DomainArtifactsTabPanel) selectedComponent);
} else if (selectedComponent instanceof MiniTimelinePanel) {
runMiniTimelineWorker((MiniTimelinePanel) selectedComponent);
}
if (StringUtils.isBlank(domain)) {
//send fade out event //send fade out event
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(false)); DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(false));
} else { } else {
domain = populateEvent.getDomain();
Component selectedComponent = jTabbedPane1.getSelectedComponent();
if (selectedComponent instanceof DomainArtifactsTabPanel) {
runDomainWorker((DomainArtifactsTabPanel) selectedComponent);
} else if (selectedComponent instanceof MiniTimelinePanel) {
runMiniTimelineWorker((MiniTimelinePanel) selectedComponent);
}
//send fade in event //send fade in event
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(true)); DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(true));
} }
@ -176,7 +185,7 @@ final class DomainDetailsPanel extends JPanel {
if (comp instanceof DomainArtifactsTabPanel) { if (comp instanceof DomainArtifactsTabPanel) {
((DomainArtifactsTabPanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED); ((DomainArtifactsTabPanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED);
} else if (comp instanceof MiniTimelinePanel) { } else if (comp instanceof MiniTimelinePanel) {
((MiniTimelinePanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED); ((MiniTimelinePanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED, domain);
} }
} }
} }
@ -215,4 +224,15 @@ final class DomainDetailsPanel extends JPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTabbedPane jTabbedPane1; private javax.swing.JTabbedPane jTabbedPane1;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
/*
* Unregister the MiniTimelinePanel from the event bus.
*/
void unregister() {
for (Component comp : jTabbedPane1.getComponents()) {
if (comp instanceof MiniTimelinePanel) {
DiscoveryEventUtils.getDiscoveryEventBus().unregister(comp);
}
}
}
} }

View File

@ -24,18 +24,25 @@
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="domainNameLabel" alignment="0" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Component id="domainNameLabel" pref="539" max="32767" attributes="0"/> <Group type="103" groupAlignment="1" max="-2" attributes="0">
<EmptySpace min="-2" pref="47" max="-2" attributes="0"/> <Component id="filesDownloadedLabel" linkSize="3" alignment="0" pref="300" max="32767" attributes="0"/>
<Component id="activityLabel" linkSize="3" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="domainNotabilityLabel" pref="300" max="32767" attributes="0"/>
<Component id="categoryLabel" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="4" max="-2" attributes="0"/>
</Group> </Group>
<Component id="activityLabel" alignment="0" max="32767" attributes="0"/> <Component id="pagesLabel" alignment="0" max="32767" attributes="0"/>
<Component id="pagesLabel" max="32767" attributes="0"/> <Component id="totalVisitsLabel" alignment="0" max="32767" attributes="0"/>
<Component id="filesDownloadedLabel" alignment="0" max="32767" attributes="0"/>
<Component id="totalVisitsLabel" max="32767" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0"> <Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="numberOfImagesLabel" max="32767" attributes="0"/> <Component id="numberOfImagesLabel" max="32767" attributes="0"/>
<Component id="sampleImageLabel" max="32767" attributes="0"/> <Component id="sampleImageLabel" max="32767" attributes="0"/>
@ -51,20 +58,26 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Component id="numberOfImagesLabel" min="-2" pref="17" max="-2" attributes="0"/> <Component id="numberOfImagesLabel" min="-2" pref="17" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/> <EmptySpace pref="8" max="32767" attributes="0"/>
<Component id="sampleImageLabel" min="-2" max="-2" attributes="0"/> <Component id="sampleImageLabel" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Component id="domainNameLabel" min="-2" pref="32" max="-2" attributes="0"/> <Component id="domainNameLabel" min="-2" pref="35" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="32767" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="activityLabel" linkSize="4" alignment="1" min="-2" pref="15" max="-2" attributes="0"/>
<Component id="domainNotabilityLabel" linkSize="2" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="activityLabel" min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" max="-2" attributes="0">
<EmptySpace min="-2" pref="11" max="-2" attributes="0"/> <Component id="filesDownloadedLabel" linkSize="4" pref="14" max="32767" attributes="0"/>
<Component id="totalVisitsLabel" min="-2" max="-2" attributes="0"/> <Component id="categoryLabel" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="11" max="-2" attributes="0"/> </Group>
<Component id="pagesLabel" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <Component id="totalVisitsLabel" linkSize="2" pref="14" max="32767" attributes="0"/>
<Component id="filesDownloadedLabel" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <Component id="pagesLabel" min="-2" pref="15" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
@ -97,11 +110,6 @@
<Component class="javax.swing.JLabel" name="numberOfImagesLabel"> <Component class="javax.swing.JLabel" name="numberOfImagesLabel">
</Component> </Component>
<Component class="javax.swing.JLabel" name="activityLabel"> <Component class="javax.swing.JLabel" name="activityLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DomainSummaryPanel.activityLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="pagesLabel"> <Component class="javax.swing.JLabel" name="pagesLabel">
<Properties> <Properties>
@ -118,11 +126,10 @@
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="totalVisitsLabel"> <Component class="javax.swing.JLabel" name="totalVisitsLabel">
<Properties> </Component>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Component class="javax.swing.JLabel" name="domainNotabilityLabel">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DomainSummaryPanel.totalVisitsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> </Component>
</Property> <Component class="javax.swing.JLabel" name="categoryLabel">
</Properties>
</Component> </Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -32,6 +32,7 @@ import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.discovery.search.SearchData;
/** /**
* Class which displays a preview and details about a domain. * Class which displays a preview and details about a domain.
@ -66,6 +67,8 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
pagesLabel = new javax.swing.JLabel(); pagesLabel = new javax.swing.JLabel();
filesDownloadedLabel = new javax.swing.JLabel(); filesDownloadedLabel = new javax.swing.JLabel();
totalVisitsLabel = new javax.swing.JLabel(); totalVisitsLabel = new javax.swing.JLabel();
domainNotabilityLabel = new javax.swing.JLabel();
categoryLabel = new javax.swing.JLabel();
setBorder(javax.swing.BorderFactory.createEtchedBorder()); setBorder(javax.swing.BorderFactory.createEtchedBorder());
@ -75,14 +78,10 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
sampleImageLabel.setMinimumSize(new java.awt.Dimension(100, 100)); sampleImageLabel.setMinimumSize(new java.awt.Dimension(100, 100));
sampleImageLabel.setPreferredSize(new java.awt.Dimension(100, 100)); sampleImageLabel.setPreferredSize(new java.awt.Dimension(100, 100));
org.openide.awt.Mnemonics.setLocalizedText(activityLabel, org.openide.util.NbBundle.getMessage(DomainSummaryPanel.class, "DomainSummaryPanel.activityLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(pagesLabel, org.openide.util.NbBundle.getMessage(DomainSummaryPanel.class, "DomainSummaryPanel.pagesLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(pagesLabel, org.openide.util.NbBundle.getMessage(DomainSummaryPanel.class, "DomainSummaryPanel.pagesLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(filesDownloadedLabel, org.openide.util.NbBundle.getMessage(DomainSummaryPanel.class, "DomainSummaryPanel.filesDownloadedLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(filesDownloadedLabel, org.openide.util.NbBundle.getMessage(DomainSummaryPanel.class, "DomainSummaryPanel.filesDownloadedLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(totalVisitsLabel, org.openide.util.NbBundle.getMessage(DomainSummaryPanel.class, "DomainSummaryPanel.totalVisitsLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
@ -90,12 +89,17 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(domainNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(domainNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 539, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addGap(47, 47, 47)) .addComponent(filesDownloadedLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
.addComponent(activityLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(activityLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(domainNotabilityLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
.addComponent(categoryLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(4, 4, 4))
.addComponent(pagesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pagesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(filesDownloadedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(totalVisitsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addComponent(totalVisitsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
@ -103,6 +107,9 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
.addComponent(sampleImageLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addComponent(sampleImageLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap()) .addContainerGap())
); );
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {activityLabel, filesDownloadedLabel});
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
@ -110,27 +117,38 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(numberOfImagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 17, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(numberOfImagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 17, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 8, Short.MAX_VALUE)
.addComponent(sampleImageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(sampleImageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(domainNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(domainNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(activityLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(domainNotabilityLabel, javax.swing.GroupLayout.Alignment.TRAILING))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(activityLabel) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addGap(11, 11, 11) .addComponent(filesDownloadedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 14, Short.MAX_VALUE)
.addComponent(totalVisitsLabel) .addComponent(categoryLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(11, 11, 11) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pagesLabel) .addComponent(totalVisitsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 14, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(filesDownloadedLabel) .addComponent(pagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE))) .addGap(1, 1, 1)))
.addContainerGap()) .addContainerGap())
); );
layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {domainNotabilityLabel, totalVisitsLabel});
layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {activityLabel, filesDownloadedLabel});
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel activityLabel; private javax.swing.JLabel activityLabel;
private javax.swing.JLabel categoryLabel;
private javax.swing.JLabel domainNameLabel; private javax.swing.JLabel domainNameLabel;
private javax.swing.JLabel domainNotabilityLabel;
private javax.swing.JLabel filesDownloadedLabel; private javax.swing.JLabel filesDownloadedLabel;
private javax.swing.JLabel numberOfImagesLabel; private javax.swing.JLabel numberOfImagesLabel;
private javax.swing.JLabel pagesLabel; private javax.swing.JLabel pagesLabel;
@ -145,13 +163,27 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
"DomainSummaryPanel.pages.text=Page views in past 60 days: ", "DomainSummaryPanel.pages.text=Page views in past 60 days: ",
"DomainSummaryPanel.totalPages.text=Total page views: ", "DomainSummaryPanel.totalPages.text=Total page views: ",
"DomainSummaryPanel.downloads.text=Files downloaded: ", "DomainSummaryPanel.downloads.text=Files downloaded: ",
"DomainSummaryPanel.loadingImages.text=Loading thumbnail..."}) "DomainSummaryPanel.notability.text=Previously tagged as notable: ",
"DomainSummaryPanel.unknown.text=User role: Unknown",
"DomainSummaryPanel.known.text=User role: Known account type(s)",
"DomainSummaryPanel.category.text=Category: ",
"DomainSummaryPanel.loadingImages.text=Loading thumbnail...",
"DomainSummaryPanel.no.text=No",
"DomainSummaryPanel.yes.text=Yes"})
@Override @Override
public Component getListCellRendererComponent(JList<? extends DomainWrapper> list, DomainWrapper value, int index, boolean isSelected, boolean cellHasFocus) { public Component getListCellRendererComponent(JList<? extends DomainWrapper> list, DomainWrapper value, int index, boolean isSelected, boolean cellHasFocus) {
domainNameLabel.setText(value.getResultDomain().getDomain()); domainNameLabel.setText(value.getResultDomain().getDomain());
TimeZone timeZone = ContentUtils.getTimeZone(value.getResultDomain().getDataSource()); TimeZone timeZone = ContentUtils.getTimeZone(value.getResultDomain().getDataSource());
String startDate = formatDate(value.getResultDomain().getActivityStart(), timeZone); String startDate = formatDate(value.getResultDomain().getActivityStart(), timeZone);
String endDate = formatDate(value.getResultDomain().getActivityEnd(), timeZone); String endDate = formatDate(value.getResultDomain().getActivityEnd(), timeZone);
String notability = Bundle.DomainSummaryPanel_notability_text();
if (value.getResultDomain().getPreviouslyNotableInCR() == SearchData.PreviouslyNotable.PREVIOUSLY_NOTABLE) {
notability += Bundle.DomainSummaryPanel_yes_text();
} else {
notability += Bundle.DomainSummaryPanel_no_text();
}
domainNotabilityLabel.setText(notability);
categoryLabel.setText(Bundle.DomainSummaryPanel_category_text() + value.getResultDomain().getWebCategory());
activityLabel.setText(Bundle.DomainSummaryPanel_activity_text(startDate, endDate)); activityLabel.setText(Bundle.DomainSummaryPanel_activity_text(startDate, endDate));
totalVisitsLabel.setText(Bundle.DomainSummaryPanel_totalPages_text() + value.getResultDomain().getTotalPageViews()); totalVisitsLabel.setText(Bundle.DomainSummaryPanel_totalPages_text() + value.getResultDomain().getTotalPageViews());
pagesLabel.setText(Bundle.DomainSummaryPanel_pages_text() + value.getResultDomain().getPageViewsInLast60Days()); pagesLabel.setText(Bundle.DomainSummaryPanel_pages_text() + value.getResultDomain().getPageViewsInLast60Days());
@ -166,20 +198,21 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
setBackground(isSelected ? SELECTION_COLOR : list.getBackground()); setBackground(isSelected ? SELECTION_COLOR : list.getBackground());
return this; return this;
} }
/** /**
* Formats an epoch time in a given time zone using the following pattern * Formats an epoch time in a given time zone using the following pattern
* *
* MMM dd YYYY * MMM dd YYYY
* *
* The pattern below is formatted manually to reuse the MonthAbbreviation utility. * The pattern below is formatted manually to reuse the MonthAbbreviation
* utility.
*/ */
private String formatDate(long epochSeconds, TimeZone timeZone) { private String formatDate(long epochSeconds, TimeZone timeZone) {
Instant epochSecondsAsInstant = Instant.ofEpochSecond(epochSeconds); Instant epochSecondsAsInstant = Instant.ofEpochSecond(epochSeconds);
ZonedDateTime dateTime = ZonedDateTime.ofInstant(epochSecondsAsInstant, timeZone.toZoneId()); ZonedDateTime dateTime = ZonedDateTime.ofInstant(epochSecondsAsInstant, timeZone.toZoneId());
MonthAbbreviation currentCutOffMonth = MonthAbbreviation.fromMonthValue(dateTime.getMonthValue()); MonthAbbreviation currentCutOffMonth = MonthAbbreviation.fromMonthValue(dateTime.getMonthValue());
return String.format("%s %02d %04d", return String.format("%s %02d %04d",
currentCutOffMonth.toString(), currentCutOffMonth.toString(),
dateTime.getDayOfMonth(), dateTime.getYear()); dateTime.getDayOfMonth(), dateTime.getYear());
} }

View File

@ -33,6 +33,7 @@
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="domainListModel" type="code"/> <Connection code="domainListModel" type="code"/>
</Property> </Property>
<Property name="selectionMode" type="int" value="0"/>
<Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DomainSummaryPanel()" type="code"/> <Connection code="new DomainSummaryPanel()" type="code"/>
</Property> </Property>

View File

@ -64,6 +64,7 @@ public class DomainSummaryViewer extends javax.swing.JPanel {
setLayout(new java.awt.BorderLayout()); setLayout(new java.awt.BorderLayout());
domainList.setModel(domainListModel); domainList.setModel(domainListModel);
domainList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
domainList.setCellRenderer(new DomainSummaryPanel()); domainList.setCellRenderer(new DomainSummaryPanel());
domainScrollPane.setViewportView(domainList); domainScrollPane.setViewportView(domainList);

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="loadingLabel" alignment="1" max="32767" attributes="0"/>
<Component id="detailsLabel" alignment="0" pref="390" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="loadingLabel" min="-2" pref="48" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="detailsLabel" min="-2" pref="33" max="-2" attributes="0"/>
<EmptySpace pref="30" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="loadingLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font component="loadingLabel" property="font" relativeSize="true" size="4"/>
</FontInfo>
</Property>
<Property name="horizontalAlignment" type="int" value="0"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="detailsLabel">
<Properties>
<Property name="horizontalAlignment" type="int" value="0"/>
<Property name="verticalAlignment" type="int" value="1"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="verticalTextPosition" type="int" value="1"/>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="LoadingPanel.detailsLabel.AccessibleContext.accessibleName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,97 @@
/*
* Autopsy
*
* Copyright 2020 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.discovery.ui;
import javax.swing.JPanel;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle;
/**
* Panel to let user know that the information is loading.
*/
class LoadingPanel extends JPanel {
private static final long serialVersionUID = 1L;
/**
* Creates new form LoadingPanel
*/
LoadingPanel() {
this(null);
}
@NbBundle.Messages({"LoadingPanel.loading.text=Loading, please wait.",
"# {0} - resultInfo",
"LoadingPanel.retrieving.text=Retrieving results for {0}."})
LoadingPanel(String details) {
initComponents();
loadingLabel.setText(Bundle.LoadingPanel_loading_text());
if (!StringUtils.isBlank(details)) {
detailsLabel.setText(Bundle.LoadingPanel_retrieving_text(details));
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
loadingLabel = new javax.swing.JLabel();
detailsLabel = new javax.swing.JLabel();
loadingLabel.setFont(loadingLabel.getFont().deriveFont(loadingLabel.getFont().getSize()+4f));
loadingLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
loadingLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
detailsLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
detailsLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP);
detailsLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
detailsLabel.setVerticalTextPosition(javax.swing.SwingConstants.TOP);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(loadingLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(detailsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 390, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(loadingLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(detailsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(30, Short.MAX_VALUE))
);
detailsLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(LoadingPanel.class, "LoadingPanel.detailsLabel.AccessibleContext.accessibleName")); // NOI18N
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel detailsLabel;
private javax.swing.JLabel loadingLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -25,10 +25,12 @@ import java.util.logging.Level;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.guiutils.SimpleTableCellRenderer;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.Type; import org.sleuthkit.datamodel.BlackboardAttribute.Type;
@ -54,6 +56,11 @@ class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel {
MiniTimelineArtifactListPanel() { MiniTimelineArtifactListPanel() {
tableModel = new TypeDescriptionTableModel(); tableModel = new TypeDescriptionTableModel();
initComponents(); initComponents();
// add the cell renderer to all columns
TableCellRenderer renderer = new SimpleTableCellRenderer();
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
}
artifactsTable.getRowSorter().toggleSortOrder(0); artifactsTable.getRowSorter().toggleSortOrder(0);
artifactsTable.getRowSorter().toggleSortOrder(0); artifactsTable.getRowSorter().toggleSortOrder(0);
} }
@ -138,8 +145,8 @@ class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel {
artifactsTable = new javax.swing.JTable(); artifactsTable = new javax.swing.JTable();
setOpaque(false); setOpaque(false);
setPreferredSize(new java.awt.Dimension(300, 0)); setPreferredSize(new java.awt.Dimension(200, 10));
jScrollPane1.setPreferredSize(new java.awt.Dimension(200, 10));
jScrollPane1.setBorder(null); jScrollPane1.setBorder(null);
jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0)); jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0));

View File

@ -23,9 +23,11 @@ import java.util.List;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.MiniTimelineResult; import org.sleuthkit.autopsy.discovery.search.MiniTimelineResult;
import org.sleuthkit.autopsy.guiutils.SimpleTableCellRenderer;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
/** /**
@ -42,6 +44,11 @@ class MiniTimelineDateListPanel extends JPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
MiniTimelineDateListPanel() { MiniTimelineDateListPanel() {
initComponents(); initComponents();
// add the cell renderer to all columns
TableCellRenderer renderer = new SimpleTableCellRenderer();
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
jTable1.getColumnModel().getColumn(i).setCellRenderer(renderer);
}
jTable1.getRowSorter().toggleSortOrder(0); jTable1.getRowSorter().toggleSortOrder(0);
jTable1.getRowSorter().toggleSortOrder(0); jTable1.getRowSorter().toggleSortOrder(0);
} }
@ -139,8 +146,8 @@ class MiniTimelineDateListPanel extends JPanel {
jTable1 = new javax.swing.JTable(); jTable1 = new javax.swing.JTable();
setOpaque(false); setOpaque(false);
setPreferredSize(new java.awt.Dimension(200, 0)); setPreferredSize(new java.awt.Dimension(200, 10));
jScrollPane1.setPreferredSize(new java.awt.Dimension(200, 10));
jScrollPane1.setBorder(null); jScrollPane1.setBorder(null);
jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0)); jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0));

View File

@ -1,13 +1,42 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
<Properties>
<Property name="dividerLocation" type="int" value="400"/>
<Property name="resizeWeight" type="double" value="0.1"/>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="leftSplitPane">
<Properties>
<Property name="dividerLocation" type="int" value="198"/>
<Property name="resizeWeight" type="double" value="0.5"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
</Container>
</SubComponents>
</Container>
</NonVisualComponents>
<Properties> <Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/> <Dimension value="[0, 0]"/>
</Property> </Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
@ -23,45 +52,4 @@
</AuxValues> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
<Properties>
<Property name="resizeWeight" type="double" value="0.4"/>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="leftSplitPane">
<Properties>
<Property name="resizeWeight" type="double" value="0.5"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 0]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form> </Form>

View File

@ -19,12 +19,11 @@
package org.sleuthkit.autopsy.discovery.ui; package org.sleuthkit.autopsy.discovery.ui;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.util.logging.Level;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifactViewer; import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifactViewer;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils; import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
@ -32,7 +31,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
/** /**
* Panel to display the entire mini timeline feature. * Panel to display the entire mini timeline feature.
*/ */
class MiniTimelinePanel extends javax.swing.JPanel { final class MiniTimelinePanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -40,10 +39,11 @@ class MiniTimelinePanel extends javax.swing.JPanel {
private final MiniTimelineArtifactListPanel artifactListPanel = new MiniTimelineArtifactListPanel(); private final MiniTimelineArtifactListPanel artifactListPanel = new MiniTimelineArtifactListPanel();
private DomainArtifactsTabPanel.ArtifactRetrievalStatus status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED; private DomainArtifactsTabPanel.ArtifactRetrievalStatus status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED;
private AbstractArtifactDetailsPanel rightPanel = new GeneralPurposeArtifactViewer(); private AbstractArtifactDetailsPanel rightPanel = new GeneralPurposeArtifactViewer();
private static final Logger logger = Logger.getLogger(MiniTimelinePanel.class.getName()); private String selectedDomain = null;
private final ListSelectionListener artifactListener; private final ListSelectionListener artifactListener;
private final ListSelectionListener dateListener; private final ListSelectionListener dateListener;
@NbBundle.Messages({"MiniTimelinePanel.loadingPanel.details=the Timeline view"})
/** /**
* Creates new form MiniTimelinePanel. * Creates new form MiniTimelinePanel.
*/ */
@ -62,8 +62,8 @@ class MiniTimelinePanel extends javax.swing.JPanel {
} else { } else {
rightPanel = new GeneralPurposeArtifactViewer(); rightPanel = new GeneralPurposeArtifactViewer();
} }
rightPanel.setArtifact(artifact);
mainSplitPane.setRightComponent(rightPanel.getComponent()); mainSplitPane.setRightComponent(rightPanel.getComponent());
rightPanel.setArtifact(artifact);
validate(); validate();
repaint(); repaint();
} }
@ -88,6 +88,7 @@ class MiniTimelinePanel extends javax.swing.JPanel {
leftSplitPane.setLeftComponent(dateListPanel); leftSplitPane.setLeftComponent(dateListPanel);
leftSplitPane.setRightComponent(artifactListPanel); leftSplitPane.setRightComponent(artifactListPanel);
mainSplitPane.setRightComponent(rightPanel.getComponent()); mainSplitPane.setRightComponent(rightPanel.getComponent());
add(mainSplitPane);
} }
/** /**
@ -103,17 +104,24 @@ class MiniTimelinePanel extends javax.swing.JPanel {
/** /**
* Manually set the status of the panel. * Manually set the status of the panel.
* *
* @param status The ArtifactRetrievalStatus of the panel. * @param status The ArtifactRetrievalStatus of the panel
* @param domain The domain the panel is currently reflecting.
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus status) { void setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus status, String domain) {
this.status = status; this.status = status;
this.selectedDomain = domain;
if (status == DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED) { if (status == DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED) {
artifactListPanel.clearList(); artifactListPanel.clearList();
dateListPanel.clearList(); dateListPanel.clearList();
removeAll();
add(mainSplitPane);
if (rightPanel != null) { if (rightPanel != null) {
rightPanel.setArtifact(null); rightPanel.setArtifact(null);
} }
} else if (status == DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING) {
removeAll();
add(new LoadingPanel(Bundle.MiniTimelinePanel_loadingPanel_details()));
} }
} }
@ -126,21 +134,19 @@ class MiniTimelinePanel extends javax.swing.JPanel {
@Subscribe @Subscribe
void handleMiniTimelineResultEvent(DiscoveryEventUtils.MiniTimelineResultEvent miniTimelineResultEvent) { void handleMiniTimelineResultEvent(DiscoveryEventUtils.MiniTimelineResultEvent miniTimelineResultEvent) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
dateListPanel.removeListSelectionListener(dateListener); if (miniTimelineResultEvent.getDomain().equals(selectedDomain)) {
artifactListPanel.removeSelectionListener(artifactListener); dateListPanel.removeListSelectionListener(dateListener);
dateListPanel.addArtifacts(miniTimelineResultEvent.getResultList()); artifactListPanel.removeSelectionListener(artifactListener);
status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATED; dateListPanel.addArtifacts(miniTimelineResultEvent.getResultList());
setEnabled(!dateListPanel.isEmpty()); status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATED;
dateListPanel.addSelectionListener(dateListener); setEnabled(!dateListPanel.isEmpty());
artifactListPanel.addSelectionListener(artifactListener); dateListPanel.addSelectionListener(dateListener);
dateListPanel.selectFirst(); artifactListPanel.addSelectionListener(artifactListener);
revalidate(); dateListPanel.selectFirst();
repaint(); removeAll();
try { add(mainSplitPane);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(this); revalidate();
} catch (IllegalArgumentException notRegistered) { repaint();
logger.log(Level.INFO, "Attempting to unregister mini timeline view which was not registered");
// attempting to remove a tab that was never registered
} }
}); });
} }
@ -157,21 +163,18 @@ class MiniTimelinePanel extends javax.swing.JPanel {
mainSplitPane = new javax.swing.JSplitPane(); mainSplitPane = new javax.swing.JSplitPane();
leftSplitPane = new javax.swing.JSplitPane(); leftSplitPane = new javax.swing.JSplitPane();
setMinimumSize(new java.awt.Dimension(0, 0)); mainSplitPane.setDividerLocation(400);
setPreferredSize(new java.awt.Dimension(0, 0)); mainSplitPane.setResizeWeight(0.1);
setLayout(new java.awt.BorderLayout());
mainSplitPane.setResizeWeight(0.4);
mainSplitPane.setToolTipText(""); mainSplitPane.setToolTipText("");
mainSplitPane.setMinimumSize(new java.awt.Dimension(0, 0)); mainSplitPane.setMinimumSize(new java.awt.Dimension(0, 0));
mainSplitPane.setPreferredSize(new java.awt.Dimension(0, 0));
leftSplitPane.setDividerLocation(198);
leftSplitPane.setResizeWeight(0.5); leftSplitPane.setResizeWeight(0.5);
leftSplitPane.setMinimumSize(new java.awt.Dimension(0, 0)); leftSplitPane.setMinimumSize(new java.awt.Dimension(0, 0));
leftSplitPane.setPreferredSize(new java.awt.Dimension(300, 0));
mainSplitPane.setLeftComponent(leftSplitPane); mainSplitPane.setLeftComponent(leftSplitPane);
add(mainSplitPane, java.awt.BorderLayout.CENTER); setMinimumSize(new java.awt.Dimension(0, 0));
setLayout(new java.awt.BorderLayout());
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents

View File

@ -35,7 +35,7 @@ import org.sleuthkit.autopsy.discovery.search.DomainSearch;
/** /**
* SwingWorker to retrieve a list of artifacts for a specified type and domain. * SwingWorker to retrieve a list of artifacts for a specified type and domain.
*/ */
class MiniTimelineWorker extends SwingWorker<List<MiniTimelineResult>, Void> { class MiniTimelineWorker extends SwingWorker<Void, Void> {
private final static Logger logger = Logger.getLogger(MiniTimelineWorker.class.getName()); private final static Logger logger = Logger.getLogger(MiniTimelineWorker.class.getName());
private final String domain; private final String domain;
@ -51,37 +51,35 @@ class MiniTimelineWorker extends SwingWorker<List<MiniTimelineResult>, Void> {
} }
@Override @Override
protected List<MiniTimelineResult> doInBackground() throws Exception { protected Void doInBackground() throws Exception {
List<MiniTimelineResult> results = new ArrayList<>(); List<MiniTimelineResult> results = new ArrayList<>();
if (!StringUtils.isBlank(domain)) { if (!StringUtils.isBlank(domain)) {
DomainSearch domainSearch = new DomainSearch(); DomainSearch domainSearch = new DomainSearch();
try { try {
results.addAll(domainSearch.getAllArtifactsForDomain(Case.getCurrentCase().getSleuthkitCase(), domain)); results.addAll(domainSearch.getAllArtifactsForDomain(Case.getCurrentCase().getSleuthkitCase(), domain));
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.MiniTimelineResultEvent(results, domain));
} catch (DiscoveryException ex) { } catch (DiscoveryException ex) {
if (ex.getCause() instanceof InterruptedException) { if (ex.getCause() instanceof InterruptedException) {
logger.log(Level.INFO, "MiniTimeline search was cancelled or interrupted for domain: {0}", domain); this.cancel(true);
//ignore the exception as it was cancelled while the cache was performing its get and we support cancellation
} else { } else {
throw ex; throw ex;
} }
} }
} }
return results; return null;
} }
@Override @Override
protected void done() { protected void done() {
List<MiniTimelineResult> results = new ArrayList<>();
if (!isCancelled()) { if (!isCancelled()) {
try { try {
results.addAll(get()); get();
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.MiniTimelineResultEvent(results));
} catch (InterruptedException | ExecutionException ex) { } catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Exception while trying to get list of artifacts for Domain details for mini timeline view for domain: " + domain, ex); logger.log(Level.SEVERE, "Exception while trying to get list of artifacts for Domain details for mini timeline view for domain: " + domain, ex);
} catch (CancellationException ignored) { } catch (CancellationException ignored) {
//Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging //Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging
} }
} }
} }
} }

View File

@ -198,8 +198,13 @@ public class Waypoint {
try { try {
List<BlackboardAttribute> attributeList = artifact.getAttributes(); List<BlackboardAttribute> attributeList = artifact.getAttributes();
for (BlackboardAttribute attribute : attributeList) { for (BlackboardAttribute attribute : attributeList) {
BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID()); try{
attributeMap.put(type, attribute); BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID());
attributeMap.put(type, attribute);
} catch(IllegalArgumentException ex) {
// This was thrown due to a custom attribute that geolocation
// does not currently support.
}
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
throw new GeoLocationDataException("Unable to get attributes from artifact", ex); throw new GeoLocationDataException("Unable to get attributes from artifact", ex);

View File

@ -1281,7 +1281,7 @@ public final class HealthMonitor implements PropertyChangeListener {
} }
/** /**
* Get an shared lock for the health monitor database. Acquire this before * Get a shared lock for the health monitor database. Acquire this before
* database reads or writes. * database reads or writes.
* *
* @return The lock * @return The lock

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -155,7 +155,7 @@ public class ALeappAnalyzerIngestModule implements DataSourceIngestModule {
statusHelper.switchToDeterminate(aLeappFilesToProcess.size()); statusHelper.switchToDeterminate(aLeappFilesToProcess.size());
processALeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString()); processALeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
} else { } else {
aLeappFilesToProcess = findaLeappFilesToProcess(dataSource); aLeappFilesToProcess = LeappFileProcessor.findLeappFilesToProcess(dataSource);
statusHelper.switchToDeterminate(aLeappFilesToProcess.size()); statusHelper.switchToDeterminate(aLeappFilesToProcess.size());
Integer filesProcessedCount = 0; Integer filesProcessedCount = 0;
@ -268,40 +268,7 @@ public class ALeappAnalyzerIngestModule implements DataSourceIngestModule {
} }
/**
* Find the files that will be processed by the aLeapp program
*
* @param dataSource
*
* @return List of abstract files to process.
*/
private List<AbstractFile> findaLeappFilesToProcess(Content dataSource) {
List<AbstractFile> aLeappFiles = new ArrayList<>();
FileManager fileManager = getCurrentCase().getServices().getFileManager();
// findFiles use the SQL wildcard % in the file name
try {
aLeappFiles = fileManager.findFiles(dataSource, "%", "/"); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "No files found to process"); //NON-NLS
return aLeappFiles;
}
List<AbstractFile> aLeappFilesToProcess = new ArrayList<>();
for (AbstractFile aLeappFile : aLeappFiles) {
if (((aLeappFile.getLocalAbsPath() != null)
&& (!aLeappFile.getNameExtension().isEmpty() && (!aLeappFile.isVirtual())))
&& ((aLeappFile.getName().toLowerCase().contains(".zip") || (aLeappFile.getName().toLowerCase().contains(".tar")))
|| aLeappFile.getName().toLowerCase().contains(".tgz"))) {
aLeappFilesToProcess.add(aLeappFile);
}
}
return aLeappFilesToProcess;
}
/** /**
* Build the aLeapp command to run * Build the aLeapp command to run

View File

@ -155,7 +155,7 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
statusHelper.switchToDeterminate(iLeappFilesToProcess.size()); statusHelper.switchToDeterminate(iLeappFilesToProcess.size());
processILeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString()); processILeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
} else { } else {
iLeappFilesToProcess = findiLeappFilesToProcess(dataSource); iLeappFilesToProcess = LeappFileProcessor.findLeappFilesToProcess(dataSource);
statusHelper.switchToDeterminate(iLeappFilesToProcess.size()); statusHelper.switchToDeterminate(iLeappFilesToProcess.size());
Integer filesProcessedCount = 0; Integer filesProcessedCount = 0;
@ -268,41 +268,6 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
} }
/**
* Find the files that will be processed by the iLeapp program
*
* @param dataSource
*
* @return List of abstract files to process.
*/
private List<AbstractFile> findiLeappFilesToProcess(Content dataSource) {
List<AbstractFile> iLeappFiles = new ArrayList<>();
FileManager fileManager = getCurrentCase().getServices().getFileManager();
// findFiles use the SQL wildcard % in the file name
try {
iLeappFiles = fileManager.findFiles(dataSource, "%", "/"); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "No files found to process"); //NON-NLS
return iLeappFiles;
}
List<AbstractFile> iLeappFilesToProcess = new ArrayList<>();
for (AbstractFile iLeappFile : iLeappFiles) {
if (((iLeappFile.getLocalAbsPath() != null)
&& (!iLeappFile.getNameExtension().isEmpty() && (!iLeappFile.isVirtual())))
&& ((iLeappFile.getName().toLowerCase().contains(".zip") || (iLeappFile.getName().toLowerCase().contains(".tar")))
|| iLeappFile.getName().toLowerCase().contains(".tgz"))) {
iLeappFilesToProcess.add(iLeappFile);
}
}
return iLeappFilesToProcess;
}
/** /**
* Build the command to run xLeapp * Build the command to run xLeapp
* @param moduleOutputPath output path for xLeapp * @param moduleOutputPath output path for xLeapp

View File

@ -35,18 +35,24 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import static java.util.Locale.US; import static java.util.Locale.US;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import static org.sleuthkit.autopsy.casemodule.Case.getCurrentCase;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
@ -69,6 +75,52 @@ import org.xml.sax.SAXException;
*/ */
public final class LeappFileProcessor { public final class LeappFileProcessor {
/**
* Represents metadata for a particular column in a tsv file.
*/
private static class TsvColumn {
private final String attributeName;
private final String columnName;
private final boolean required;
/**
* Main constructor.
*
* @param attributeName The BlackboardAttribute name or null if not
* used.
* @param columnName The name of the column in the tsv file.
* @param required Whether or not this attribute is required to be
* present.
*/
TsvColumn(String attributeName, String columnName, boolean required) {
this.attributeName = attributeName;
this.columnName = columnName;
this.required = required;
}
/**
* @return The BlackboardAttribute name or null if not used.
*/
String getAttributeName() {
return attributeName;
}
/**
* @return The name of the column in the tsv file.
*/
String getColumnName() {
return columnName;
}
/**
* @return Whether or not this attribute is required to be present.
*/
boolean isRequired() {
return required;
}
}
private static final Logger logger = Logger.getLogger(LeappFileProcessor.class.getName()); private static final Logger logger = Logger.getLogger(LeappFileProcessor.class.getName());
private static final String MODULE_NAME = ILeappAnalyzerModuleFactory.getModuleName(); private static final String MODULE_NAME = ILeappAnalyzerModuleFactory.getModuleName();
@ -77,7 +129,7 @@ public final class LeappFileProcessor {
private final Map<String, String> tsvFiles; private final Map<String, String> tsvFiles;
private final Map<String, String> tsvFileArtifacts; private final Map<String, String> tsvFileArtifacts;
private final Map<String, String> tsvFileArtifactComments; private final Map<String, String> tsvFileArtifactComments;
private final Map<String, List<List<String>>> tsvFileAttributes; private final Map<String, List<TsvColumn>> tsvFileAttributes;
Blackboard blkBoard; Blackboard blkBoard;
@ -104,9 +156,7 @@ public final class LeappFileProcessor {
"LeappFileProcessor.Leapp.cancelled=Leapp run was canceled", "LeappFileProcessor.Leapp.cancelled=Leapp run was canceled",
"LeappFileProcessor.completed=Leapp Processing Completed", "LeappFileProcessor.completed=Leapp Processing Completed",
"LeappFileProcessor.error.reading.Leapp.directory=Error reading Leapp Output Directory"}) "LeappFileProcessor.error.reading.Leapp.directory=Error reading Leapp Output Directory"})
public ProcessResult processFiles(Content dataSource, Path moduleOutputPath, AbstractFile LeappFile) { public ProcessResult processFiles(Content dataSource, Path moduleOutputPath, AbstractFile LeappFile) {
try { try {
List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath); List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath);
processLeappFiles(LeappTsvOutputFiles, LeappFile); processLeappFiles(LeappTsvOutputFiles, LeappFile);
@ -123,7 +173,7 @@ public final class LeappFileProcessor {
try { try {
List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath); List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath);
processLeappFiles(LeappTsvOutputFiles, dataSource); processLeappFiles(LeappTsvOutputFiles, dataSource);
} catch (IOException | IngestModuleException ex) { } catch (IngestModuleException ex) {
logger.log(Level.SEVERE, String.format("Error trying to process Leapp output files in directory %s. ", moduleOutputPath.toString()), ex); //NON-NLS logger.log(Level.SEVERE, String.format("Error trying to process Leapp output files in directory %s. ", moduleOutputPath.toString()), ex); //NON-NLS
return ProcessResult.ERROR; return ProcessResult.ERROR;
} }
@ -162,7 +212,7 @@ public final class LeappFileProcessor {
* Process the Leapp files that were found that match the xml mapping file * Process the Leapp files that were found that match the xml mapping file
* *
* @param LeappFilesToProcess List of files to process * @param LeappFilesToProcess List of files to process
* @param LeappImageFile Abstract file to create artifact for * @param LeappImageFile Abstract file to create artifact for
* *
* @throws FileNotFoundException * @throws FileNotFoundException
* @throws IOException * @throws IOException
@ -174,7 +224,7 @@ public final class LeappFileProcessor {
String fileName = FilenameUtils.getName(LeappFileName); String fileName = FilenameUtils.getName(LeappFileName);
File LeappFile = new File(LeappFileName); File LeappFile = new File(LeappFileName);
if (tsvFileAttributes.containsKey(fileName)) { if (tsvFileAttributes.containsKey(fileName)) {
List<List<String>> attrList = tsvFileAttributes.get(fileName); List<TsvColumn> attrList = tsvFileAttributes.get(fileName);
try { try {
BlackboardArtifact.Type artifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(tsvFileArtifacts.get(fileName)); BlackboardArtifact.Type artifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(tsvFileArtifacts.get(fileName));
@ -197,26 +247,34 @@ public final class LeappFileProcessor {
* Process the Leapp files that were found that match the xml mapping file * Process the Leapp files that were found that match the xml mapping file
* *
* @param LeappFilesToProcess List of files to process * @param LeappFilesToProcess List of files to process
* @param dataSource The data source. * @param dataSource The data source.
* *
* @throws FileNotFoundException * @throws FileNotFoundException
* @throws IOException * @throws IOException
*/ */
private void processLeappFiles(List<String> LeappFilesToProcess, Content dataSource) throws FileNotFoundException, IOException, IngestModuleException { private void processLeappFiles(List<String> LeappFilesToProcess, Content dataSource) throws IngestModuleException {
List<BlackboardArtifact> bbartifacts = new ArrayList<>(); List<BlackboardArtifact> bbartifacts = new ArrayList<>();
for (String LeappFileName : LeappFilesToProcess) { for (String LeappFileName : LeappFilesToProcess) {
String fileName = FilenameUtils.getName(LeappFileName); String fileName = FilenameUtils.getName(LeappFileName);
File LeappFile = new File(LeappFileName); File LeappFile = new File(LeappFileName);
if (tsvFileAttributes.containsKey(fileName)) { if (tsvFileAttributes.containsKey(fileName)) {
List<List<String>> attrList = tsvFileAttributes.get(fileName); List<TsvColumn> attrList = tsvFileAttributes.get(fileName);
BlackboardArtifact.Type artifactType = null;
try { try {
BlackboardArtifact.Type artifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(tsvFileArtifacts.get(fileName)); artifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(tsvFileArtifacts.get(fileName));
processFile(LeappFile, attrList, fileName, artifactType, bbartifacts, dataSource);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
throw new IngestModuleException(String.format("Error getting Blackboard Artifact Type for %s", tsvFileArtifacts.get(fileName)), ex); logger.log(Level.SEVERE, String.format("Error getting Blackboard Artifact Type for %s", tsvFileArtifacts.get(fileName)), ex);
}
if (artifactType == null) {
continue;
}
try {
processFile(LeappFile, attrList, fileName, artifactType, bbartifacts, dataSource);
} catch (TskCoreException | IOException ex) {
logger.log(Level.SEVERE, String.format("Error processing file at %s", LeappFile.toString()), ex);
} }
} }
@ -228,26 +286,34 @@ public final class LeappFileProcessor {
} }
private void processFile(File LeappFile, List<List<String>> attrList, String fileName, BlackboardArtifact.Type artifactType, private void processFile(File LeappFile, List<TsvColumn> attrList, String fileName, BlackboardArtifact.Type artifactType,
List<BlackboardArtifact> bbartifacts, Content dataSource) throws FileNotFoundException, IOException, IngestModuleException, List<BlackboardArtifact> bbartifacts, Content dataSource) throws FileNotFoundException, IOException, IngestModuleException,
TskCoreException { TskCoreException {
if (LeappFile == null || !LeappFile.exists() || fileName == null) {
logger.log(Level.WARNING, String.format("Leap file: %s is null or does not exist", LeappFile == null ? LeappFile.toString() : "<null>"));
return;
} else if (attrList == null || artifactType == null || dataSource == null) {
logger.log(Level.WARNING, String.format("attribute list, artifact type or dataSource not provided for %s", LeappFile == null ? LeappFile.toString() : "<null>"));
return;
}
try (BufferedReader reader = new BufferedReader(new FileReader(LeappFile))) { try (BufferedReader reader = new BufferedReader(new FileReader(LeappFile))) {
String line = reader.readLine(); String header = reader.readLine();
// Check first line, if it is null then no heading so nothing to match to, close and go to next file. // Check first line, if it is null then no heading so nothing to match to, close and go to next file.
if (line != null) { if (header != null) {
Map<Integer, String> columnNumberToProcess = findColumnsToProcess(line, attrList); Map<Integer, String> columnNumberToProcess = findColumnsToProcess(fileName, header, attrList);
line = reader.readLine(); String line = reader.readLine();
while (line != null) { while (line != null) {
Collection<BlackboardAttribute> bbattributes = processReadLine(line, columnNumberToProcess, fileName); Collection<BlackboardAttribute> bbattributes = processReadLine(line, columnNumberToProcess, fileName);
if (artifactType == null) {
logger.log(Level.SEVERE, "Error trying to process Leapp output files in directory . "); //NON-NLS
}
if (!bbattributes.isEmpty() && !blkBoard.artifactExists(dataSource, BlackboardArtifact.ARTIFACT_TYPE.fromID(artifactType.getTypeID()), bbattributes)) { if (!bbattributes.isEmpty() && !blkBoard.artifactExists(dataSource, BlackboardArtifact.ARTIFACT_TYPE.fromID(artifactType.getTypeID()), bbattributes)) {
BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType.getTypeID(), dataSource, bbattributes); BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType.getTypeID(), dataSource, bbattributes);
if (bbartifact != null) { if (bbartifact != null) {
bbartifacts.add(bbartifact); bbartifacts.add(bbartifact);
} }
} }
line = reader.readLine(); line = reader.readLine();
} }
} }
@ -258,20 +324,28 @@ public final class LeappFileProcessor {
/** /**
* Process the line read and create the necessary attributes for it * Process the line read and create the necessary attributes for it
* *
* @param line a tsv line to process that was read * @param line a tsv line to process that was read
* @param columnNumberToProcess Which columns to process in the tsv line * @param columnNumberToProcess Which columns to process in the tsv line
* @param fileName name of file begin processed * @param fileName name of file begin processed
* *
* @return * @return
*/ */
private Collection<BlackboardAttribute> processReadLine(String line, Map<Integer, String> columnNumberToProcess, String fileName) throws IngestModuleException { private Collection<BlackboardAttribute> processReadLine(String line, Map<Integer, String> columnNumberToProcess, String fileName) throws IngestModuleException {
if (MapUtils.isEmpty(columnNumberToProcess)) {
return Collections.emptyList();
} else if (line == null) {
logger.log(Level.WARNING, "Line is null. Returning empty list for attributes.");
return Collections.emptyList();
}
String[] columnValues; String[] columnValues;
// Check to see if the 2 values are equal, they may not be equal if there is no corresponding data in the line. // Check to see if the 2 values are equal, they may not be equal if there is no corresponding data in the line.
// or if the size of the line to split is not equal to the column numbers we are looking to process. This
// can happen when the last value of the tsv line has no data in it.
// If this happens then adding an empty value(s) for each columnValue where data does not exist // If this happens then adding an empty value(s) for each columnValue where data does not exist
Integer maxColumnNumber = Collections.max(columnNumberToProcess.keySet()); Integer maxColumnNumber = Collections.max(columnNumberToProcess.keySet());
if (maxColumnNumber > line.split("\\t").length) { if ((maxColumnNumber > line.split("\\t").length) || (columnNumberToProcess.size() > line.split("\\t").length)) {
columnValues = Arrays.copyOf(line.split("\\t"), maxColumnNumber + 1); columnValues = Arrays.copyOf(line.split("\\t"), maxColumnNumber + 1);
} else { } else {
columnValues = line.split("\\t"); columnValues = line.split("\\t");
@ -283,15 +357,17 @@ public final class LeappFileProcessor {
Integer columnNumber = columnToProcess.getKey(); Integer columnNumber = columnToProcess.getKey();
String attributeName = columnToProcess.getValue(); String attributeName = columnToProcess.getValue();
try { if (columnValues[columnNumber] != null) {
BlackboardAttribute.Type attributeType = Case.getCurrentCase().getSleuthkitCase().getAttributeType(attributeName.toUpperCase()); try {
if (attributeType == null) { BlackboardAttribute.Type attributeType = Case.getCurrentCase().getSleuthkitCase().getAttributeType(attributeName.toUpperCase());
break; if (attributeType == null) {
continue;
}
String attrType = attributeType.getValueType().getLabel().toUpperCase();
checkAttributeType(bbattributes, attrType, columnValues, columnNumber, attributeType, fileName);
} catch (TskCoreException ex) {
throw new IngestModuleException(String.format("Error getting Attribute type for Attribute Name %s", attributeName), ex); //NON-NLS
} }
String attrType = attributeType.getValueType().getLabel().toUpperCase();
checkAttributeType(bbattributes, attrType, columnValues, columnNumber, attributeType, fileName);
} catch (TskCoreException ex) {
throw new IngestModuleException(String.format("Error getting Attribute type for Attribute Name %s", attributeName), ex); //NON-NLS
} }
} }
@ -303,34 +379,60 @@ public final class LeappFileProcessor {
} }
private void checkAttributeType(Collection<BlackboardAttribute> bbattributes, String attrType, String[] columnValues, Integer columnNumber, BlackboardAttribute.Type attributeType, private void checkAttributeType(Collection<BlackboardAttribute> bbattributes, String attrType, String[] columnValues, int columnNumber, BlackboardAttribute.Type attributeType,
String fileName) { String fileName) {
if (columnValues == null || columnNumber < 0 || columnNumber > columnValues.length || columnValues[columnNumber] == null) {
logger.log(Level.WARNING, String.format("Unable to determine column value at index %d in columnValues: %s",
columnNumber,
columnValues == null ? "<null>" : "[" + String.join(", ", columnValues) + "]"));
return;
}
String columnValue = columnValues[columnNumber];
if (attrType.matches("STRING")) { if (attrType.matches("STRING")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValues[columnNumber])); bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValue));
} else if (attrType.matches("INTEGER")) { } else if (attrType.matches("INTEGER")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Integer.valueOf(columnValues[columnNumber]))); try {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Integer.valueOf(columnValue)));
} catch (NumberFormatException ex) {
logger.log(Level.WARNING, String.format("Unable to format %s as an integer.", columnValue), ex);
}
} else if (attrType.matches("LONG")) { } else if (attrType.matches("LONG")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Long.valueOf(columnValues[columnNumber]))); try {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Long.valueOf(columnValue)));
} catch (NumberFormatException ex) {
logger.log(Level.WARNING, String.format("Unable to format %s as an long.", columnValue), ex);
}
} else if (attrType.matches("DOUBLE")) { } else if (attrType.matches("DOUBLE")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Double.valueOf(columnValues[columnNumber]))); try {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Double.valueOf(columnValue)));
} catch (NumberFormatException ex) {
logger.log(Level.WARNING, String.format("Unable to format %s as an double.", columnValue), ex);
}
} else if (attrType.matches("BYTE")) { } else if (attrType.matches("BYTE")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Byte.valueOf(columnValues[columnNumber]))); try {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Byte.valueOf(columnValue)));
} catch (NumberFormatException ex) {
logger.log(Level.WARNING, String.format("Unable to format %s as an byte.", columnValue), ex);
}
} else if (attrType.matches("DATETIME")) { } else if (attrType.matches("DATETIME")) {
// format of data should be the same in all the data and the format is 2020-03-28 01:00:17 // format of data should be the same in all the data and the format is 2020-03-28 01:00:17
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-d HH:mm:ss", US); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-d HH:mm:ss", US);
Long dateLong = Long.valueOf(0); Long dateLong = Long.valueOf(0);
try { try {
Date newDate = dateFormat.parse(columnValues[columnNumber]); Date newDate = dateFormat.parse(columnValue);
dateLong = newDate.getTime() / 1000; dateLong = newDate.getTime() / 1000;
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, dateLong)); bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, dateLong));
} catch (ParseException ex) { } catch (ParseException ex) {
// catching error and displaying date that could not be parsed // catching error and displaying date that could not be parsed
// we set the timestamp to 0 and continue on processing // we set the timestamp to 0 and continue on processing
logger.log(Level.WARNING, String.format("Failed to parse date/time %s for attribute type %s in file %s.", columnValues[columnNumber], attributeType.getDisplayName(), fileName)); //NON-NLS logger.log(Level.WARNING, String.format("Failed to parse date/time %s for attribute type %s in file %s.", columnValue, attributeType.getDisplayName(), fileName)); //NON-NLS
} }
} else if (attrType.matches("JSON")) { } else if (attrType.matches("JSON")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValues[columnNumber])); bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValue));
} else { } else {
// Log this and continue on with processing // Log this and continue on with processing
logger.log(Level.WARNING, String.format("Attribute Type %s not defined.", attrType)); //NON-NLS logger.log(Level.WARNING, String.format("Attribute Type %s not defined.", attrType)); //NON-NLS
@ -343,29 +445,43 @@ public final class LeappFileProcessor {
* headings to the columns in the XML mapping file so we know which columns * headings to the columns in the XML mapping file so we know which columns
* to process. * to process.
* *
* @param line a tsv heading line of the columns in the file * @param fileName The name of the file in which these column headers exist.
* @param line a tsv heading line of the columns in the file
* @param attrList the list of headings we want to process * @param attrList the list of headings we want to process
* *
* @return the numbered column(s) and attribute(s) we want to use for the * @return the numbered column(s) and attribute(s) we want to use for the
* column(s) * column(s)
*/ */
private Map<Integer, String> findColumnsToProcess(String line, List<List<String>> attrList) { private Map<Integer, String> findColumnsToProcess(String fileName, String line, List<TsvColumn> attrList) {
String[] columnNames = line.split("\\t"); String[] columnNames = line.split("\\t");
HashMap<Integer, String> columnsToProcess = new HashMap<>(); HashMap<Integer, String> columnsToProcess = new HashMap<>();
Integer columnPosition = 0; Integer columnPosition = 0;
for (String columnName : columnNames) { for (String columnName : columnNames) {
// for some reason the first column of the line has unprintable characters so removing them // for some reason the first column of the line has unprintable characters so removing them
String cleanColumnName = columnName.replaceAll("[^\\n\\r\\t\\p{Print}]", ""); String cleanColumnName = columnName.trim().replaceAll("[^\\n\\r\\t\\p{Print}]", "");
for (List<String> atList : attrList) { for (TsvColumn tsvColumn : attrList) {
if (atList.contains(cleanColumnName.toLowerCase())) { if (cleanColumnName.equalsIgnoreCase(tsvColumn.getColumnName())) {
columnsToProcess.put(columnPosition, atList.get(0)); columnsToProcess.put(columnPosition, tsvColumn.getAttributeName());
break; break;
} }
} }
columnPosition++; columnPosition++;
} }
if (columnsToProcess.size() != attrList.size()) {
String missingColumns = IntStream.range(0, attrList.size())
.filter((idx) -> !columnsToProcess.containsKey(attrList.get(idx).getAttributeName()))
.mapToObj((idx) -> String.format("'%s'", attrList.get(idx).getColumnName() == null ? "<null>" : attrList.get(idx).getColumnName()))
.collect(Collectors.joining(", "));
logger.log(Level.WARNING, String.format("Columns size expected not found in file %s based on xml from %s. Column Keys Missing = [%s]; Header Line = '%s'.",
this.xmlFile == null ? "<null>" : this.xmlFile,
fileName,
missingColumns,
line));
}
return columnsToProcess; return columnsToProcess;
} }
@ -424,6 +540,18 @@ public final class LeappFileProcessor {
String comment = nnm.getNamedItem("comment").getNodeValue(); String comment = nnm.getNamedItem("comment").getNodeValue();
String parentName = artifactNlist.item(k).getParentNode().getAttributes().getNamedItem("filename").getNodeValue(); String parentName = artifactNlist.item(k).getParentNode().getAttributes().getNamedItem("filename").getNodeValue();
BlackboardArtifact.Type foundArtifactType = null;
try {
foundArtifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(artifactName);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("There was an issue that arose while trying to fetch artifact type for %s.", artifactName), ex);
}
if (foundArtifactType == null) {
logger.log(Level.SEVERE, String.format("No known artifact mapping found for [artifact: %s, %s]",
artifactName, getXmlFileIdentifier(parentName)));
}
tsvFileArtifacts.put(parentName, artifactName); tsvFileArtifacts.put(parentName, artifactName);
if (!comment.toLowerCase().matches("null")) { if (!comment.toLowerCase().matches("null")) {
@ -433,29 +561,66 @@ public final class LeappFileProcessor {
} }
private String getXmlFileIdentifier(String fileName) {
return String.format("file: %s, filename: %s",
this.xmlFile == null ? "<null>" : this.xmlFile,
fileName == null ? "<null>" : fileName);
}
private String getXmlAttrIdentifier(String fileName, String attributeName) {
return String.format("attribute: %s %s",
attributeName == null ? "<null>" : attributeName,
getXmlFileIdentifier(fileName));
}
private void getAttributeNodes(Document xmlinput) { private void getAttributeNodes(Document xmlinput) {
NodeList attributeNlist = xmlinput.getElementsByTagName("AttributeName"); //NON-NLS NodeList attributeNlist = xmlinput.getElementsByTagName("AttributeName"); //NON-NLS
for (int k = 0; k < attributeNlist.getLength(); k++) { for (int k = 0; k < attributeNlist.getLength(); k++) {
List<String> attributeList = new ArrayList<>();
NamedNodeMap nnm = attributeNlist.item(k).getAttributes(); NamedNodeMap nnm = attributeNlist.item(k).getAttributes();
String attributeName = nnm.getNamedItem("attributename").getNodeValue(); String attributeName = nnm.getNamedItem("attributename").getNodeValue();
if (!attributeName.toLowerCase().matches("null")) { if (!attributeName.toLowerCase().matches("null")) {
String columnName = nnm.getNamedItem("columnName").getNodeValue(); String columnName = nnm.getNamedItem("columnName").getNodeValue();
String required = nnm.getNamedItem("required").getNodeValue(); String required = nnm.getNamedItem("required").getNodeValue();
String parentName = attributeNlist.item(k).getParentNode().getParentNode().getAttributes().getNamedItem("filename").getNodeValue(); String parentName = attributeNlist.item(k).getParentNode().getParentNode().getAttributes().getNamedItem("filename").getNodeValue();
attributeList.add(attributeName.toLowerCase()); BlackboardAttribute.Type foundAttrType = null;
attributeList.add(columnName.toLowerCase()); try {
attributeList.add(required.toLowerCase()); foundAttrType = Case.getCurrentCase().getSleuthkitCase().getAttributeType(attributeName.toUpperCase());
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("There was an issue that arose while trying to fetch attribute type for %s.", attributeName), ex);
}
if (foundAttrType == null) {
logger.log(Level.SEVERE, String.format("No known attribute mapping found for [%s]", getXmlAttrIdentifier(parentName, attributeName)));
}
if (required != null && required.compareToIgnoreCase("yes") != 0 && required.compareToIgnoreCase("no") != 0) {
logger.log(Level.SEVERE, String.format("Required value %s did not match 'yes' or 'no' for [%s]",
required, getXmlAttrIdentifier(parentName, attributeName)));
}
if (columnName == null) {
logger.log(Level.SEVERE, String.format("No column name provided for [%s]", getXmlAttrIdentifier(parentName, attributeName)));
} else if (columnName.trim().length() != columnName.length()) {
logger.log(Level.SEVERE, String.format("Column name '%s' starts or ends with whitespace for [%s]", columnName, getXmlAttrIdentifier(parentName, attributeName)));
} else if (columnName.matches("[^ \\S]")) {
logger.log(Level.SEVERE, String.format("Column name '%s' contains invalid characters [%s]", columnName, getXmlAttrIdentifier(parentName, attributeName)));
}
TsvColumn thisCol = new TsvColumn(
attributeName.toLowerCase(),
columnName.toLowerCase(),
"yes".compareToIgnoreCase(required) == 0);
if (tsvFileAttributes.containsKey(parentName)) { if (tsvFileAttributes.containsKey(parentName)) {
List<List<String>> attrList = tsvFileAttributes.get(parentName); List<TsvColumn> attrList = tsvFileAttributes.get(parentName);
attrList.add(attributeList); attrList.add(thisCol);
tsvFileAttributes.replace(parentName, attrList); tsvFileAttributes.replace(parentName, attrList);
} else { } else {
List<List<String>> attrList = new ArrayList<>(); List<TsvColumn> attrList = new ArrayList<>();
attrList.add(attributeList); attrList.add(thisCol);
tsvFileAttributes.put(parentName, attrList); tsvFileAttributes.put(parentName, attrList);
} }
} }
@ -466,13 +631,12 @@ public final class LeappFileProcessor {
/** /**
* Generic method for creating a blackboard artifact with attributes * Generic method for creating a blackboard artifact with attributes
* *
* @param type is a blackboard.artifact_type enum to determine which * @param type is a blackboard.artifact_type enum to determine which type
* type the artifact should be * the artifact should be
* @param abstractFile is the AbstractFile object that needs to have the * @param abstractFile is the AbstractFile object that needs to have the
* artifact added for it * artifact added for it
* @param bbattributes is the collection of blackboard attributes that need * @param bbattributes is the collection of blackboard attributes that need
* to be added to the artifact after the artifact has * to be added to the artifact after the artifact has been created
* been created
* *
* @return The newly-created artifact, or null on error * @return The newly-created artifact, or null on error
*/ */
@ -490,13 +654,12 @@ public final class LeappFileProcessor {
/** /**
* Generic method for creating a blackboard artifact with attributes * Generic method for creating a blackboard artifact with attributes
* *
* @param type is a blackboard.artifact_type enum to determine which * @param type is a blackboard.artifact_type enum to determine which type
* type the artifact should be * the artifact should be
* @param dataSource is the Content object that needs to have the artifact * @param dataSource is the Content object that needs to have the artifact
* added for it * added for it
* @param bbattributes is the collection of blackboard attributes that need * @param bbattributes is the collection of blackboard attributes that need
* to be added to the artifact after the artifact has * to be added to the artifact after the artifact has been created
* been created
* *
* @return The newly-created artifact, or null on error * @return The newly-created artifact, or null on error
*/ */
@ -515,7 +678,7 @@ public final class LeappFileProcessor {
* Method to post a list of BlackboardArtifacts to the blackboard. * Method to post a list of BlackboardArtifacts to the blackboard.
* *
* @param artifacts A list of artifacts. IF list is empty or null, the * @param artifacts A list of artifacts. IF list is empty or null, the
* function will return. * function will return.
*/ */
void postArtifacts(Collection<BlackboardArtifact> artifacts) { void postArtifacts(Collection<BlackboardArtifact> artifacts) {
if (artifacts == null || artifacts.isEmpty()) { if (artifacts == null || artifacts.isEmpty()) {
@ -535,7 +698,44 @@ public final class LeappFileProcessor {
* @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException * @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException
*/ */
private void configExtractor() throws IOException { private void configExtractor() throws IOException {
PlatformUtil.extractResourceToUserConfigDir(LeappFileProcessor.class, xmlFile, true); PlatformUtil.extractResourceToUserConfigDir(LeappFileProcessor.class,
xmlFile, true);
} }
private static final Set<String> ALLOWED_EXTENSIONS = new HashSet<>(Arrays.asList("zip", "tar", "tgz"));
/**
* Find the files that will be processed by the iLeapp program
*
* @param dataSource
*
* @return List of abstract files to process.
*/
static List<AbstractFile> findLeappFilesToProcess(Content dataSource) {
List<AbstractFile> leappFiles = new ArrayList<>();
FileManager fileManager = getCurrentCase().getServices().getFileManager();
// findFiles use the SQL wildcard % in the file name
try {
leappFiles = fileManager.findFiles(dataSource, "%", "/"); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "No files found to process"); //NON-NLS
return leappFiles;
}
List<AbstractFile> leappFilesToProcess = new ArrayList<>();
for (AbstractFile leappFile : leappFiles) {
if (((leappFile.getLocalAbsPath() != null)
&& !leappFile.isVirtual())
&& leappFile.getNameExtension() != null
&& ALLOWED_EXTENSIONS.contains(leappFile.getNameExtension().toLowerCase())) {
leappFilesToProcess.add(leappFile);
}
}
return leappFilesToProcess;
}
} }

View File

@ -31,18 +31,18 @@
<FileName filename="accounts ce 0.tsv" description="Accounts_ce"> <FileName filename="accounts ce 0.tsv" description="Accounts_ce">
<ArtifactName artifactname="TSK_SERVICE_ACCOUNT" comment="accounts ce 0"> <ArtifactName artifactname="TSK_SERVICE_ACCOUNT" comment="accounts ce 0">
<AttributeName attributename="TSK_USER_ID" columnName="Name" required="yes" /> <AttributeName attributename="TSK_USER_ID" columnName="Name" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName=" Type" required="yes" /> <AttributeName attributename="TSK_PROG_NAME" columnName="Type" required="yes" />
<AttributeName attributename="TSK_PASSWORD" columnName=" Password" required="yes" /> <AttributeName attributename="TSK_PASSWORD" columnName="Password" required="yes" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="authtokens 0.tsv" description="Authtokens"> <FileName filename="authtokens 0.tsv" description="Authtokens">
<ArtifactName artifactname="TSK_SERVICE_ACCOUNT" comment="Authtokens"> <ArtifactName artifactname="TSK_SERVICE_ACCOUNT" comment="Authtokens">
<AttributeName attributename="null" columnName="ID" required="no" /> <AttributeName attributename="null" columnName="ID" required="no" />
<AttributeName attributename="TSK_USER_ID" columnName=" Name" required="yes" /> <AttributeName attributename="TSK_USER_ID" columnName="Name" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName=" Account Type" required="yes" /> <AttributeName attributename="TSK_PROG_NAME" columnName="Account Type" required="yes" />
<AttributeName attributename="null" columnName="Authtoken Type" required="no" /> <AttributeName attributename="null" columnName="Authtoken Type" required="no" />
<AttributeName attributename="TSK_PASSWORD" columnName=" Authtoken" required="yes" /> <AttributeName attributename="TSK_PASSWORD" columnName="Authtoken" required="yes" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -56,17 +56,17 @@
<FileName filename="Browser Bookmarks.tsv" description="Browser Bookmarks"> <FileName filename="Browser Bookmarks.tsv" description="Browser Bookmarks">
<ArtifactName artifactname="TSK_WEB_BOOKMARK" comment="Browser Bookmarks"> <ArtifactName artifactname="TSK_WEB_BOOKMARK" comment="Browser Bookmarks">
<AttributeName attributename="TSK_DATETIME_CREATED " columnName="Added Date" required="yes" /> <AttributeName attributename="TSK_DATETIME_CREATED" columnName="Added Date" required="yes" />
<AttributeName attributename="TSK_URL" columnName=" URL" required="yes" /> <AttributeName attributename="TSK_URL" columnName="URL" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName=" Name" required="yes" /> <AttributeName attributename="TSK_TITLE" columnName="Name" required="yes" />
<AttributeName attributename="null" columnName=" Parent" required="no" /> <AttributeName attributename="null" columnName="Parent" required="no" />
<AttributeName attributename="null" columnName=" Type" required="no" /> <AttributeName attributename="null" columnName="Type" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="Browser cookies.tsv" description="Browser Cookies"> <FileName filename="Browser cookies.tsv" description="Browser Cookies">
<ArtifactName artifactname="TSK_WEB_COOKIE" comment="Browser Cookies"> <ArtifactName artifactname="TSK_WEB_COOKIE" comment="Browser Cookies">
<AttributeName attributename="TSK_DATETIME_ACCESS" columnName="Last Access Date" required="yes" /> <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Date" required="yes" />
<AttributeName attributename="TSK_DOMAIN" columnName="Host" required="yes" /> <AttributeName attributename="TSK_DOMAIN" columnName="Host" required="yes" />
<AttributeName attributename="TSK_NAME" columnName="Name" required="yes" /> <AttributeName attributename="TSK_NAME" columnName="Name" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName="Value" required="yes" /> <AttributeName attributename="TSK_VALUE" columnName="Value" required="yes" />
@ -108,11 +108,11 @@
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="Browser Offline Pages"> <ArtifactName artifactname="TSK_WEB_HISTORY" comment="Browser Offline Pages">
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Creation Time" required="yes" /> <AttributeName attributename="TSK_DATETIME_CREATED" columnName="Creation Time" required="yes" />
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Time" required="yes" /> <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Time" required="yes" />
<AttributeName attributename="TSK_URL" columnName=" Online URL" required="yes" /> <AttributeName attributename="TSK_URL" columnName="Online URL" required="yes" />
<AttributeName attributename="null" columnName=" File Path" required="no" /> <AttributeName attributename="null" columnName="File Path" required="no" />
<AttributeName attributename="TSK_TITLE" columnName=" Title" required="no" /> <AttributeName attributename="TSK_TITLE" columnName="Title" required="no" />
<AttributeName attributename="null" columnName=" Access Count" required="no" /> <AttributeName attributename="null" columnName="Access Count" required="no" />
<AttributeName attributename="null" columnName=" File Size" required="no" /> <AttributeName attributename="null" columnName="File Size" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -153,17 +153,17 @@
<FileName filename="Chrome Bookmarks.tsv" description="Chrome Bookmarks"> <FileName filename="Chrome Bookmarks.tsv" description="Chrome Bookmarks">
<ArtifactName artifactname="TSK_WEB_BOOKMARK" comment="Chrome Bookmarks"> <ArtifactName artifactname="TSK_WEB_BOOKMARK" comment="Chrome Bookmarks">
<AttributeName attributename="TSK_DATETIME_CREATED " columnName="Added Date" required="yes" /> <AttributeName attributename="TSK_DATETIME_CREATED" columnName="Added Date" required="yes" />
<AttributeName attributename="TSK_URL" columnName=" URL" required="yes" /> <AttributeName attributename="TSK_URL" columnName="URL" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName=" Name" required="yes" /> <AttributeName attributename="TSK_TITLE" columnName="Name" required="yes" />
<AttributeName attributename="null" columnName=" Parent" required="no" /> <AttributeName attributename="null" columnName="Parent" required="no" />
<AttributeName attributename="null" columnName=" Type" required="no" /> <AttributeName attributename="null" columnName="Type" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="Chrome cookies.tsv" description="Chrome Cookies"> <FileName filename="Chrome cookies.tsv" description="Chrome Cookies">
<ArtifactName artifactname="TSK_WEB_COOKIE" comment="Chrome Cookies"> <ArtifactName artifactname="TSK_WEB_COOKIE" comment="Chrome Cookies">
<AttributeName attributename="TSK_DATETIME_ACCESS" columnName="Last Access Date" required="yes" /> <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Date" required="yes" />
<AttributeName attributename="TSK_DOMAIN" columnName="Host" required="yes" /> <AttributeName attributename="TSK_DOMAIN" columnName="Host" required="yes" />
<AttributeName attributename="TSK_NAME" columnName="Name" required="yes" /> <AttributeName attributename="TSK_NAME" columnName="Name" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName="Value" required="yes" /> <AttributeName attributename="TSK_VALUE" columnName="Value" required="yes" />
@ -197,11 +197,11 @@
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="Chrome Offline Pages"> <ArtifactName artifactname="TSK_WEB_HISTORY" comment="Chrome Offline Pages">
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Creation Time" required="yes" /> <AttributeName attributename="TSK_DATETIME_CREATED" columnName="Creation Time" required="yes" />
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Time" required="yes" /> <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Time" required="yes" />
<AttributeName attributename="TSK_URL" columnName=" Online URL" required="yes" /> <AttributeName attributename="TSK_URL" columnName="Online URL" required="yes" />
<AttributeName attributename="null" columnName=" File Path" required="no" /> <AttributeName attributename="null" columnName="File Path" required="no" />
<AttributeName attributename="TSK_TITLE" columnName=" Title" required="no" /> <AttributeName attributename="TSK_TITLE" columnName="Title" required="no" />
<AttributeName attributename="null" columnName=" Access Count" required="no" /> <AttributeName attributename="null" columnName="Access Count" required="no" />
<AttributeName attributename="null" columnName=" File Size" required="no" /> <AttributeName attributename="null" columnName="File Size" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -224,6 +224,79 @@
</ArtifactName> </ArtifactName>
</FileName> </FileName>
<FileName filename="Edge Bookmarks.tsv" description="Edge Bookmarks">
<ArtifactName artifactname="TSK_WEB_BOOKMARK" comment="Chrome Bookmarks">
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Added Date" required="yes" />
<AttributeName attributename="TSK_URL" columnName="URL" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName="Name" required="yes" />
<AttributeName attributename="null" columnName="Parent" required="no" />
<AttributeName attributename="null" columnName="Type" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Edge cookies.tsv" description="Edge Cookies">
<ArtifactName artifactname="TSK_WEB_COOKIE" comment="Edge Cookies">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Date" required="yes" />
<AttributeName attributename="TSK_DOMAIN" columnName="Host" required="yes" />
<AttributeName attributename="TSK_NAME" columnName="Name" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName="Value" required="yes" />
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Created Date" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="Expiration Date" required="yes" />
<AttributeName attributename="TSK_PATH" columnName="Path" required="yes" />
</ArtifactName>
</FileName>
<FileName filename="Edge History.tsv" description="Edge History">
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="Edge History">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Visit Time" required="yes"/>
<AttributeName attributename="TSK_URL" columnName="URL" required="yes"/>
<AttributeName attributename="TSK_TITLE" columnName="Title" required="yes"/>
<AttributeName attributename="null" columnName="Visit Count" required="no"/>
<AttributeName attributename="null" columnName="Hidden" required="no"/>
</ArtifactName>
</FileName>
<FileName filename="Edge login data.tsv" description="Edge Login Data">
<ArtifactName artifactname="TSK_SERVICE_ACCOUNT" comment="Edge Login">
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Created Time" required="yes" />
<AttributeName attributename="TSK_USER_NAME" columnName="Username" required="yes" />
<AttributeName attributename="TSK_PASSWORD" columnName="Password" required="yes" />
<AttributeName attributename="TSK_URL" columnName="Origin URL" required="no" />
<AttributeName attributename="null" columnName="Blacklisted by User" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Edge offline pages.tsv" description="Edge Offline Pages">
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="Edge Offline Pages">
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Creation Time" required="yes" />
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Time" required="yes" />
<AttributeName attributename="TSK_URL" columnName="Online URL" required="yes" />
<AttributeName attributename="null" columnName="File Path" required="no" />
<AttributeName attributename="TSK_TITLE" columnName="Title" required="no" />
<AttributeName attributename="null" columnName="Access Count" required="no" />
<AttributeName attributename="null" columnName="File Size" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Edge search terms.tsv" description="Edge Search Terms">
<ArtifactName artifactname="TSK_WEB_SEARCH_QUERY" comment="Chrome Search Terms">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Visit Time" required="yes"/>
<AttributeName attributename="TSK_TEXT" columnName="Search Term" required="yes"/>
<AttributeName attributename="TSK_URL" columnName="URL" required="yes"/>
<AttributeName attributename="null" columnName="Title" required="no"/>
<AttributeName attributename="null" columnName="Visit Count" required="no"/>
</ArtifactName>
</FileName>
<FileName filename="Edge top sites.tsv" description="Edge Top Sites">
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="Edge Top Sites">
<AttributeName attributename="TSK_URL" columnName="URL" required="yes" />
<AttributeName attributename="null" columnName="Rank" required="no" />
<AttributeName attributename="TSK_TITLE" columnName="Title" required="no" />
<AttributeName attributename="null" columnName="Redirects" required="no" />
</ArtifactName>
</FileName>
<FileName filename="google play searches.tsv" description="Google Play Searches"> <FileName filename="google play searches.tsv" description="Google Play Searches">
<ArtifactName artifactname="TSK_WEB_SEARCH_QUERY" comment="Google Play Search"> <ArtifactName artifactname="TSK_WEB_SEARCH_QUERY" comment="Google Play Search">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Timestamp" required="yes" /> <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Timestamp" required="yes" />
@ -233,10 +306,11 @@
</FileName> </FileName>
<FileName filename="google quick search box.tsv" description="Google quick search box"> <FileName filename="google quick search box.tsv" description="Google quick search box">
<ArtifactName artifactname="TSK_WEB_SEARCH_QUERY" comment="Google Quick Search Search"> <ArtifactName artifactname="TSK_WEB_SEARCH_QUERY" comment="Google Quick Search">
<AttributeName attributename="TSK_DATETIME" columnName="File Timestamp" required="yes" /> <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="File Timestamp" required="yes" />
<AttributeName attributename="null" columnName="Type" required="no" /> <AttributeName attributename="null" columnName="Type" required="no" />
<AttributeName attributename="TSK_TEXT" columnName="Queries Response" required="yes" /> <AttributeName attributename="TSK_TEXT" columnName="Queries" required="yes" />
<AttributeName attributename="null" columnName="Response" required="no" />
<AttributeName attributename="null" columnName="Source File" required="no" /> <AttributeName attributename="null" columnName="Source File" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -256,12 +330,12 @@
</FileName> </FileName>
<FileName filename="installed apps vending.tsv" description="Installed Apps (Vending)"> <FileName filename="installed apps vending.tsv" description="Installed Apps (Vending)">
<ArtifactName artifactname="TSK_INSTALLED_PROG" comment="Installed Apps (VEnding)"> <ArtifactName artifactname="TSK_INSTALLED_PROG" comment="Installed Apps (Vending)">
<AttributeName attributename="TSK_DATETIME" columnName="First Download" required="yes" /> <AttributeName attributename="TSK_DATETIME" columnName="First Download" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Package Name" required="yes" /> <AttributeName attributename="TSK_PROG_NAME" columnName="Package Name" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName=" Title" required="yes" /> <AttributeName attributename="TSK_TITLE" columnName="Title" required="yes" />
<AttributeName attributename="null" columnName="Install Reason" required="no" /> <AttributeName attributename="null" columnName="Install Reason" required="no" />
<AttributeName attributename="null" columnName=" Auto Update?" required="no" /> <AttributeName attributename="null" columnName="Auto Update?" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>

View File

@ -42,8 +42,8 @@
<FileName filename="Application State.tsv" description="Application State"> <FileName filename="Application State.tsv" description="Application State">
<ArtifactName artifactname="TSK_INSTALLED_PROG" comment="Application State"> <ArtifactName artifactname="TSK_INSTALLED_PROG" comment="Application State">
<AttributeName attributename="TSK_PROG_NAME" columnName="Bundle ID" required="no" /> <AttributeName attributename="TSK_PROG_NAME" columnName="Bundle ID" required="no" />
<AttributeName attributename="TSK_INSTALLED_PATH" columnName="Bundle Path" required="yes" /> <AttributeName attributename="TSK_PATH" columnName="Bundle Path" required="yes" />
<AttributeName attributename="TSK_INSTALLED_SOURCE" columnName="Sandbox Path" required="yes" /> <AttributeName attributename="TSK_PATH_SOURCE" columnName="Sandbox Path" required="yes" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -84,7 +84,7 @@
<AttributeName attributename="TSK_DATETIME_END" columnName="End Date" required="yes" /> <AttributeName attributename="TSK_DATETIME_END" columnName="End Date" required="yes" />
<AttributeName attributename="null" columnName="End Timezone" required="no" /> <AttributeName attributename="null" columnName="End Timezone" required="no" />
<AttributeName attributename="null" columnName="All Day?" required="no" /> <AttributeName attributename="null" columnName="All Day?" required="no" />
<AttributeName attributename="TSK_CALENDAR_ENTRY" columnName="Summary" required="yes" /> <AttributeName attributename="TSK_CALENDAR_ENTRY_TYPE" columnName="Summary" required="yes" />
<AttributeName attributename="null" columnName="Calendar ID" required="no" /> <AttributeName attributename="null" columnName="Calendar ID" required="no" />
<AttributeName attributename="null" columnName="Last Modified" required="no" /> <AttributeName attributename="null" columnName="Last Modified" required="no" />
</ArtifactName> </ArtifactName>
@ -113,7 +113,7 @@
<AttributeName attributename="null" columnName="Process Name" required="no" /> <AttributeName attributename="null" columnName="Process Name" required="no" />
<AttributeName attributename="null" columnName="WIFI In" required="no" /> <AttributeName attributename="null" columnName="WIFI In" required="no" />
<AttributeName attributename="null" columnName="WIFI Out" required="no" /> <AttributeName attributename="null" columnName="WIFI Out" required="no" />
<AttributeName attributename="TSK_BYTES_RCVD" columnName="WWAN IN" required="yes" /> <AttributeName attributename="TSK_BYTES_RECEIVED" columnName="WWAN IN" required="yes" />
<AttributeName attributename="TSK_BYTES_SENT" columnName="WWAN Out" required="yes" /> <AttributeName attributename="TSK_BYTES_SENT" columnName="WWAN Out" required="yes" />
<AttributeName attributename="null" columnName="Table ID" required="no" /> <AttributeName attributename="null" columnName="Table ID" required="no" />
</ArtifactName> </ArtifactName>
@ -143,13 +143,13 @@
<AttributeName attributename="null" columnName="Start" required="no" /> <AttributeName attributename="null" columnName="Start" required="no" />
<AttributeName attributename="null" columnName="End" required="no" /> <AttributeName attributename="null" columnName="End" required="no" />
<AttributeName attributename="null" columnName="ZSTREAMNAME" required="no" /> <AttributeName attributename="null" columnName="ZSTREAMNAME" required="no" />
<AttributeName attributename="TSK_PROG_NAME" columnName=" ZVALUESTRING" required="no" /> <AttributeName attributename="TSK_PROG_NAME" columnName="ZVALUESTRING" required="no" />
<AttributeName attributename="null" columnName=" Activity Type" required="no" /> <AttributeName attributename="null" columnName="Activity Type" required="no" />
<AttributeName attributename="null" columnName=" Title" required="no" /> <AttributeName attributename="null" columnName="Title" required="no" />
<AttributeName attributename="null" columnName=" Expiration Date" required="no" /> <AttributeName attributename="null" columnName="Expiration Date" required="no" />
<AttributeName attributename="null" columnName=" Content URL" required="no" /> <AttributeName attributename="null" columnName="Content URL" required="no" />
<AttributeName attributename="null" columnName=" Calendar Date" required="no" /> <AttributeName attributename="null" columnName="Calendar Date" required="no" />
<AttributeName attributename="null" columnName=" Calendar End Date" required="no" /> <AttributeName attributename="null" columnName="Calendar End Date" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -160,7 +160,7 @@
<AttributeName attributename="null" columnName="Bundle ID" required="no" /> <AttributeName attributename="null" columnName="Bundle ID" required="no" />
<AttributeName attributename="TSK_CALENDAR_ENTRY_TYPE" columnName="Activity Type" required="yes" /> <AttributeName attributename="TSK_CALENDAR_ENTRY_TYPE" columnName="Activity Type" required="yes" />
<AttributeName attributename="TSK_DESCRIPTION" columnName="User Activity Required String" required="yes" /> <AttributeName attributename="TSK_DESCRIPTION" columnName="User Activity Required String" required="yes" />
<AttributeName attributename="null" columnName=" Title" required="no" /> <AttributeName attributename="null" columnName="Title" required="no" />
<AttributeName attributename="null" columnName="Calendar Date" required="no" /> <AttributeName attributename="null" columnName="Calendar Date" required="no" />
<AttributeName attributename="null" columnName="Calendar End Date" required="no" /> <AttributeName attributename="null" columnName="Calendar End Date" required="no" />
<AttributeName attributename="TSK_LOCATION" columnName="Source ID" required="yes" /> <AttributeName attributename="TSK_LOCATION" columnName="Source ID" required="yes" />
@ -209,7 +209,7 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Device Backlit"> <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Device Backlit">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" /> <AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Screen is Backlit" required="yes" /> <AttributeName attributename="null" columnName="Screen is Backlit" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" /> <AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" /> <AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -226,12 +226,12 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Battery Level"> <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Battery Level">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" /> <AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Battery Level" required="yes" /> <AttributeName attributename="null" columnName="Battery Level" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" /> <AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Day of the Week" required="no" /> <AttributeName attributename="null" columnName="Day of the Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" /> <AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" /> <AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName=" ZOBJECT Table ID" required="no" /> <AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -255,7 +255,7 @@
<ArtifactName artifactname="TSK_DEVICE_INFO" comment="KnowledgeC Car Play Connections"> <ArtifactName artifactname="TSK_DEVICE_INFO" comment="KnowledgeC Car Play Connections">
<AttributeName attributename="TSK_DATETIME" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME" columnName="Start" required="yes" />
<AttributeName attributename="null" columnName="End" required="no" /> <AttributeName attributename="null" columnName="End" required="no" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Car Play Connected" required="yes" /> <AttributeName attributename="null" columnName="Car Play Connected" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" /> <AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" /> <AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -271,7 +271,7 @@
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" /> <AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Bundle ID" required="yes" /> <AttributeName attributename="TSK_PROG_NAME" columnName="Bundle ID" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Value String" required="yes" /> <AttributeName attributename="null" columnName="Value String" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" /> <AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" /> <AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -286,7 +286,7 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Do Not Disturb"> <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Do Not Disturb">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" /> <AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Value" required="yes" /> <AttributeName attributename="null" columnName="Value" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" /> <AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" /> <AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -301,7 +301,7 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Inferred Motion"> <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Inferred Motion">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" /> <AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Value" required="yes" /> <AttributeName attributename="null" columnName="Value" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" /> <AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" /> <AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -344,12 +344,12 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Device Locked"> <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Device Locked">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" /> <AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Is Locked?" required="yes" /> <AttributeName attributename="null" columnName="Is Locked?" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" /> <AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Day of the Week" required="no" /> <AttributeName attributename="null" columnName="Day of the Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" /> <AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" /> <AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName=" ZOBJECT Table ID" required="no" /> <AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -362,7 +362,7 @@
<AttributeName attributename="null" columnName="Now Playing Artists" required="no" /> <AttributeName attributename="null" columnName="Now Playing Artists" required="no" />
<AttributeName attributename="null" columnName="Playing Genre" required="no" /> <AttributeName attributename="null" columnName="Playing Genre" required="no" />
<AttributeName attributename="TSK_NAME" columnName="Playing Title" required="yes" /> <AttributeName attributename="TSK_NAME" columnName="Playing Title" required="yes" />
<AttributeName attributename="null" columnName=" Now Playing Duration" required="no" /> <AttributeName attributename="null" columnName="Now Playing Duration" required="no" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" /> <AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" /> <AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -388,7 +388,7 @@
<AttributeName attributename="null" columnName="GMT Offset" required="no" /> <AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" /> <AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="Expiration Date" required="no" /> <AttributeName attributename="null" columnName="Expiration Date" required="no" />
<AttributeName attributename="null" columnName=" UUID" required="no" /> <AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" /> <AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -397,7 +397,7 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Screen Orientation"> <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Screen Orientation">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" /> <AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Orientation" required="yes" /> <AttributeName attributename="null" columnName="Orientation" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" /> <AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" /> <AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -412,14 +412,14 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Plugged In"> <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Plugged In">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" /> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" /> <AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Is Plugged In?" required="yes" /> <AttributeName attributename="null" columnName="Is Plugged In?" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" /> <AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Day of the Week" required="no" /> <AttributeName attributename="null" columnName="Day of the Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" /> <AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Start" required="no" /> <AttributeName attributename="null" columnName="Start" required="no" />
<AttributeName attributename="null" columnName="End" required="no" /> <AttributeName attributename="null" columnName="End" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" /> <AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName=" ZOBJECT Table ID" required="no" /> <AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -528,8 +528,8 @@
<AttributeName attributename="null" columnName="Location Date" required="no" /> <AttributeName attributename="null" columnName="Location Date" required="no" />
<AttributeName attributename="null" columnName="Coordinates" required="no" /> <AttributeName attributename="null" columnName="Coordinates" required="no" />
<AttributeName attributename="null" columnName="Vehicle Identifier" required="no" /> <AttributeName attributename="null" columnName="Vehicle Identifier" required="no" />
<AttributeName attributename="null" columnName=" Location Identifier" required="no" /> <AttributeName attributename="null" columnName="Location Identifier" required="no" />
<AttributeName attributename="null" columnName=" Identifier" required="no" /> <AttributeName attributename="null" columnName="Identifier" required="no" />
<AttributeName attributename="null" columnName="Location Quality" required="no" /> <AttributeName attributename="null" columnName="Location Quality" required="no" />
<AttributeName attributename="null" columnName="User Set Location" required="no" /> <AttributeName attributename="null" columnName="User Set Location" required="no" />
<AttributeName attributename="null" columnName="Usual Location" required="no" /> <AttributeName attributename="null" columnName="Usual Location" required="no" />
@ -584,11 +584,11 @@
<AttributeName attributename="TSK_EMAIL_FROM" columnName="Address" required="yes" /> <AttributeName attributename="TSK_EMAIL_FROM" columnName="Address" required="yes" />
<AttributeName attributename="null" columnName="Comment" required="no" /> <AttributeName attributename="null" columnName="Comment" required="no" />
<AttributeName attributename="TSK_SUBJECT" columnName="Subject" required="yes" /> <AttributeName attributename="TSK_SUBJECT" columnName="Subject" required="yes" />
<AttributeName attributename="TSK_EMAIL_CONTENT_PLAIN" columnName=" Summary" required="yes" /> <AttributeName attributename="TSK_EMAIL_CONTENT_PLAIN" columnName="Summary" required="yes" />
<AttributeName attributename="TSK_READ_STATUS" columnName=" Read?" required="yes" /> <AttributeName attributename="TSK_READ_STATUS" columnName="Read?" required="yes" />
<AttributeName attributename="TSK_FLAG" columnName=" Flagged?" required="yes" /> <AttributeName attributename="TSK_FLAG" columnName="Flagged?" required="yes" />
<AttributeName attributename="TSK_ISDELETED" columnName=" Deleted" required="yes" /> <AttributeName attributename="TSK_ISDELETED" columnName="Deleted" required="yes" />
<AttributeName attributename="null" columnName=" Mailbox" required="no" /> <AttributeName attributename="null" columnName="Mailbox" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
--> -->
@ -596,10 +596,10 @@
<FileName filename="Notifications.tsv" description="iOS Notificatons"> <FileName filename="Notifications.tsv" description="iOS Notificatons">
<ArtifactName artifactname="TSK_PROG_NOTIFICATIONS" comment="iOS Notificatons"> <ArtifactName artifactname="TSK_PROG_NOTIFICATIONS" comment="iOS Notificatons">
<AttributeName attributename="TSK_DATETIME" columnName="Creation Time" required="yes" /> <AttributeName attributename="TSK_DATETIME" columnName="Creation Time" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName=" Bundle" required="yes" /> <AttributeName attributename="TSK_PROG_NAME" columnName="Bundle" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName=" Title[Subtitle]" required="yes" /> <AttributeName attributename="TSK_TITLE" columnName="Title[Subtitle]" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName=" Message" required="yes" /> <AttributeName attributename="TSK_VALUE" columnName="Message" required="yes" />
<AttributeName attributename="null" columnName=" Other Details" required="no" /> <AttributeName attributename="null" columnName="Other Details" required="no" />
</ArtifactName> </ArtifactName>
</FileName> </FileName>
@ -651,7 +651,7 @@
<FileName filename="Powerlog Lightning Connector.tsv" description="Powerlog Lightning Connector Status"> <FileName filename="Powerlog Lightning Connector.tsv" description="Powerlog Lightning Connector Status">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Lightning Connector Status"> <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Lightning Connector Status">
<AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" /> <AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Accesory Power Mode" required="yes" /> <AttributeName attributename="null" columnName="Accesory Power Mode" required="yes" />
<AttributeName attributename="null" columnName="Original Lightnint Connector Timestamp" required="no" /> <AttributeName attributename="null" columnName="Original Lightnint Connector Timestamp" required="no" />
<AttributeName attributename="null" columnName="Offset Timestamp" required="no" /> <AttributeName attributename="null" columnName="Offset Timestamp" required="no" />
<AttributeName attributename="null" columnName="Table ID" required="no" /> <AttributeName attributename="null" columnName="Table ID" required="no" />
@ -680,7 +680,7 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Torch"> <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Torch">
<AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" /> <AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" />
<AttributeName attributename="null" columnName="Bundle ID" required="no" /> <AttributeName attributename="null" columnName="Bundle ID" required="no" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Status" required="yes" /> <AttributeName attributename="null" columnName="Status" required="yes" />
<AttributeName attributename="null" columnName="Original Torch Timestamp" required="no" /> <AttributeName attributename="null" columnName="Original Torch Timestamp" required="no" />
<AttributeName attributename="null" columnName="Offset Timestamp" required="no" /> <AttributeName attributename="null" columnName="Offset Timestamp" required="no" />
<AttributeName attributename="null" columnName="Time Offset" required="no" /> <AttributeName attributename="null" columnName="Time Offset" required="no" />
@ -705,7 +705,7 @@
<AttributeName attributename="TSK_PROG_NAME" columnName="App Name" required="yes" /> <AttributeName attributename="TSK_PROG_NAME" columnName="App Name" required="yes" />
<AttributeName attributename="null" columnName="App Executable Name" required="no" /> <AttributeName attributename="null" columnName="App Executable Name" required="no" />
<AttributeName attributename="TSK_PATH" columnName="Bundle ID" required="yes" /> <AttributeName attributename="TSK_PATH" columnName="Bundle ID" required="yes" />
<AttributeName attributename="TSK_BUILD_VERSION" columnName="App Build Version" required="yes" /> <AttributeName attributename="null" columnName="App Build Version" required="yes" />
<AttributeName attributename="TSK_VERSION" columnName="App Bundle Version" required="yes" /> <AttributeName attributename="TSK_VERSION" columnName="App Bundle Version" required="yes" />
<AttributeName attributename="null" columnName="App TYpe" required="no" /> <AttributeName attributename="null" columnName="App TYpe" required="no" />
<AttributeName attributename="null" columnName="App Deleted Date" required="no" /> <AttributeName attributename="null" columnName="App Deleted Date" required="no" />

View File

@ -109,7 +109,10 @@ final class YaraIngestHelper {
} }
/** /**
* * Scan the given AbstractFile for yara rule matches from the rule sets in
* the given directory creating a blackboard artifact for each matching
* rule.
*
* @param file The Abstract File being processed. * @param file The Abstract File being processed.
* @param baseRuleSetDirectory Base directory of the compiled rule sets. * @param baseRuleSetDirectory Base directory of the compiled rule sets.
* @param localFile Local copy of file. * @param localFile Local copy of file.
@ -138,8 +141,8 @@ final class YaraIngestHelper {
* Scan the given file byte array for rule matches using the YaraJNIWrapper * Scan the given file byte array for rule matches using the YaraJNIWrapper
* API. * API.
* *
* @param fileBytes * @param fileBytes An array of the file data.
* @param ruleSetDirectory * @param ruleSetDirectory Base directory of the compiled rule sets.
* *
* @return List of rules that match from the given file from the given rule * @return List of rules that match from the given file from the given rule
* set. Empty list is returned if no matches where found. * set. Empty list is returned if no matches where found.
@ -158,6 +161,17 @@ final class YaraIngestHelper {
return matchingRules; return matchingRules;
} }
/**
* Scan the given file for rules that match from the given rule set directory.
*
* @param scanFile Locally stored file to scan.
* @param ruleSetDirectory Base directory of the compiled rule sets.
* @param timeout YARA Scanner timeout value.
*
* @return List of matching rules, if none were found the list will be empty.
*
* @throws YaraWrapperException
*/
private static List<String> scanFileForMatch(File scanFile, File ruleSetDirectory, int timeout) throws YaraWrapperException { private static List<String> scanFileForMatch(File scanFile, File ruleSetDirectory, int timeout) throws YaraWrapperException {
List<String> matchingRules = new ArrayList<>(); List<String> matchingRules = new ArrayList<>();
@ -228,7 +242,7 @@ final class YaraIngestHelper {
ProcessBuilder builder = new ProcessBuilder(commandList); ProcessBuilder builder = new ProcessBuilder(commandList);
try { try {
int result = ExecUtil.execute(builder); int result = ExecUtil.execute(builder);
if(result != 0) { if (result != 0) {
throw new IngestModuleException(String.format("Failed to compile Yara rules file %s. Compile error %d", file.toString(), result)); throw new IngestModuleException(String.format("Failed to compile Yara rules file %s. Compile error %d", file.toString(), result));
} }
} catch (SecurityException | IOException ex) { } catch (SecurityException | IOException ex) {
@ -249,7 +263,7 @@ final class YaraIngestHelper {
private static List<RuleSet> getRuleSetsForNames(List<String> names) { private static List<RuleSet> getRuleSetsForNames(List<String> names) {
List<RuleSet> ruleSetList = new ArrayList<>(); List<RuleSet> ruleSetList = new ArrayList<>();
RuleSetManager manager = new RuleSetManager(); RuleSetManager manager = RuleSetManager.getInstance();
for (RuleSet set : manager.getRuleSetList()) { for (RuleSet set : manager.getRuleSetList()) {
if (names.contains(set.getName())) { if (names.contains(set.getName())) {
ruleSetList.add(set); ruleSetList.add(set);

View File

@ -25,8 +25,10 @@ import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.ingest.FileIngestModule; import org.sleuthkit.autopsy.ingest.FileIngestModule;
import org.sleuthkit.autopsy.ingest.IngestModuleFactory; import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
import org.sleuthkit.autopsy.modules.yara.ui.YaraGlobalSettingsPanel;
import org.sleuthkit.autopsy.modules.yara.ui.YaraIngestSettingsPanel; import org.sleuthkit.autopsy.modules.yara.ui.YaraIngestSettingsPanel;
/** /**
@ -63,7 +65,7 @@ public class YaraIngestModuleFactory extends IngestModuleFactoryAdapter {
@Override @Override
public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) { public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) {
return new YaraIngestSettingsPanel((YaraIngestJobSettings)settings); return new YaraIngestSettingsPanel((YaraIngestJobSettings) settings);
} }
@Override @Override
@ -89,4 +91,16 @@ public class YaraIngestModuleFactory extends IngestModuleFactoryAdapter {
static String getModuleName() { static String getModuleName() {
return Bundle.Yara_Module_Name(); return Bundle.Yara_Module_Name();
} }
@Override
public boolean hasGlobalSettingsPanel() {
return true;
}
@Override
public IngestModuleGlobalSettingsPanel getGlobalSettingsPanel() {
YaraGlobalSettingsPanel globalOptionsPanel = new YaraGlobalSettingsPanel();
globalOptionsPanel.load();
return globalOptionsPanel;
}
} }

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.modules.yara.rules;
import java.io.File; import java.io.File;
import java.io.Serializable; import java.io.Serializable;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -69,7 +70,15 @@ public class RuleSet implements Comparable<RuleSet>, Serializable {
* @return List of Files in current directory. * @return List of Files in current directory.
*/ */
public List<File> getRuleFiles() { public List<File> getRuleFiles() {
return Arrays.asList(path.toFile().listFiles()); List<File> fileList = new ArrayList<>();
if(path.toFile().exists()) {
File[] fileArray = path.toFile().listFiles();
if(fileArray != null) {
fileList.addAll(Arrays.asList(fileArray));
}
}
return fileList;
} }
@Override @Override

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2020 Basis Technology Corp. * Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,11 +18,14 @@
*/ */
package org.sleuthkit.autopsy.modules.yara.rules; package org.sleuthkit.autopsy.modules.yara.rules;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.SwingUtilities;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/** /**
@ -34,6 +37,54 @@ public class RuleSetManager {
private final static String BASE_FOLDER = "yara"; private final static String BASE_FOLDER = "yara";
private final static String RULE_SET_FOLDER = "ruleSets"; private final static String RULE_SET_FOLDER = "ruleSets";
/**
* Rule Set Property names.
*/
public final static String RULE_SET_ADDED = "YARARuleSetAdded";
public final static String RULE_SET_DELETED = "YARARuleSetDeleted";
private final PropertyChangeSupport changeSupport;
private static RuleSetManager instance;
/**
* Private constructor for this singleton.
*/
private RuleSetManager() {
changeSupport = new PropertyChangeSupport(this);
}
/**
* Returns the instance of this manager class.
*
* @return
*/
public synchronized static RuleSetManager getInstance() {
if (instance == null) {
instance = new RuleSetManager();
}
return instance;
}
/**
* Adds a property change listener to the manager.
*
* @param listener Listener to be added.
*/
public static void addPropertyChangeListener(PropertyChangeListener listener) {
getInstance().getChangeSupport().addPropertyChangeListener(listener);
}
/**
* Remove a property change listener from this manager.
*
* @param listener Listener to be added.
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
getInstance().getChangeSupport().removePropertyChangeListener(listener);
}
/** /**
* Create a new Yara rule set with the given set name. * Create a new Yara rule set with the given set name.
* *
@ -43,12 +94,11 @@ public class RuleSetManager {
* *
* @throws RuleSetException RuleSet with given name already exists. * @throws RuleSetException RuleSet with given name already exists.
*/ */
public RuleSet createRuleSet(String name) throws RuleSetException { public synchronized RuleSet createRuleSet(String name) throws RuleSetException {
if (name == null || name.isEmpty()) {
if(name == null || name.isEmpty()) { throw new RuleSetException("YARA rule set name cannot be null or empty string");
throw new RuleSetException("YARA rule set name cannot be null or empty string" );
} }
if (isRuleSetExists(name)) { if (isRuleSetExists(name)) {
throw new RuleSetException(String.format("Yara rule set with name %s already exits.", name)); throw new RuleSetException(String.format("Yara rule set with name %s already exits.", name));
} }
@ -58,7 +108,42 @@ public class RuleSetManager {
setPath.toFile().mkdir(); setPath.toFile().mkdir();
return new RuleSet(name, setPath); RuleSet newSet = new RuleSet(name, setPath);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
getChangeSupport().firePropertyChange(RULE_SET_ADDED, null, newSet);
}
});
return newSet;
}
/**
* Deletes an existing RuleSet.
*
* @param ruleSet RuleSet to be deleted.
*
* @throws RuleSetException
*/
public synchronized void deleteRuleSet(RuleSet ruleSet) throws RuleSetException {
if (ruleSet == null) {
throw new RuleSetException("YARA rule set name cannot be null or empty string");
}
if (!isRuleSetExists(ruleSet.getName())) {
throw new RuleSetException(String.format("A YARA rule set with name %s does not exits.", ruleSet.getName()));
}
deleteDirectory(ruleSet.getPath().toFile());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
getChangeSupport().firePropertyChange(RULE_SET_DELETED, ruleSet, null);
}
});
} }
/** /**
@ -66,7 +151,7 @@ public class RuleSetManager {
* *
* @return * @return
*/ */
public List<RuleSet> getRuleSetList() { public synchronized List<RuleSet> getRuleSetList() {
List<RuleSet> ruleSets = new ArrayList<>(); List<RuleSet> ruleSets = new ArrayList<>();
Path basePath = getRuleSetPath(); Path basePath = getRuleSetPath();
@ -86,7 +171,7 @@ public class RuleSetManager {
* *
* @return True if the rule set exist. * @return True if the rule set exist.
*/ */
public boolean isRuleSetExists(String name) { public synchronized boolean isRuleSetExists(String name) {
Path basePath = getRuleSetPath(); Path basePath = getRuleSetPath();
Path setPath = Paths.get(basePath.toString(), name); Path setPath = Paths.get(basePath.toString(), name);
@ -110,4 +195,30 @@ public class RuleSetManager {
return basePath; return basePath;
} }
/**
* Returns the PropertyChangeSupport instance.
*
* @return PropertyChangeSupport instance.
*/
private PropertyChangeSupport getChangeSupport() {
return changeSupport;
}
/**
* Recursively delete the given directory and its children.
*
* @param directoryToBeDeleted
*
* @return True if the delete was successful.
*/
private boolean deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
for (File file : allContents) {
deleteDirectory(file);
}
}
return directoryToBeDeleted.delete();
}
} }

View File

@ -10,6 +10,7 @@ RuleSetDetailsPanel.setDetailsLabel.text=Set Details
RuleSetDetailsPanel.openFolderButton.text=Open Folder RuleSetDetailsPanel.openFolderButton.text=Open Folder
RuleSetPanel.descriptionField.text=This module allows you to find files the match Yara rules. Each set has a list of Yara rule files. A file need only match one rule in the set to be found. RuleSetPanel.descriptionField.text=This module allows you to find files the match Yara rules. Each set has a list of Yara rule files. A file need only match one rule in the set to be found.
RuleSetDetailsPanel.openLabel.text=Place rule files in the set's folder. They will be compiled before use. RuleSetDetailsPanel.openLabel.text=Place rule files in the set's folder. They will be compiled before use.
YARA_Global_Settings_Panel_Title=YARA Options
YaraIngestSettingsPanel.border.title=Select YARA rule sets to enable during ingest: YaraIngestSettingsPanel.border.title=Select YARA rule sets to enable during ingest:
YaraIngestSettingsPanel.allFilesButton.text=All Files YaraIngestSettingsPanel.allFilesButton.text=All Files
YaraIngestSettingsPanel.allFilesButton.toolTipText= YaraIngestSettingsPanel.allFilesButton.toolTipText=
@ -21,3 +22,8 @@ YaraRuleSetOptionPanel_badName_msg=Rule set name {0} already exists.\nRule set n
YaraRuleSetOptionPanel_badName_title=Create Rule Set YaraRuleSetOptionPanel_badName_title=Create Rule Set
YaraRuleSetOptionPanel_new_rule_set_name_msg=Supply a new unique rule set name: YaraRuleSetOptionPanel_new_rule_set_name_msg=Supply a new unique rule set name:
YaraRuleSetOptionPanel_new_rule_set_name_title=Rule Set Name YaraRuleSetOptionPanel_new_rule_set_name_title=Rule Set Name
# {0} - rule set name
YaraRuleSetOptionPanel_rule_set_delete=Unable to delete the selected YARA rule set {0}.\nRule set may have already been removed.
# {0} - rule set name
YaraRuleSetOptionPanel_RuleSet_Missing=The folder for the selected YARA rule set, {0}, no longer exists.
YaraRuleSetOptionPanel_RuleSet_Missing_title=Folder removed

View File

@ -99,10 +99,6 @@
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="refreshButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="refreshButtonActionPerformed"/>
</Events> </Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="2" gridY="5" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="14" weightX="0.0" weightY="0.0"/> <GridBagConstraints gridX="2" gridY="5" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="14" weightX="0.0" weightY="0.0"/>

View File

@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.modules.yara.ui;
import java.awt.Component; import java.awt.Component;
import java.awt.Desktop; import java.awt.Desktop;
import java.awt.Graphics;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@ -58,7 +57,7 @@ public class RuleSetDetailsPanel extends javax.swing.JPanel {
fileList.setCellRenderer(new FileRenderer()); fileList.setCellRenderer(new FileRenderer());
openFolderButton.setEnabled(false); openFolderButton.setEnabled(false);
scrollPane.setViewportView(fileList); scrollPane.setViewportView(fileList);
refreshButton.setEnabled(false);
} }
/** /**
@ -82,6 +81,7 @@ public class RuleSetDetailsPanel extends javax.swing.JPanel {
} }
openFolderButton.setEnabled(ruleSet != null); openFolderButton.setEnabled(ruleSet != null);
refreshButton.setEnabled(ruleSet != null);
} }
/** /**
@ -120,7 +120,7 @@ public class RuleSetDetailsPanel extends javax.swing.JPanel {
openFolderButton = new javax.swing.JButton(); openFolderButton = new javax.swing.JButton();
openLabel = new javax.swing.JLabel(); openLabel = new javax.swing.JLabel();
scrollPane = new javax.swing.JScrollPane(); scrollPane = new javax.swing.JScrollPane();
javax.swing.JButton refreshButton = new javax.swing.JButton(); refreshButton = new javax.swing.JButton();
setLayout(new java.awt.GridBagLayout()); setLayout(new java.awt.GridBagLayout());
@ -223,6 +223,7 @@ public class RuleSetDetailsPanel extends javax.swing.JPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton openFolderButton; private javax.swing.JButton openFolderButton;
private javax.swing.JLabel openLabel; private javax.swing.JLabel openLabel;
private javax.swing.JButton refreshButton;
private javax.swing.JScrollPane scrollPane; private javax.swing.JScrollPane scrollPane;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<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,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
</Form>

View File

@ -0,0 +1,85 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.modules.yara.ui;
import java.awt.BorderLayout;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
/**
* YARA global settings panel.
*/
public class YaraGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel {
private static final long serialVersionUID = 1L;
private final YaraRuleSetOptionPanel panel = new YaraRuleSetOptionPanel();
@Messages({
"YARA_Global_Settings_Panel_Title=YARA Options"
})
/**
* Creates new form YaraGlobalSettingsPanel
*/
public YaraGlobalSettingsPanel() {
initComponents();
addOptionPanel();
}
@Override
public void saveSettings() {
}
@Override
public void store() {
}
@Override
public void load() {
if (panel != null) {
panel.updatePanel();
}
}
/**
* Add the YaraRuleSetOptionPanel to this panel.
*/
private void addOptionPanel() {
add(panel, BorderLayout.CENTER);
setName(Bundle.YARA_Global_Settings_Panel_Title());
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
setLayout(new java.awt.BorderLayout());
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -18,6 +18,8 @@
*/ */
package org.sleuthkit.autopsy.modules.yara.ui; package org.sleuthkit.autopsy.modules.yara.ui;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
@ -49,24 +51,42 @@ public class YaraIngestSettingsPanel extends IngestModuleIngestJobSettingsPanel
checkboxList = new CheckBoxJList<>(); checkboxList = new CheckBoxJList<>();
scrollPane.setViewportView(checkboxList); scrollPane.setViewportView(checkboxList);
} }
/**
* Constructs a new panel with the given JobSetting objects.
*
* @param settings Ingest job settings.
*/
public YaraIngestSettingsPanel(YaraIngestJobSettings settings) { public YaraIngestSettingsPanel(YaraIngestJobSettings settings) {
this(); this();
List<String> setNames = settings.getSelectedRuleSetNames(); List<String> setNames = settings.getSelectedRuleSetNames();
checkboxList.setModel(listModel); checkboxList.setModel(listModel);
checkboxList.setOpaque(false); checkboxList.setOpaque(false);
RuleSetManager manager = new RuleSetManager(); List<RuleSet> ruleSetList = RuleSetManager.getInstance().getRuleSetList();
List<RuleSet> ruleSetList = manager.getRuleSetList();
for (RuleSet set : ruleSetList) { for (RuleSet set : ruleSetList) {
RuleSetListItem item = new RuleSetListItem(set); RuleSetListItem item = new RuleSetListItem(set);
item.setChecked(setNames.contains(set.getName())); item.setChecked(setNames.contains(set.getName()));
listModel.addElement(item); listModel.addElement(item);
} }
allFilesButton.setSelected(!settings.onlyExecutableFiles()); allFilesButton.setSelected(!settings.onlyExecutableFiles());
executableFilesButton.setSelected(settings.onlyExecutableFiles()); executableFilesButton.setSelected(settings.onlyExecutableFiles());
RuleSetManager.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
switch (evt.getPropertyName()) {
case RuleSetManager.RULE_SET_ADDED:
handleRuleSetAdded((RuleSet) evt.getNewValue());
break;
case RuleSetManager.RULE_SET_DELETED:
handleRuleSetDeleted((RuleSet) evt.getOldValue());
break;
}
}
});
} }
@Override @Override
@ -84,6 +104,36 @@ public class YaraIngestSettingsPanel extends IngestModuleIngestJobSettingsPanel
return new YaraIngestJobSettings(selectedRules, executableFilesButton.isSelected()); return new YaraIngestJobSettings(selectedRules, executableFilesButton.isSelected());
} }
/**
* Handle the addition of a new Rule Set.
*
* @param ruleSet
*/
private void handleRuleSetAdded(RuleSet ruleSet) {
if (ruleSet == null) {
return;
}
RuleSetListItem item = new RuleSetListItem(ruleSet);
listModel.addElement(item);
}
/**
* Handle the removal of the rule set.
*
* @param ruleSet
*/
private void handleRuleSetDeleted(RuleSet ruleSet) {
Enumeration<RuleSetListItem> enumeration = listModel.elements();
while (enumeration.hasMoreElements()) {
RuleSetListItem item = enumeration.nextElement();
if (item.getDisplayName().equals(ruleSet.getName())) {
listModel.removeElement(item);
return;
}
}
}
/** /**
* RuleSet wrapper class for Checkbox JList model. * RuleSet wrapper class for Checkbox JList model.
*/ */

View File

@ -42,6 +42,14 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Container class="javax.swing.JPanel" name="viewportPanel"> <Container class="javax.swing.JPanel" name="viewportPanel">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[1000, 127]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[1020, 400]"/>
</Property>
</Properties>
<AuxValues> <AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/> <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/> <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>

View File

@ -20,13 +20,12 @@ package org.sleuthkit.autopsy.modules.yara.ui;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.io.File;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.modules.yara.rules.RuleSet; import org.sleuthkit.autopsy.modules.yara.rules.RuleSet;
import org.sleuthkit.autopsy.modules.yara.rules.RuleSetException; import org.sleuthkit.autopsy.modules.yara.rules.RuleSetException;
import org.sleuthkit.autopsy.modules.yara.rules.RuleSetManager; import org.sleuthkit.autopsy.modules.yara.rules.RuleSetManager;
@ -41,16 +40,12 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(YaraRuleSetOptionPanel.class.getName()); private static final Logger logger = Logger.getLogger(YaraRuleSetOptionPanel.class.getName());
private final RuleSetManager manager;
/** /**
* Creates new form YaraRuleSetOptionPanel * Creates new form YaraRuleSetOptionPanel
*/ */
public YaraRuleSetOptionPanel() { public YaraRuleSetOptionPanel() {
initComponents(); initComponents();
manager = new RuleSetManager();
ruleSetPanel.addListSelectionListener(new ListSelectionListener() { ruleSetPanel.addListSelectionListener(new ListSelectionListener() {
@Override @Override
public void valueChanged(ListSelectionEvent e) { public void valueChanged(ListSelectionEvent e) {
@ -77,25 +72,41 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
* Update the panel with the current rule set. * Update the panel with the current rule set.
*/ */
void updatePanel() { void updatePanel() {
ruleSetPanel.addSetList(manager.getRuleSetList()); ruleSetPanel.addSetList(RuleSetManager.getInstance().getRuleSetList());
} }
@Messages({
"# {0} - rule set name",
"YaraRuleSetOptionPanel_RuleSet_Missing=The folder for the selected YARA rule set, {0}, no longer exists.",
"YaraRuleSetOptionPanel_RuleSet_Missing_title=Folder removed",
})
/** /**
* Handle the change in rule set selection. Update the detail panel with the * Handle the change in rule set selection. Update the detail panel with the
* selected rule. * selected rule.
*/ */
private void handleSelectionChange() { private void handleSelectionChange() {
ruleSetDetailsPanel.setRuleSet(ruleSetPanel.getSelectedRule()); RuleSet ruleSet = ruleSetPanel.getSelectedRule();
if(ruleSet != null && !ruleSet.getPath().toFile().exists()) {
ruleSetDetailsPanel.setRuleSet(null);
ruleSetPanel.removeRuleSet(ruleSet);
JOptionPane.showMessageDialog(this,
Bundle.YaraRuleSetOptionPanel_RuleSet_Missing(ruleSet.getName()),
Bundle.YaraRuleSetOptionPanel_RuleSet_Missing_title(),
JOptionPane.ERROR_MESSAGE);
} else {
ruleSetDetailsPanel.setRuleSet(ruleSet);
}
} }
@NbBundle.Messages({ @Messages({
"YaraRuleSetOptionPanel_new_rule_set_name_msg=Supply a new unique rule set name:", "YaraRuleSetOptionPanel_new_rule_set_name_msg=Supply a new unique rule set name:",
"YaraRuleSetOptionPanel_new_rule_set_name_title=Rule Set Name", "YaraRuleSetOptionPanel_new_rule_set_name_title=Rule Set Name",
"# {0} - rule set name", "# {0} - rule set name",
"YaraRuleSetOptionPanel_badName_msg=Rule set name {0} already exists.\nRule set names must be unique.", "YaraRuleSetOptionPanel_badName_msg=Rule set name {0} already exists.\nRule set names must be unique.",
"YaraRuleSetOptionPanel_badName_title=Create Rule Set", "YaraRuleSetOptionPanel_badName_title=Create Rule Set",
"YaraRuleSetOptionPanel_badName2_msg=Rule set is invalid.\nRule set names must be non-empty string and unique.", "YaraRuleSetOptionPanel_badName2_msg=Rule set is invalid.\nRule set names must be non-empty string and unique.",})
})
/** /**
* Handle the new rule set action. Prompt the user for a rule set name, * Handle the new rule set action. Prompt the user for a rule set name,
* create the new set and update the rule set list. * create the new set and update the rule set list.
@ -105,16 +116,21 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
Bundle.YaraRuleSetOptionPanel_new_rule_set_name_msg(), Bundle.YaraRuleSetOptionPanel_new_rule_set_name_msg(),
Bundle.YaraRuleSetOptionPanel_new_rule_set_name_title()); Bundle.YaraRuleSetOptionPanel_new_rule_set_name_title());
if(value == null || value.isEmpty()) { // User hit cancel.
if(value == null) {
return;
}
if (value.isEmpty()) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.YaraRuleSetOptionPanel_badName2_msg(), Bundle.YaraRuleSetOptionPanel_badName2_msg(),
Bundle.YaraRuleSetOptionPanel_badName_title(), Bundle.YaraRuleSetOptionPanel_badName_title(),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
try { try {
ruleSetPanel.addRuleSet(manager.createRuleSet(value)); ruleSetPanel.addRuleSet(RuleSetManager.getInstance().createRuleSet(value));
} catch (RuleSetException ex) { } catch (RuleSetException ex) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.YaraRuleSetOptionPanel_badName_msg(value), Bundle.YaraRuleSetOptionPanel_badName_msg(value),
@ -124,31 +140,29 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
} }
} }
@Messages({
"# {0} - rule set name",
"YaraRuleSetOptionPanel_rule_set_delete=Unable to delete the selected YARA rule set {0}.\nRule set may have already been removed."
})
/** /**
* Handle the delete rule action. Delete the rule set and update the the * Handle the delete rule action. Delete the rule set and update the the
* rule set list. * rule set list.
*/ */
private void handleDeleteRuleSet() { private void handleDeleteRuleSet() {
RuleSet ruleSet = ruleSetPanel.getSelectedRule(); RuleSet ruleSet = ruleSetPanel.getSelectedRule();
ruleSetPanel.removeRuleSet(ruleSet); if (ruleSet != null) {
deleteDirectory(ruleSet.getPath().toFile()); try {
} RuleSetManager.getInstance().deleteRuleSet(ruleSet);
} catch (RuleSetException ex) {
/** JOptionPane.showMessageDialog(this,
* Recursively delete the given directory and its children. Bundle.YaraRuleSetOptionPanel_rule_set_delete(ruleSet.getName()),
* Bundle.YaraRuleSetOptionPanel_badName_title(),
* @param directoryToBeDeleted JOptionPane.ERROR_MESSAGE);
* logger.log(Level.WARNING, String.format("Failed to delete YARA rule set %s", ruleSet.getName()), ex);
* @return True if the delete was successful.
*/
private boolean deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
for (File file : allContents) {
deleteDirectory(file);
} }
ruleSetPanel.removeRuleSet(ruleSet);
} }
return directoryToBeDeleted.delete();
} }
/** /**
@ -172,6 +186,8 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
scrollPane.setBorder(null); scrollPane.setBorder(null);
viewportPanel.setMinimumSize(new java.awt.Dimension(1000, 127));
viewportPanel.setPreferredSize(new java.awt.Dimension(1020, 400));
viewportPanel.setLayout(new java.awt.GridBagLayout()); viewportPanel.setLayout(new java.awt.GridBagLayout());
separator.setOrientation(javax.swing.SwingConstants.VERTICAL); separator.setOrientation(javax.swing.SwingConstants.VERTICAL);

View File

@ -1348,7 +1348,7 @@ class TableReportGenerator {
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH))); new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH)));
columns.add(new AttributeColumn(NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.dateTime"), columns.add(new AttributeColumn(NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.dateTime"),
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME))); new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED )));
attributeTypeSet.remove(new Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID)); attributeTypeSet.remove(new Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_INSTALLED_PROG.getTypeID() == artifactTypeId) { } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_INSTALLED_PROG.getTypeID() == artifactTypeId) {

View File

@ -390,6 +390,9 @@ public class HTMLReport implements TableReportModule {
case TSK_WEB_CATEGORIZATION: case TSK_WEB_CATEGORIZATION:
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/domain-16.png"); //NON-NLS in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/domain-16.png"); //NON-NLS
break; break;
case TSK_YARA_HIT:
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/yara_16.png"); //NON-NLS
break;
default: default:
logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS

View File

@ -56,11 +56,6 @@ public class TaskRetryUtil {
* Constructs an object that encapsulates the specification of a task * Constructs an object that encapsulates the specification of a task
* attempt for the attemptTask() utility. The attempt will have neither * attempt for the attemptTask() utility. The attempt will have neither
* a delay nor a time out. * a delay nor a time out.
*
* @param delay The delay before the task should be attempted,
* may be zero or any positive integer.
* @param delayTimeUnit The time unit for the delay before the task
* should be attempted.
*/ */
public TaskAttempt() { public TaskAttempt() {
this.delay = 0L; this.delay = 0L;
@ -168,7 +163,7 @@ public class TaskRetryUtil {
* each attempt and an optional timeout for each attempt. If an attempt * each attempt and an optional timeout for each attempt. If an attempt
* times out, that particular attempt task will be cancelled. * times out, that particular attempt task will be cancelled.
* *
* @param <T> The return type of the task. * @tparam T The return type of the task.
* @param task The task. * @param task The task.
* @param attempts The defining details for each attempt of the task. * @param attempts The defining details for each attempt of the task.
* @param executor The scheduled task executor to be used to attempt the * @param executor The scheduled task executor to be used to attempt the

View File

@ -26,7 +26,7 @@ import com.google.common.annotations.Beta;
* and should have a class annotation of '(at)ServiceProvider(service = * and should have a class annotation of '(at)ServiceProvider(service =
* DomainCategoryProvider.class)'. * DomainCategoryProvider.class)'.
* *
* NOTE: The @SuppressWarnings("try") on the class is to suppress warnings * NOTE: The (at)SuppressWarnings("try") on the class is to suppress warnings
* relating to the fact that the close method can throw an InterruptedException * relating to the fact that the close method can throw an InterruptedException
* since Exception can encompass the InterruptedException. See the following * since Exception can encompass the InterruptedException. See the following
* github issue and bugs for more information: * github issue and bugs for more information:

View File

@ -256,7 +256,7 @@ public class RecentFilesSummaryTest {
*/ */
private BlackboardArtifact getRecentDocumentArtifact(DataSource ds, long artifactId, Long dateTime, String path) { private BlackboardArtifact getRecentDocumentArtifact(DataSource ds, long artifactId, Long dateTime, String path) {
return getArtifact(ds, artifactId, ARTIFACT_TYPE.TSK_RECENT_OBJECT, Arrays.asList( return getArtifact(ds, artifactId, ARTIFACT_TYPE.TSK_RECENT_OBJECT, Arrays.asList(
Pair.of(ATTRIBUTE_TYPE.TSK_DATETIME, dateTime), Pair.of(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED , dateTime),
Pair.of(ATTRIBUTE_TYPE.TSK_PATH, path) Pair.of(ATTRIBUTE_TYPE.TSK_PATH, path)
)); ));
} }

View File

@ -284,7 +284,11 @@ class Ingester {
//Make a SolrInputDocument out of the field map //Make a SolrInputDocument out of the field map
SolrInputDocument updateDoc = new SolrInputDocument(); SolrInputDocument updateDoc = new SolrInputDocument();
for (String key : fields.keySet()) { for (String key : fields.keySet()) {
updateDoc.addField(key, Chunker.sanitize((String)fields.get(key)).toString()); if (fields.get(key).getClass() == String.class) {
updateDoc.addField(key, Chunker.sanitize((String)fields.get(key)).toString());
} else {
updateDoc.addField(key, fields.get(key));
}
} }
try { try {

View File

@ -62,8 +62,12 @@ class LanguageSpecificContentIndexingHelper {
//Make a SolrInputDocument out of the field map //Make a SolrInputDocument out of the field map
SolrInputDocument updateDoc = new SolrInputDocument(); SolrInputDocument updateDoc = new SolrInputDocument();
for (String key : fields.keySet()) { for (String key : fields.keySet()) {
updateDoc.addField(key, Chunker.sanitize((String)fields.get(key)).toString()); if (fields.get(key).getClass() == String.class) {
} updateDoc.addField(key, Chunker.sanitize((String)fields.get(key)).toString());
} else {
updateDoc.addField(key, fields.get(key));
}
}
try { try {
updateDoc.setField(Server.Schema.ID.toString(), Chunker.sanitize(MiniChunkHelper.getChunkIdString(baseChunkID)).toString()); updateDoc.setField(Server.Schema.ID.toString(), Chunker.sanitize(MiniChunkHelper.getChunkIdString(baseChunkID)).toString());

View File

@ -1013,16 +1013,34 @@ public class Server {
"# {0} - colelction name", "Server.deleteCore.exception.msg=Failed to delete Solr colelction {0}",}) "# {0} - colelction name", "Server.deleteCore.exception.msg=Failed to delete Solr colelction {0}",})
void deleteCollection(String coreName, CaseMetadata metadata) throws KeywordSearchServiceException, KeywordSearchModuleException { void deleteCollection(String coreName, CaseMetadata metadata) throws KeywordSearchServiceException, KeywordSearchModuleException {
try { try {
IndexingServerProperties properties = getMultiUserServerProperties(metadata.getCaseDirectory()); HttpSolrClient solrServer;
HttpSolrClient solrServer = getSolrClient("http://" + properties.getHost() + ":" + properties.getPort() + "/solr"); if (metadata.getCaseType() == CaseType.SINGLE_USER_CASE) {
connectToSolrServer(solrServer); solrServer = getSolrClient("http://localhost:" + localSolrServerPort + "/solr"); //NON-NLS
CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, solrServer);
CollectionAdminRequest.Delete deleteCollectionRequest = CollectionAdminRequest.deleteCollection(coreName); if (null != response.getCoreStatus(coreName).get("instanceDir")) { //NON-NLS
CollectionAdminResponse response = deleteCollectionRequest.process(solrServer); /*
if (response.isSuccess()) { * Send a core unload request to the Solr server, with the
logger.log(Level.INFO, "Deleted collection {0}", coreName); //NON-NLS * parameter set that request deleting the index and the
* instance directory (deleteInstanceDir = true). Note that
* this removes everything related to the core on the server
* (the index directory, the configuration files, etc.), but
* does not delete the actual Solr text index because it is
* currently stored in the case directory.
*/
org.apache.solr.client.solrj.request.CoreAdminRequest.unloadCore(coreName, true, true, solrServer);
}
} else { } else {
logger.log(Level.WARNING, "Unable to delete collection {0}", coreName); //NON-NLS IndexingServerProperties properties = getMultiUserServerProperties(metadata.getCaseDirectory());
solrServer = getSolrClient("http://" + properties.getHost() + ":" + properties.getPort() + "/solr");
connectToSolrServer(solrServer);
CollectionAdminRequest.Delete deleteCollectionRequest = CollectionAdminRequest.deleteCollection(coreName);
CollectionAdminResponse response = deleteCollectionRequest.process(solrServer);
if (response.isSuccess()) {
logger.log(Level.INFO, "Deleted collection {0}", coreName); //NON-NLS
} else {
logger.log(Level.WARNING, "Unable to delete collection {0}", coreName); //NON-NLS
}
} }
} catch (SolrServerException | IOException ex) { } catch (SolrServerException | IOException ex) {
// We will get a RemoteSolrException with cause == null and detailsMessage // We will get a RemoteSolrException with cause == null and detailsMessage

View File

@ -238,8 +238,11 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService {
} catch (KeywordSearchModuleException ex) { } catch (KeywordSearchModuleException ex) {
throw new KeywordSearchServiceException(Bundle.SolrSearchService_exceptionMessage_unableToDeleteCollection(index.getIndexName()), ex); throw new KeywordSearchServiceException(Bundle.SolrSearchService_exceptionMessage_unableToDeleteCollection(index.getIndexName()), ex);
} }
if (!FileUtil.deleteDir(new File(index.getIndexPath()).getParentFile())) { File indexDir = new File(index.getIndexPath()).getParentFile();
throw new KeywordSearchServiceException(Bundle.SolrSearchService_exceptionMessage_failedToDeleteIndexFiles(index.getIndexPath())); if (indexDir.exists()) {
if (!FileUtil.deleteDir(indexDir)) {
throw new KeywordSearchServiceException(Bundle.SolrSearchService_exceptionMessage_failedToDeleteIndexFiles(index.getIndexPath()));
}
} }
} }
} }

View File

@ -1,36 +1,76 @@
---------------- VERSION 4.17.0 -------------- ---------------- VERSION 4.18.0 --------------
GUI: Keyword Search:
Expanded the Data Source Summary panel to show recent activity, past cases, analysis results, etc. Also made this available from the main UI when a data source is selected. - A major upgrade from Solr 4 to Solr 8.6.3. Single user cases continue to use the embedded server.
Expanded Discovery UI to support searching for and basic display of web domains. It collapses the various web artifacts into a single view. Multi-user clusters need to install a new Solr 8 server and can now create a Solr cloud with multiple servers.
-- NOTE: Cases created with Autopsy 4.18 cannot be opened by previous versions of Autopsy. Autopsy 4.18 can open older cases though.
- Improved text indexing speed by not doing language detection on unknown file formats and unallocated space.
Domain Discovery:
- Added details view to Domain Discovery to show what web-based artifacts are associated with the selected domain.
- Updated the Domain Discovery grouping and sorting by options.
- Added basic domain categorization for webmail-based domains.
Content Viewers:
- Built more specialized viewers for web-based artifacts.
Data Source Summary:
- Added a “Geolocations” tab that shows what cities the data source was near (based on geolocation data).
- Added a “Timeline” tab that shows counts of events from the last 30 days the data source was used.
- Added navigation buttons to jump from the summary view to the main Autopsy UI (for example to go to the map).
Ingest Modules: Ingest Modules:
Added iOS Analyzer module based on iLEAPP and a subset of its artifacts. - New YARA ingest module to flag files based on regular expression patterns.
New Picture Analyzer module that does EXIF extraction and HEIC conversion. HEIC/HEIF images are converted to JPEGs that retain EXIF using ImageMagick (replaces the previous EXIF ingest module). - New “Android Analyzer (aLEAPP)” module based on aLEAPP. Previous “Android Analyzer” also still exists.
Added support for the latest version of Edge browser that is based on Chromium into Recent Activity. Other Chromium-based browsers are also supported. - Updated “iOS Analyzer (iLEAPP)” module to create more artifacts and work on disk images.
Updated the rules that search Web History artifacts for search queries. Expanded module to support multiple search engines for ambiguous URLs. - Hash Database module will calculate SHA-256 hash in addition to MD5.
Bluetooth pairing artifacts are created based on RegRipper output. - Removed Interesting Item rule that flagged existence of Bitlocker (since it ships with Windows).
Prefetch artifacts record the full path of exes. - Fixed a major bug in the PhotoRec module that could result in an incorrect file layout if the carved file spanned non-contiguous sectors.
PhotoRec module allows you to include or exclude specific file types. - Fixed MBOX detection bug in Email module.
Upgraded to Tika 1.23.
Performance: Reporting:
Documents are added to Solr in batches instead of one by one. - Attachments from tagged messages are now included in a Portable Case.
More efficient queries to find WAL files for SQLite databases.
Use a local drive for temp files for multi-user cases instead of the shared folder.
Command Line
Command line support for report profiles.
Restored support for Windows file type association for opening a case in Autopsy by double clicking case metadata (.aut) file.
Better feedback for command line argument errors.
Misc: Misc:
Updated versions of libvmdk, libvhdi, and libewf. - Added support for Ext4 inline data and sparse blocks (via TSK fix).
Persona UI fixes: Pre-populate account and changed order of New Persona dialog. - Updated PostgreSQL JDBC driver to support any recent version of PostgreSQL for multi-user cases and PostgreSQL Central Repository.
Streaming ingest support added to auto ingest. - Added personas to the summary viewer in CVT.
Recent Activity module processes now use the global timeout. - Handling of bad characters in auto ingest manifest files.
Option to include Autopsy executable in portable case (Windows only.) - Assorted small bug fixes.
Upgraded to NetBeans 11 Rich Client Platform.
Added debug feature to save the stack trace on all threads.
---------------- VERSION 4.17.0 --------------
GUI:
- Expanded the Data Source Summary panel to show recent activity, past cases, analysis results, etc. Also made this available from the main UI when a data source is selected.
- Expanded Discovery UI to support searching for and basic display of web domains. It collapses the various web artifacts into a single view.
Ingest Modules:
- Added iOS Analyzer module based on iLEAPP and a subset of its artifacts.
- New Picture Analyzer module that does EXIF extraction and HEIC conversion. HEIC/HEIF images are converted to JPEGs that retain EXIF using ImageMagick (replaces the previous EXIF ingest module).
- Added support for the latest version of Edge browser that is based on Chromium into Recent Activity. Other Chromium-based browsers are also supported.
- Updated the rules that search Web History artifacts for search queries. Expanded module to support multiple search engines for ambiguous URLs.
- Bluetooth pairing artifacts are created based on RegRipper output.
- Prefetch artifacts record the full path of exes.
- PhotoRec module allows you to include or exclude specific file types.
- Upgraded to Tika 1.23.
Performance:
- Documents are added to Solr in batches instead of one by one.
- More efficient queries to find WAL files for SQLite databases.
- Use a local drive for temp files for multi-user cases instead of the shared folder.
Command Line
- Command line support for report profiles.
- Restored support for Windows file type association for opening a case in Autopsy by double clicking case metadata (.aut) file.
- Better feedback for command line argument errors.
Misc:
- Updated versions of libvmdk, libvhdi, and libewf.
- Persona UI fixes: Pre-populate account and changed order of New Persona dialog.
- Streaming ingest support added to auto ingest.
- Recent Activity module processes now use the global timeout.
- Option to include Autopsy executable in portable case (Windows only.)
- Upgraded to NetBeans 11 Rich Client Platform.
- Added debug feature to save the stack trace on all threads.
---------------- VERSION 4.16.0 -------------- ---------------- VERSION 4.16.0 --------------

View File

@ -45,7 +45,7 @@ import org.sleuthkit.autopsy.url.analytics.DomainCategory;
* messaging: * messaging:
* https://www.raymond.cc/blog/list-of-web-messengers-for-your-convenience/ * https://www.raymond.cc/blog/list-of-web-messengers-for-your-convenience/
* *
* NOTE: The @SuppressWarnings("try") on the class is to suppress warnings * NOTE: The (at)SuppressWarnings("try") on the class is to suppress warnings
* relating to the fact that the close method can throw an InterruptedException * relating to the fact that the close method can throw an InterruptedException
* since Exception can encompass the InterruptedException. See the following * since Exception can encompass the InterruptedException. See the following
* github issue and bugs for more information: * github issue and bugs for more information:

View File

@ -68,7 +68,6 @@ import java.util.Set;
import java.util.HashSet; import java.util.HashSet;
import static java.util.Locale.US; import static java.util.Locale.US;
import static java.util.TimeZone.getTimeZone; import static java.util.TimeZone.getTimeZone;
import org.openide.util.Exceptions;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -98,7 +97,6 @@ import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
import org.sleuthkit.datamodel.Report; import org.sleuthkit.datamodel.Report;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException;
/** /**
* Extract windows registry data using regripper. Runs two versions of * Extract windows registry data using regripper. Runs two versions of
@ -539,7 +537,7 @@ class ExtractRegistry extends Extract {
//sometimes etime will be an empty string and therefore can not be parsed into a date //sometimes etime will be an empty string and therefore can not be parsed into a date
if (etime != null && !etime.isEmpty()) { if (etime != null && !etime.isEmpty()) {
try { try {
mtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(etime).getTime(); mtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US).parse(etime).getTime();
String Tempdate = mtime.toString(); String Tempdate = mtime.toString();
mtime = Long.valueOf(Tempdate) / MS_IN_SEC; mtime = Long.valueOf(Tempdate) / MS_IN_SEC;
} catch (ParseException ex) { } catch (ParseException ex) {
@ -604,7 +602,7 @@ class ExtractRegistry extends Extract {
case "InstallDate": //NON-NLS case "InstallDate": //NON-NLS
if (value != null && !value.isEmpty()) { if (value != null && !value.isEmpty()) {
try { try {
installtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(value).getTime(); installtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US).parse(value).getTime();
String Tempdate = installtime.toString(); String Tempdate = installtime.toString();
installtime = Long.valueOf(Tempdate) / MS_IN_SEC; installtime = Long.valueOf(Tempdate) / MS_IN_SEC;
} catch (ParseException e) { } catch (ParseException e) {
@ -783,7 +781,7 @@ class ExtractRegistry extends Extract {
try { try {
String mTimeAttr = artnode.getAttribute("mtime"); String mTimeAttr = artnode.getAttribute("mtime");
if (mTimeAttr != null && !mTimeAttr.isEmpty()) { if (mTimeAttr != null && !mTimeAttr.isEmpty()) {
itemMtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(mTimeAttr).getTime(); //NON-NLS itemMtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US).parse(mTimeAttr).getTime(); //NON-NLS
itemMtime /= MS_IN_SEC; itemMtime /= MS_IN_SEC;
} }
} catch (ParseException ex) { } catch (ParseException ex) {
@ -866,6 +864,8 @@ class ExtractRegistry extends Extract {
parentModuleName, sid)); parentModuleName, sid));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
parentModuleName, homeDir)); parentModuleName, homeDir));
newArtifacts.add(bbart);
} else { } else {
//add attributes to existing artifact //add attributes to existing artifact
BlackboardAttribute bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_NAME)); BlackboardAttribute bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_NAME));
@ -878,10 +878,10 @@ class ExtractRegistry extends Extract {
if (bbattr == null) { if (bbattr == null) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
parentModuleName, homeDir)); parentModuleName, homeDir));
} }
} }
bbart.addAttributes(bbattributes); bbart.addAttributes(bbattributes);
newArtifacts.add(bbart);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding account artifact to blackboard.", ex); //NON-NLS logger.log(Level.SEVERE, "Error adding account artifact to blackboard.", ex); //NON-NLS
} }
@ -1163,7 +1163,7 @@ class ExtractRegistry extends Extract {
Collection<BlackboardAttribute> getAttributesForAccount(Map<String, String> userInfo, List<String> groupList, boolean existingUser, AbstractFile regAbstractFile) throws ParseException { Collection<BlackboardAttribute> getAttributesForAccount(Map<String, String> userInfo, List<String> groupList, boolean existingUser, AbstractFile regAbstractFile) throws ParseException {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'"); SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'", US);
regRipperTimeFormat.setTimeZone(getTimeZone("GMT")); regRipperTimeFormat.setTimeZone(getTimeZone("GMT"));
if (!existingUser) { if (!existingUser) {
@ -1501,7 +1501,7 @@ class ExtractRegistry extends Extract {
} }
Collection<BlackboardAttribute> attributes = new ArrayList<>(); Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), adobeUsedTime)); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, getName(), adobeUsedTime));
attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if (bba != null) { if (bba != null) {
@ -1742,7 +1742,7 @@ class ExtractRegistry extends Extract {
String fileName = fileNameTokens[1]; String fileName = fileNameTokens[1];
Collection<BlackboardAttribute> attributes = new ArrayList<>(); Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), docDate)); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, getName(), docDate));
attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if (bba != null) { if (bba != null) {
@ -1805,7 +1805,7 @@ class ExtractRegistry extends Extract {
} }
Collection<BlackboardAttribute> attributes = new ArrayList<>(); Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), usedTime)); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, getName(), usedTime));
attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if (bba != null) { if (bba != null) {

View File

@ -294,6 +294,7 @@ final class ExtractZoneIdentifier extends Extract {
private static final String REFERRER_URL = "ReferrerUrl"; //NON-NLS private static final String REFERRER_URL = "ReferrerUrl"; //NON-NLS
private static final String HOST_URL = "HostUrl"; //NON-NLS private static final String HOST_URL = "HostUrl"; //NON-NLS
private static final String FAMILY_NAME = "LastWriterPackageFamilyName"; //NON-NLS private static final String FAMILY_NAME = "LastWriterPackageFamilyName"; //NON-NLS
private static String fileName;
private final Properties properties = new Properties(null); private final Properties properties = new Properties(null);
@ -307,6 +308,7 @@ final class ExtractZoneIdentifier extends Extract {
* @throws IOException * @throws IOException
*/ */
ZoneIdentifierInfo(AbstractFile zoneFile) throws IOException { ZoneIdentifierInfo(AbstractFile zoneFile) throws IOException {
fileName = zoneFile.getName();
properties.load(new ReadContentInputStream(zoneFile)); properties.load(new ReadContentInputStream(zoneFile));
} }
@ -318,8 +320,13 @@ final class ExtractZoneIdentifier extends Extract {
private int getZoneId() { private int getZoneId() {
int zoneValue = -1; int zoneValue = -1;
String value = properties.getProperty(ZONE_ID); String value = properties.getProperty(ZONE_ID);
if (value != null) { try {
zoneValue = Integer.parseInt(value); if (value != null) {
zoneValue = Integer.parseInt(value);
}
} catch (NumberFormatException ex) {
String message = String.format("Unable to parse Zone Id for File %s", fileName); //NON-NLS
LOG.log(Level.WARNING, message);
} }
return zoneValue; return zoneValue;

View File

@ -119,7 +119,7 @@ class RecentDocumentsByLnk extends Extract {
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"RecentDocumentsByLnk.parentModuleName.noSpace"), "RecentDocumentsByLnk.parentModuleName.noSpace"),
Util.findID(dataSource, path))); Util.findID(dataSource, path)));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"RecentDocumentsByLnk.parentModuleName.noSpace"), "RecentDocumentsByLnk.parentModuleName.noSpace"),
recentFile.getCrtime())); recentFile.getCrtime()));

View File

@ -1,5 +1,5 @@
<hr/> <hr/>
<p><i>Copyright &#169; 2012-2020 Basis Technology. Generated on $date<br/> <p><i>Copyright &#169; 2012-2021 Basis Technology. Generated on $date<br/>
This work is licensed under a This work is licensed under a
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>. <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>.
</i></p> </i></p>

View File

@ -8,7 +8,7 @@ The Android Analyzer ingest module runs aLEAPP (https://github.com/abrignoni/aLE
\section aleapp_config Using the Module \section aleapp_config Using the Module
Select the checkbox in the Ingest Modules settings screen to enable the Android Analzyer (ALEAPP) module. The module will run on .tar/.zip files found in a \ref ds_log "logical files data source" or a \ref ds_img disk image. Select the checkbox in the Ingest Modules settings screen to enable the Android Analzyer (ALEAPP) module. The module will run on .tar/.zip files found in a \ref ds_log "logical files data source" or a \ref ds_img "disk image".
\section aleapp_results Seeing Results \section aleapp_results Seeing Results

View File

@ -24,7 +24,7 @@ The middle column displays each account, its device and type, and the number of
Selecting an account in the middle column will bring up the data for that account in the right hand column. There are four tabs that show information about the selected account. Selecting an account in the middle column will bring up the data for that account in the right hand column. There are four tabs that show information about the selected account.
<ul> <ul>
<li> The <b>Summary</b> tab displays counts of how many times the account has appeared in different data types in the top section. In the middle it displays the files this account was found in. If the \ref central_repo_page is enabled, the bottom section will show any other cases that contained this account. <li> The <b>Summary</b> tab displays counts of how many times the account has appeared in different data types in the top section. In the middle it displays the files this account was found in. If the \ref central_repo_page is enabled, you can see if any \ref personas_page "personas" are associated with this account and whether any other cases that contained this account.
\image html cvt_summary_tab.png \image html cvt_summary_tab.png

View File

@ -32,7 +32,7 @@ The Types tab shows counts of different file types found in the data source.
\subsection ds_summary_user_activity User Activity \subsection ds_summary_user_activity User Activity
The User Activity tab shows the most recent results found in the data source. The User Activity tab shows the most recent results found in the data source. You can right click on a row to navigate directly to the corresponding result.
\image html ds_summary_user_activity.png \image html ds_summary_user_activity.png
@ -44,7 +44,7 @@ The Analysis tab shows the sets with the most results from the \ref hash_db_page
\subsection ds_summary_recent_files Recent Files \subsection ds_summary_recent_files Recent Files
The Recent Files tab shows information on the most recent files opened and downloaded. The Recent Files tab shows information on the most recent files opened and downloaded. You can right click on a row to navigate directly to the corresponding file or result.
\image html ds_summary_recent_files.png \image html ds_summary_recent_files.png
@ -56,6 +56,18 @@ The Past Cases tab shows which cases had results or notable files in common with
Note that because these entries are based on the Interesting Items results created during ingest and not querying the central repository, they will not reflect any matches in cases processed after this case. For example, suppose we create Case A and ingest a data source with Device Z. If we make a new case Case B afterward and ingest a data source that also has Device Z, we would see Case A listed in this tab for Case B, but if we reopened Case A we would not see Case B listed unless ingest was run again. Note that because these entries are based on the Interesting Items results created during ingest and not querying the central repository, they will not reflect any matches in cases processed after this case. For example, suppose we create Case A and ingest a data source with Device Z. If we make a new case Case B afterward and ingest a data source that also has Device Z, we would see Case A listed in this tab for Case B, but if we reopened Case A we would not see Case B listed unless ingest was run again.
\subsection ds_summary_geo Geolocation
The Geolocation tab uses the coordinates from geolocation results to find the nearest city for each and displays the most recent cities and most common cities. If the location is more than 150 km from a city then it will be displayed as "Unknown". The "View in Map" button under the recent cities table will open the \ref geolocation_page "Geolocation window" showing all waypoints for this data source with timestamps in the last 30 days. The "View in Map" button under the most common cities will show all waypoints for this data source.
\image html ds_summary_geo.png
\subsection ds_summary_timeline Timeline
The Timeline tab shows a simplified version of the \ref timeline_page "Timeline Viewer" for the selected data source. It will show events for the last 30 days of activity in the data source and give the first and last dates of activity. "File events" represent file creation, modification, access, and change. "Result events" represent the results from running ingest, such as the time a message was sent or when a URL was accessed. The "View in Timeline" button will open the main \ref timeline_page "Timeline Viewer".
\image html ds_summary_timeline.png
\subsection ds_summary_ingest_history Ingest History \subsection ds_summary_ingest_history Ingest History
The Ingest History tab shows which ingest modules have been run on the data source and the version of each module. The Ingest History tab shows which ingest modules have been run on the data source and the version of each module.

View File

@ -1,13 +1,13 @@
/*! \page drone_page Drone Analyzer /*! \page drone_page DJI Drone Analyzer
[TOC] [TOC]
\section drone_overview Overview \section drone_overview Overview
The Drone Analyzer module allows you to analyze files from a drone. The DJI Drone Analyzer module allows you to analyze files from a drone.
Currently, the Drone Analyzer module works on images obtained from the internal SD card found in the following DJI drone models: Currently, the DJI Drone Analyzer module works on images obtained from the internal SD card found in the following DJI drone models:
- Phantom 3 - Phantom 3
- Phantom 4 - Phantom 4
- Phantom 4 Pro - Phantom 4 Pro
@ -20,7 +20,7 @@ The module will find DAT files and process them using DatCon (https://datfile.ne
\section drone_config Running the Module \section drone_config Running the Module
To enable the Drone Analyzer ingest module select the checkbox in the \ref ingest_configure "Ingest Modules configuration screen". To enable the DJI Drone Analyzer ingest module select the checkbox in the \ref ingest_configure "Ingest Modules configuration screen".
\section drone_results Viewing Results \section drone_results Viewing Results

View File

@ -1,5 +1,5 @@
<hr/> <hr/>
<p><i>Copyright &#169; 2012-2020 Basis Technology. Generated on $date<br/> <p><i>Copyright &#169; 2012-2021 Basis Technology. Generated on $date<br/>
This work is licensed under a This work is licensed under a
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>. <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>.
</i></p> </i></p>

View File

@ -6,7 +6,7 @@
What Does It Do What Does It Do
======== ========
The Hash Lookup Module calculates MD5 hash values for files and looks up hash values in a database to determine if the file is notable, known (in general), included in a specific set of files, or unknown. The Hash Lookup Module calculates MD5 hash values for files and looks up hash values in a database to determine if the file is notable, known (in general), included in a specific set of files, or unknown. SHA-256 hashes are also calculated, though these will not be used in hash set lookups.
Configuration Configuration

View File

@ -8,7 +8,7 @@ The iOS Analyzer ingest module runs iLEAPP (https://github.com/abrignoni/iLEAPP)
\section ileapp_config Using the Module \section ileapp_config Using the Module
Select the checkbox in the Ingest Modules settings screen to enable the IOS Analzyer (iLEAPP) module. In Autopsy 4.17.0 the module only runs on .tar/.zip files found in a \ref ds_log "logical files data source". Select the checkbox in the Ingest Modules settings screen to enable the IOS Analzyer (iLEAPP) module. The module will run on .tar/.zip files found in a \ref ds_log "logical files data source" or a \ref ds_img "disk image".
\section ileapp_results Seeing Results \section ileapp_results Seeing Results

Some files were not shown because too many files have changed in this diff Show More