diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index fbb1430cc42cb46359a4dcb8c11bc0cae3ab40a5..30f782983e4bb1ec123cf9b01743b650409cf47a 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -52,8 +52,8 @@ Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}. Case.exceptionMessage.metadataUpdateError=Failed to update case metadata # {0} - exception message Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}. -Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a exclusive lock on the case. -Case.lockingException.couldNotAcquireSharedLock=Failed to get an shared lock on the case. +Case.lockingException.couldNotAcquireExclusiveLock=Failed to get an exclusive 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. # {0} - image Case.openFileSystems.openingImage=Opening all filesystems for image: {0}... diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 62dd5ddc3bb194a141862f8c5aced4916999662d..c2fd9d822f3f3f41a092f9dff1a7e1180d4a4a1c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -2828,8 +2828,8 @@ private void closeAppServiceCaseResources() { * @throws CaseActionException If the lock cannot be acquired. */ @Messages({ - "Case.lockingException.couldNotAcquireSharedLock=Failed to get an shared lock on the case.", - "Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a exclusive lock on the case." + "Case.lockingException.couldNotAcquireSharedLock=Failed to get a shared lock on the case.", + "Case.lockingException.couldNotAcquireExclusiveLock=Failed to get an exclusive lock on the case." }) private void acquireCaseLock(CaseLockType lockType) throws CaseActionException { String caseDir = metadata.getCaseDirectory(); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED index 0fc6762cc3ee21a070a2950a99247e0d4e216d72..329bcdd88f0cb2b8a632e00d707e3ef5a24016a3 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED @@ -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.description2=Press Configure PostgreSQL to change to a PostgreSQL database. 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.description2=The Central Repository stores hash values and accounts from past cases. -GlobalSettingsPanel.onMultiUserChange.enable.title=Use with Central Repository? +GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL server? +GlobalSettingsPanel.onMultiUserChange.enable.description2=Any data in an existing SQLite Central Repository will not be transferred to the new database. +GlobalSettingsPanel.onMultiUserChange.enable.title=Central Repository GlobalSettingsPanel.testCurrentConfiguration.dbDoesNotExist.message=Database does not exist. GlobalSettingsPanel.validationErrMsg.ingestRunning=You cannot change settings while ingest is running. GlobalSettingsPanel.validationerrMsg.mustConfigure=Configure the database to enable this module. diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java index 7cb1e566ddd8ada7918754cc1dc2efdc9937721e..d4dc5470180f2aa39ff4c164ab33fc8dd84b2fd5 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java @@ -151,9 +151,9 @@ private static boolean invokeCrChoice(Component parent, CentralRepoDbChoice init * as of most recent change. */ @NbBundle.Messages({ - "GlobalSettingsPanel.onMultiUserChange.enable.title=Use with Central Repository?", - "GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL database?", - "GlobalSettingsPanel.onMultiUserChange.enable.description2=The Central Repository stores hash values and accounts from past cases." + "GlobalSettingsPanel.onMultiUserChange.enable.title=Central Repository", + "GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL server?", + "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) { boolean crEnabled = CentralRepoDbUtil.allowUseOfCentralRepository(); diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java index 974d7ca29983111294f07c47b693f98916747570..ac81b15de61eb8f4555e2707865833ca4296a631 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java @@ -82,9 +82,9 @@ private static synchronized CVTPersonaCache getInstance() { } /** - * 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. * diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.form index c2451e28cb521623525bc3464bb8a3edb7375a05..4a4133f5e4ce32286f0abbf0db953906f878c953 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.form @@ -11,30 +11,32 @@ <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> - <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,0,0,0,0,0"/> </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> - <Container class="javax.swing.JPanel" name="detailsPanel"> - <Constraints> - <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription"> - <BorderConstraints direction="First"/> - </Constraint> - </Constraints> + <Container class="javax.swing.JScrollPane" name="detailsScrollPane"> + <Properties> + <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> + <Dimension value="[300, 0]"/> + </Property> + </Properties> - <Layout> - <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> + <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> </Container> </SubComponents> </Form> diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.java index 6dbf8f34e575d1d3ea36a8ef5ffdef837b36d75e..1b07ecd75465a68f500326a070275d09743c1a35 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.java @@ -37,7 +37,6 @@ import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; import javax.swing.JTextPane; import javax.swing.SwingUtilities; import org.apache.commons.lang.StringUtils; @@ -82,6 +81,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i private final GridBagLayout gridBagLayout = new GridBagLayout(); private final GridBagConstraints gridBagConstraints = new GridBagConstraints(); private final Map<Integer, Integer[]> orderingMap = new HashMap<>(); + private final javax.swing.JPanel detailsPanel = new javax.swing.JPanel(); /** * Creates new form GeneralPurposeArtifactViewer. @@ -90,6 +90,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i public GeneralPurposeArtifactViewer() { addOrderings(); initComponents(); + gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START; detailsPanel.setLayout(gridBagLayout); } @@ -161,8 +162,9 @@ public void setArtifact(BlackboardArtifact artifact) { } updateView(artifact, attributeMap, dataSourceName, sourceFileName); } + detailsScrollPane.setViewportView(detailsPanel); + detailsScrollPane.revalidate(); revalidate(); - repaint(); } /** @@ -172,7 +174,8 @@ public void setArtifact(BlackboardArtifact artifact) { private void resetComponent() { // clear the panel detailsPanel.removeAll(); - gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START; + detailsPanel.setLayout(gridBagLayout); + detailsPanel.revalidate(); gridBagConstraints.gridy = 0; gridBagConstraints.gridx = LABEL_COLUMN; gridBagConstraints.weighty = 0.0; @@ -183,14 +186,6 @@ private void resetComponent() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT) @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) { return (artifact != null) && (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID() @@ -218,22 +213,22 @@ public boolean isSupported(BlackboardArtifact artifact) { // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN: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); - detailsPanel.setLayout(detailsPanelLayout); - detailsPanelLayout.setHorizontalGroup( - detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(detailsScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); - detailsPanelLayout.setVerticalGroup( - detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .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 /** @@ -243,7 +238,7 @@ private void initComponents() { * @param attributeMap The map of attributes that exist for the artifact. * @param dataSourceName The name of the datasource that caused the creation * 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. */ @NbBundle.Messages({"GeneralPurposeArtifactViewer.dates.created=Created", @@ -257,7 +252,7 @@ private void initComponents() { private void updateView(BlackboardArtifact artifact, Map<Integer, List<BlackboardAttribute>> attributeMap, String dataSourceName, String sourceFilePath) { final Integer artifactTypeId = artifact.getArtifactTypeID(); if (!(artifactTypeId < 1 || artifactTypeId >= Integer.MAX_VALUE)) { - addDetailsHeader(artifactTypeId); + JTextPane firstTextPane = addDetailsHeader(artifactTypeId); Integer[] orderingArray = orderingMap.get(artifactTypeId); if (orderingArray == null) { orderingArray = DEFAULT_ORDERING; @@ -282,7 +277,6 @@ private void updateView(BlackboardArtifact artifact, Map<Integer, List<Blackboar addNameValueRow(bba.getAttributeType().getDisplayName(), displayString); } else { addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString()); - } } } @@ -311,7 +305,11 @@ private void updateView(BlackboardArtifact artifact, Map<Integer, List<Blackboar addNameValueRow(Bundle.GeneralPurposeArtifactViewer_details_file(), sourceFilePath); // add veritcal glue at the end addPageEndGlue(); + if (firstTextPane != null) { + firstTextPane.setCaretPosition(0); + } } + detailsPanel.revalidate(); } /** @@ -353,7 +351,7 @@ private boolean addDates(String label, List<BlackboardAttribute> attrList, boole "GeneralPurposeArtifactViewer.details.searchHeader=Web Search", "GeneralPurposeArtifactViewer.details.cachedHeader=Cached File", "GeneralPurposeArtifactViewer.details.cookieHeader=Cookie Details",}) - private void addDetailsHeader(int artifactTypeId) { + private JTextPane addDetailsHeader(int artifactTypeId) { String header; if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) { header = Bundle.GeneralPurposeArtifactViewer_details_historyHeader(); @@ -370,7 +368,7 @@ private void addDetailsHeader(int artifactTypeId) { } else { header = Bundle.GeneralPurposeArtifactViewer_details_attrHeader(); } - addHeader(header); + return addHeader(header); } /** @@ -381,16 +379,21 @@ private void addDetailsHeader(int artifactTypeId) { * @return JLabel Heading label added. */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) - private JLabel addHeader(String headerString) { + private JTextPane addHeader(String headerString) { // 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 // the first section if (gridBagConstraints.gridy != 0) { gridBagConstraints.gridy++; - detailsPanel.add(new javax.swing.JLabel(" "), gridBagConstraints); + // add to panel + addToPanel(new javax.swing.JLabel(" ")); addLineEndGlue(); - } + headingLabel.setFocusable(false); + } gridBagConstraints.gridy++; gridBagConstraints.gridx = LABEL_COLUMN;; // let the header span all of the row @@ -401,7 +404,7 @@ private JLabel addHeader(String headerString) { // make it large and bold headingLabel.setFont(headingLabel.getFont().deriveFont(Font.BOLD, headingLabel.getFont().getSize() + 2)); // add to panel - detailsPanel.add(headingLabel, gridBagConstraints); + addToPanel(headingLabel); // reset constraints to normal gridBagConstraints.gridwidth = LABEL_WIDTH; // add line end glue @@ -417,9 +420,9 @@ private JLabel addHeader(String headerString) { * @param keyString Key name to display. * @param valueString Value string to display. */ - private void addNameValueRow(String keyString, String valueString) { + private JTextPane addNameValueRow(String keyString, String valueString) { addKeyAtCol(keyString); - addValueAtCol(valueString); + return addValueAtCol(valueString); } /** @@ -432,7 +435,8 @@ private void addLineEndGlue() { gridBagConstraints.weightx = GLUE_WEIGHT_X; // take up all the horizontal space 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)); - detailsPanel.add(horizontalFiller, gridBagConstraints); + // add to panel + addToPanel(horizontalFiller); // restore fill & weight gridBagConstraints.fill = GridBagConstraints.NONE; gridBagConstraints.weightx = TEXT_WEIGHT_X; @@ -446,7 +450,8 @@ private void addPageEndGlue() { gridBagConstraints.weighty = 1.0; // take up all the vertical space 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)); - detailsPanel.add(vertFiller, gridBagConstraints); + // add to panel + addToPanel(vertFiller); } /** @@ -459,16 +464,22 @@ private void addPageEndGlue() { private JLabel addKeyAtCol(String keyString) { // create label javax.swing.JLabel keyLabel = new javax.swing.JLabel(); + keyLabel.setFocusable(false); gridBagConstraints.gridy++; gridBagConstraints.gridx = LABEL_COLUMN; gridBagConstraints.gridwidth = LABEL_WIDTH; // set text keyLabel.setText(keyString + ": "); // add to panel - detailsPanel.add(keyLabel, gridBagConstraints); + addToPanel(keyLabel); return keyLabel; } + private void addToPanel(Component comp) { + detailsPanel.add(comp, gridBagConstraints); + detailsPanel.revalidate(); + } + /** * Adds a value string to the panel at specified column. * @@ -479,6 +490,7 @@ private JLabel addKeyAtCol(String keyString) { private JTextPane addValueAtCol(String valueString) { // create label, JTextPane valueField = new JTextPane(); + valueField.setFocusable(false); valueField.setEditable(false); valueField.setOpaque(false); gridBagConstraints.gridx = VALUE_COLUMN; @@ -488,8 +500,6 @@ private JTextPane addValueAtCol(String valueString) { cloneConstraints.fill = GridBagConstraints.BOTH; // set text valueField.setText(valueString); - // scroll to start of text - valueField.setCaretPosition(0); // attach a right click menu with Copy option valueField.addMouseListener(new java.awt.event.MouseAdapter() { @Override @@ -497,8 +507,9 @@ public void mouseClicked(java.awt.event.MouseEvent evt) { valueLabelMouseClicked(evt, valueField); } }); - // add label to panel + // add label to panel with cloned contraintsF detailsPanel.add(valueField, cloneConstraints); + revalidate(); // end the line addLineEndGlue(); return valueField; @@ -531,6 +542,6 @@ public void actionPerformed(ActionEvent e) { // 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 } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageAccountPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageAccountPanel.java index 8cf43371633e0c94f35760ec41f627b05b3e88c0..fbf1ada856d68b92a262eb288c7b3fba728c6c88 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageAccountPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/MessageAccountPanel.java @@ -586,6 +586,7 @@ public void actionPerformed(ActionEvent e) { // Set up each matching account. We don't know what type of account we have, so check all the types to // find any matches. try { + boolean showErrorMessage = true; for (CentralRepoAccount.CentralRepoAccountType type : CentralRepository.getInstance().getAllAccountTypes()) { try { // Try to load any matching accounts of this type. Throws an InvalidAccountIDException if the account is the @@ -593,13 +594,15 @@ public void actionPerformed(ActionEvent e) { CentralRepoAccount account = CentralRepository.getInstance().getAccount(type, accountContainer.getAccount().getTypeSpecificID()); if (account != null) { personaPanel.addAccount(account, Bundle.MessageAccountPanel_account_justification(), Persona.Confidence.HIGH); - } else { - createPersonaDialog.setStartupPopupMessage(Bundle.MessageAccountPanel_id_not_found_in_cr(accountContainer.getAccount().getTypeSpecificID())); - } + showErrorMessage = false; + } } catch (InvalidAccountIDException ex2) { // 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) { logger.log(Level.SEVERE, "Error looking up account types in the central repository", ex); } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/PersonaAccountFetcher.java b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/PersonaAccountFetcher.java index 0601c0893d4814a0302ad5e971f96b2fbd173b3f..020a2645b62924f64b8b33c6566430db7dd494fe 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/PersonaAccountFetcher.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/PersonaAccountFetcher.java @@ -200,6 +200,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { // Set up each matching account. We don't know what type of account we have, so check all the types to // find any matches. try { + boolean showErrorMessage = true; for (CentralRepoAccount.CentralRepoAccountType type : CentralRepository.getInstance().getAllAccountTypes()) { try { // Try to load any matching accounts of this type. Throws an InvalidAccountIDException if the account is the @@ -207,16 +208,17 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { CentralRepoAccount account = CentralRepository.getInstance().getAccount(type, personaSearcherData.getAccountIdentifer()); if (account != null) { 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) { // 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) { logger.log(Level.SEVERE, "Error looking up account types in the central repository", ex); } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/StringsTextViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/StringsTextViewer.java index 1d4a6aef0376d18327dbba810093e39ea8b0178a..666c38d0705dd41bbce404fe0c5e2453b0db93c9 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/StringsTextViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/textcontentviewer/StringsTextViewer.java @@ -25,6 +25,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.TextViewer; import org.sleuthkit.autopsy.corecomponents.DataContentViewerUtility; import org.sleuthkit.autopsy.datamodel.StringContent; +import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; /** @@ -81,7 +82,7 @@ public boolean isSupported(Node node) { return false; } Content content = DataContentViewerUtility.getDefaultContent(node); - return (content != null && content.getSize() > 0); + return (content != null && !(content instanceof BlackboardArtifact) && content.getSize() > 0); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java index 8f3c2e8e140eca3310382a36aa412097b1b05bd6..0d3fd0198a7d15a3fc231ca2401e16dcab86b52a 100644 --- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java +++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java @@ -84,6 +84,7 @@ public final class UserPreferences { private static final boolean DISPLAY_TRANSLATED_NAMES_DEFAULT = true; public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath"; 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"; private static final String GEO_TILE_OPTION = "GeolocationTileOption"; private static final String GEO_OSM_TILE_ZIP_PATH = "GeolocationOsmZipPath"; @@ -535,10 +536,10 @@ public static void setLogFileCount(int count) { /** * 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() { - return preferences.getInt(SOLR_MAX_JVM_SIZE, 512); + return preferences.getInt(SOLR_MAX_JVM_SIZE, DEFAULT_SOLR_HEAP_SIZE_MB); } /** diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java index 7f4f8bf3a7d340b0b9a89e20702b9338bab01f98..e7c0f9eaebf8d3df637f92a2c5357c6b3542725a 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java @@ -82,6 +82,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { 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 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 Logger logger = Logger.getLogger(AutopsyOptionsPanel.class.getName()); 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 // constructor is called. 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(); agencyLogoPathField.getDocument().addDocumentListener(textFieldListener); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java index be8f854b961f6d3f9672e1d43ddde29afc0f5af8..10188826b1334bc53606814b476ef59f30ea42d1 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerHex.java @@ -46,6 +46,7 @@ import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.DataConversion; +import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; @@ -606,7 +607,7 @@ public boolean isSupported(Node node) { return false; } Content content = DataContentViewerUtility.getDefaultContent(node); - return content != null && content.getSize() > 0; + return content != null && !(content instanceof BlackboardArtifact) && content.getSize() > 0; } @Override diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerUtility.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerUtility.java index d460a16f03e26b9d9bc4ed282a0bb6278be2aa42..2356a25a3bb1e352b4251e43ee649426022d728c 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerUtility.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerUtility.java @@ -32,9 +32,6 @@ public class DataContentViewerUtility { * preferring to return any Content object other than a BlackboardArtifact * 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. * * @return If there are multiple Content objects associated with the Node, diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index efdfb12c4890a55700359f008c3bca5b5380cce5..5bfca75425ea364cc7921c6b7193567e6e983883 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.datamodel; +import com.google.common.annotations.Beta; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import java.beans.PropertyChangeEvent; @@ -234,7 +235,7 @@ public void propertyChange(PropertyChangeEvent evt) { * @param iconPath The path to the icon for the artifact type. */ public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) { - super(artifact, createLookup(artifact)); + super(artifact, createLookup(artifact, false)); this.artifact = artifact; for (Content lookupContent : this.getLookup().lookupAll(Content.class)) { if ((lookupContent != null) && (!(lookupContent instanceof BlackboardArtifact))) { @@ -264,6 +265,49 @@ public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) { 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 * can be used to represent an artifact of any type. @@ -284,22 +328,46 @@ public BlackboardArtifactNode(BlackboardArtifact artifact) { */ private static Lookup createLookup(BlackboardArtifact artifact) { final long objectID = artifact.getObjectID(); - Content content = null; try { - if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() || artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID()) { - content = getPathIdFile(artifact); - } + Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID)); if (content == null) { - content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID)); + return Lookups.fixed(artifact); + } else { + return Lookups.fixed(artifact, content); } } 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); + } + } + + /** + * 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 { - return Lookups.fixed(artifact, content); + return createLookup(artifact); } } @@ -982,7 +1050,7 @@ private void fillPropertyMap(Map<String, Object> map, BlackboardArtifact artifac } map.put(attribute.getAttributeType().getDisplayName(), value); } else { - switch(attribute.getAttributeType().getValueType()) { + switch (attribute.getAttributeType().getValueType()) { case INTEGER: map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueInt()); break; @@ -990,13 +1058,13 @@ private void fillPropertyMap(Map<String, Object> map, BlackboardArtifact artifac map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueDouble()); break; case LONG: - map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong()); + map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong()); break; default: - map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString()); - + map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString()); + } - + } } } catch (TskCoreException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourceGroupingNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourceGroupingNode.java index 4c8ee90e7c2294856e4b37d892e44f8c41b66865..214ab3ead92bcc53f5965e100dc5cfcaff78a550 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourceGroupingNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourceGroupingNode.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.Optional; import java.util.logging.Level; +import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; @@ -46,7 +47,8 @@ class DataSourceGroupingNode extends DisplayableItemNode { DataSourceGroupingNode(DataSource 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) { Image image = (Image) dataSource; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/utils/IconsUtil.java b/Core/src/org/sleuthkit/autopsy/datamodel/utils/IconsUtil.java index 5660aa0e0006006606fd03cc0ce209bfd3d9aa68..78cee32000fe66fd19857e63c08596e6b95cc74a 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/utils/IconsUtil.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/utils/IconsUtil.java @@ -125,6 +125,8 @@ public static String getIconFilePath(int typeID) { imageFile = "domain-16.png"; //NON-NLS } else if (typeID == ARTIFACT_TYPE.TSK_GPS_AREA.getTypeID()) { imageFile = "gps-area.png"; //NON-NLS + } else if (typeID == ARTIFACT_TYPE.TSK_YARA_HIT.getTypeID()) { + imageFile = "yara_16.png"; //NON-NLS } else { imageFile = "artifact-icon.png"; //NON-NLS } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/CityRecord.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/CityRecord.java index ac4952674ca9c1fa67ce7d8ad40eee7d4991366d..d2a1182e6a1e112160c77855733c38a4a359a60a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/CityRecord.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/CityRecord.java @@ -28,18 +28,21 @@ public class CityRecord extends KdTree.XYZPoint { private final String cityName; private final String country; + private final String state; /** * Main constructor. * * @param cityName The name of the city. + * @param state The state of the city. * @param country The country of that city. * @param latitude Latitude 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); this.cityName = cityName; + this.state = state; this.country = country; } @@ -50,6 +53,13 @@ public String getCityName() { return cityName; } + /** + * @return The state of the city. + */ + public String getState() { + return state; + } + /** * @return The country of that city. */ diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ClosestCityMapper.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ClosestCityMapper.java index 13290e3c9a87febec9ac7e6af302672445538ae6..4427d25f23b0d645dc105128fdf99c1d580eb84c 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ClosestCityMapper.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/ClosestCityMapper.java @@ -42,6 +42,7 @@ class ClosestCityMapper { // index within a csv row of pertinent data 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 LAT_IDX = 2; private static final int LONG_IDX = 3; @@ -52,7 +53,7 @@ class ClosestCityMapper { // 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 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) .get(); @@ -169,12 +170,15 @@ private CityRecord getCsvCityRecord(List<String> csvRow, int lineNum) { return null; } + // city is required String cityName = csvRow.get(CITY_NAME_IDX); if (StringUtils.isBlank(cityName)) { logger.log(Level.WARNING, String.format("No city name determined for line %d.", lineNum)); return null; } + // state and country can be optional + String stateName = csvRow.get(STATE_NAME_IDX); String countryName = parseCountryName(csvRow.get(COUNTRY_NAME_IDX), lineNum); Double lattitude = tryParse(csvRow.get(LAT_IDX)); @@ -189,7 +193,7 @@ private CityRecord getCsvCityRecord(List<String> csvRow, int lineNum) { return null; } - return new CityRecord(cityName, countryName, lattitude, longitude); + return new CityRecord(cityName, stateName, countryName, lattitude, longitude); } /** diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/GeolocationSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/GeolocationSummary.java index 4f78e1ece2d5d1cc14f3d90de23a7516c96b9daa..046c443e4efc5d2b677ffa4319b96fccc8cff09b 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/GeolocationSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/GeolocationSummary.java @@ -309,7 +309,7 @@ public CityData getCityCounts(DataSource dataSource, int daysCount, int maxCount Long mostRecent = null; 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(); if (curTime != null && (mostRecent == null || curTime > mostRecent)) { mostRecent = curTime; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/IngestModuleCheckUtil.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/IngestModuleCheckUtil.java deleted file mode 100644 index 3477dd5ff245ceb1843ed47f145837dc44e7a591..0000000000000000000000000000000000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/IngestModuleCheckUtil.java +++ /dev/null @@ -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)); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java index 34dfa97a1322deb458ffa71ab84fc1c8a18cdd66..d2839a7f54c22661deafa598987d9c86ef4cf2c9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java @@ -29,8 +29,6 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; 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; /** @@ -46,15 +44,6 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { 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 */ @@ -90,9 +79,8 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { keywordHitsTable, interestingItemsTable ); - + private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); - /** * All of the components necessary for data fetch swing workers to load data @@ -115,28 +103,26 @@ public AnalysisPanel(AnalysisSummary analysisData) { // hashset hits loading components new DataFetchWorker.DataFetchComponents<>( (dataSource) -> analysisData.getHashsetCounts(dataSource), - (result) -> showResultWithModuleCheck(hashsetHitsTable, result, HASHSET_FACTORY, HASHSET_MODULE_NAME)), + (result) -> hashsetHitsTable.showDataFetchResult(result)), // keyword hits loading components new DataFetchWorker.DataFetchComponents<>( (dataSource) -> analysisData.getKeywordCounts(dataSource), - (result) -> showResultWithModuleCheck(keywordHitsTable, result, KEYWORD_SEARCH_FACTORY, KEYWORD_SEARCH_MODULE_NAME)), + (result) -> keywordHitsTable.showDataFetchResult(result)), // interesting item hits loading components new DataFetchWorker.DataFetchComponents<>( (dataSource) -> analysisData.getInterestingItemCounts(dataSource), - (result) -> showResultWithModuleCheck(interestingItemsTable, result, INTERESTING_ITEM_FACTORY, INTERESTING_ITEM_MODULE_NAME)) + (result) -> interestingItemsTable.showDataFetchResult(result)) ); initComponents(); } - @Override public void close() { ingestRunningLabel.unregister(); super.close(); } - - + @Override protected void fetchInformation(DataSource dataSource) { fetchInformation(dataFetchComponents, dataSource); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java index 96767fcb8fba4169d6f500da2aa23c6a871b8401..4abb5bcc32f65506bb16d60ba604a74e8c9364c6 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java @@ -25,7 +25,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.function.Predicate; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -39,11 +38,8 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; 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.DataFetchResult; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; 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 final SwingWorkerSequentialExecutor executor = new SwingWorkerSequentialExecutor(); - private final IngestModuleCheckUtil ingestModuleCheck = new IngestModuleCheckUtil(); private final EventUpdateHandler updateHandler; private final List<UpdateGovernor> governors; @@ -319,8 +314,8 @@ protected CellModelTableCellRenderer.MenuItem getFileNavigateItem(String path) { } /** - * Given the relevant file, navigates to the file in the - * tree and closes data source summary dialog if open. + * Given the relevant file, navigates to the file in the tree and closes + * data source summary dialog if open. * * @param file The file. * @return The menu item list for a go to artifact menu item. @@ -471,76 +466,4 @@ protected void onNewDataSource( 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); - } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/GeolocationPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/GeolocationPanel.java index 5bf4efceb45f34650d5ed27705f3d0983a4932cd..a4692abbf49d815d73eeae4f98a4bdbec7e6899d 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/GeolocationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/GeolocationPanel.java @@ -63,8 +63,6 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel { 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 MAX_COUNT = 10; @@ -114,6 +112,8 @@ public GeolocationPanel() { * @param whereUsedData The GeolocationSummary instance to use. */ public GeolocationPanel(GeolocationSummary whereUsedData) { + super(whereUsedData); + this.whereUsedData = whereUsedData; // set up data acquisition methods dataFetchComponents = Arrays.asList( @@ -145,11 +145,19 @@ private static String getCityName(CityRecord record) { return null; } - if (StringUtils.isBlank(record.getCountry())) { - return record.getCityName(); - } + List<String> cityIdentifiers = Stream.of(record.getCityName(), record.getState(), record.getCountry()) + .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 @@ private void showCityContent(DataFetchResult<CityCountsList> result, JTablePanel goToGeolocation.setEnabled(true); } - showResultWithModuleCheck(table, convertedData, GPX_FACTORY, GPX_NAME); + table.showDataFetchResult(convertedData); } /** @@ -225,7 +233,7 @@ private void showCityContent(DataFetchResult<CityCountsList> result, JTablePanel private void openGeolocationWindow(DataSource dataSource, Integer daysLimit) { // notify dialog (if in dialog) should close. notifyParentClose(); - + // set the filter TopComponent topComponent = WindowManager.getDefault().findTopComponent(GeolocationTopComponent.class.getSimpleName()); if (topComponent instanceof GeolocationTopComponent) { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java index 2400334ee3d3e59f856e643c384c415da9f6426e..0181ced0249d316a3f5e17580e5391a29f1f8bb4 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/PastCasesPanel.java @@ -22,7 +22,6 @@ import java.util.List; import org.apache.commons.lang3.tuple.Pair; 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.PastCasesResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; @@ -46,8 +45,6 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel { 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<>( Bundle.PastCasesPanel_caseColumn_title(), @@ -84,6 +81,8 @@ public PastCasesPanel() { * Creates new form PastCasesPanel */ public PastCasesPanel(PastCasesSummary pastCaseData) { + super(pastCaseData); + // set up data acquisition methods dataFetchComponents = Arrays.asList( new DataFetchWorker.DataFetchComponents<>( @@ -101,8 +100,8 @@ public PastCasesPanel(PastCasesSummary pastCaseData) { * @param result The result. */ private void handleResult(DataFetchResult<PastCasesResult> result) { - showResultWithModuleCheck(notableFileTable, DataFetchResult.getSubResult(result, (res) -> res.getTaggedNotable()), CR_FACTORY, CR_NAME); - showResultWithModuleCheck(sameIdTable, DataFetchResult.getSubResult(result, (res) -> res.getSameIdsResults()), CR_FACTORY, CR_NAME); + notableFileTable.showDataFetchResult(DataFetchResult.getSubResult(result, (res) -> res.getTaggedNotable())); + sameIdTable.showDataFetchResult(DataFetchResult.getSubResult(result, (res) -> res.getSameIdsResults())); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java index 2f94f2d5985630c64ef141222fc327fe6499ab7f..64fa4817c38fd42657b28c42fe4e71f5cee5641d 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.function.Supplier; 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.RecentAttachmentDetails; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails; @@ -44,8 +43,6 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { 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<DataFetchWorker.DataFetchComponents<DataSource, ?>> dataFetchComponents = new ArrayList<>(); @@ -167,11 +164,7 @@ private void initalizeOpenDocsTable() { DataFetchWorker.DataFetchComponents<DataSource, List<RecentFileDetails>> worker = new DataFetchWorker.DataFetchComponents<>( (dataSource) -> dataHandler.getRecentlyOpenedDocuments(dataSource, 10), - (result) -> { - showResultWithModuleCheck(pane, result, - IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY, - IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME); - }); + (result) -> pane.showDataFetchResult(result)); dataFetchComponents.add(worker); } @@ -210,11 +203,7 @@ private void initalizeDownloadTable() { DataFetchWorker.DataFetchComponents<DataSource, List<RecentDownloadDetails>> worker = new DataFetchWorker.DataFetchComponents<>( (dataSource) -> dataHandler.getRecentDownloads(dataSource, 10), - (result) -> { - showResultWithModuleCheck(pane, result, - IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY, - IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME); - }); + (result) -> pane.showDataFetchResult(result)); dataFetchComponents.add(worker); } @@ -253,7 +242,7 @@ private void initalizeAttchementsTable() { DataFetchWorker.DataFetchComponents<DataSource, List<RecentAttachmentDetails>> worker = new DataFetchWorker.DataFetchComponents<>( (dataSource) -> dataHandler.getRecentAttachments(dataSource, 10), - (result) -> showResultWithModuleCheck(pane, result, EMAIL_PARSER_FACTORY, EMAIL_PARSER_MODULE_NAME) + (result) -> pane.showDataFetchResult(result) ); dataFetchComponents.add(worker); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java index b4c31945455bced350510c07aae709db1a442213..436f968aa8260e51d1ea7bf39f17bd58badc9fa6 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java @@ -103,6 +103,8 @@ public TimelinePanel() { * Creates new form PastCasesPanel */ public TimelinePanel(TimelineSummary timelineData) { + super(timelineData); + // set up data acquisition methods dataFetchComponents = Arrays.asList( new DataFetchWorker.DataFetchComponents<>( diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java index 247aa0c304ee94ae720d6dbf79f14ae0ca9e9f50..f6c952d4b53c53728cd1781dfa5e71f0966a680f 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java @@ -26,16 +26,13 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary; 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.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; @@ -251,25 +248,11 @@ public TypesPanel( // usage label worker new DataFetchWorker.DataFetchComponents<>( containerData::getDataSourceType, - (result) -> { - showResultWithModuleCheck( - usageLabel, - result, - StringUtils::isNotBlank, - IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY, - IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME); - }), + (result) -> usageLabel.showDataFetchResult(result)), // os label worker new DataFetchWorker.DataFetchComponents<>( containerData::getOperatingSystems, - (result) -> { - showResultWithModuleCheck( - osLabel, - result, - StringUtils::isNotBlank, - IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY, - IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME); - }), + (result) -> osLabel.showDataFetchResult(result)), // size label worker new DataFetchWorker.DataFetchComponents<>( (dataSource) -> { @@ -379,50 +362,22 @@ private TypesPieChartData getMimeTypeCategoriesModel(MimeTypeSummary mimeTypeDat * @param result The result to be shown. */ private void showMimeTypeCategories(DataFetchResult<TypesPieChartData> result) { - // if result is null check for ingest module and show empty results. if (result == null) { - showPieResultWithModuleCheck(null); + fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(null)); return; } // if error, show error if (result.getResultType() == ResultType.ERROR) { - this.fileMimeTypesChart.showDataFetchResult(DataFetchResult.getErrorResult(result.getException())); + fileMimeTypesChart.showDataFetchResult(DataFetchResult.getErrorResult(result.getException())); return; } TypesPieChartData data = result.getData(); if (data == null) { - // if no data, do an ingest module check with empty results - showPieResultWithModuleCheck(null); - } else if (!data.isUsefulContent()) { - // if no useful data, do an ingest module check and show data - showPieResultWithModuleCheck(data.getPieSlices()); + fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(null)); } else { - // otherwise, show the data - 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)); + fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(data.getPieSlices())); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java index 63671e16f2969e8b304693fb34b9da3c431001d8..c1ac2b2c6bc7b791c95f2ad0a6687960fff01cbe 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java @@ -27,7 +27,6 @@ import java.util.Locale; import org.apache.commons.lang.StringUtils; 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.LastAccessedArtifact; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult; @@ -287,43 +286,23 @@ public UserActivityPanel(UserActivitySummary userActivityData) { // top programs query new DataFetchComponents<DataSource, List<TopProgramsResult>>( (dataSource) -> userActivityData.getTopPrograms(dataSource, TOP_PROGS_COUNT), - (result) -> { - showResultWithModuleCheck(topProgramsTable, result, - IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY, - IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME); - }), + (result) -> topProgramsTable.showDataFetchResult(result)), // top domains query new DataFetchComponents<DataSource, List<TopDomainsResult>>( (dataSource) -> userActivityData.getRecentDomains(dataSource, TOP_DOMAINS_COUNT), - (result) -> { - showResultWithModuleCheck(recentDomainsTable, result, - IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY, - IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME); - }), + (result) -> recentDomainsTable.showDataFetchResult(result)), // top web searches query new DataFetchComponents<DataSource, List<TopWebSearchResult>>( (dataSource) -> userActivityData.getMostRecentWebSearches(dataSource, TOP_SEARCHES_COUNT), - (result) -> { - showResultWithModuleCheck(topWebSearchesTable, result, - IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY, - IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME); - }), + (result) -> topWebSearchesTable.showDataFetchResult(result)), // top devices query new DataFetchComponents<DataSource, List<TopDeviceAttachedResult>>( (dataSource) -> userActivityData.getRecentDevices(dataSource, TOP_DEVICES_COUNT), - (result) -> { - showResultWithModuleCheck(topDevicesAttachedTable, result, - IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY, - IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME); - }), + (result) -> topDevicesAttachedTable.showDataFetchResult(result)), // top accounts query new DataFetchComponents<DataSource, List<TopAccountResult>>( (dataSource) -> userActivityData.getRecentAccounts(dataSource, TOP_ACCOUNTS_COUNT), - (result) -> { - showResultWithModuleCheck(topAccountsTable, result, - ANDROID_FACTORY, - ANDROID_MODULE_NAME); - }) + (result) -> topAccountsTable.showDataFetchResult(result)) ); initComponents(); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryAttributes.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryAttributes.java index 15171cf945239515a72c72cd0b9429e599f1e566..77f67899ebad334658a6b411ec9c1c61b9be2763 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryAttributes.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryAttributes.java @@ -140,9 +140,10 @@ public DiscoveryKeyUtils.GroupKey getGroupKey(Result 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 { @@ -150,7 +151,7 @@ static class DomainCategoryAttribute extends AttributeType { public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) { return new DiscoveryKeyUtils.DomainCategoryGroupKey(result); } - + @Override public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException { @@ -167,10 +168,11 @@ public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb, 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. - * Each ResultDomain is then parsed and matched against this map of values. + * Loads all TSK_WEB_CATEGORY artifacts and maps the domain attribute to + * 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 { Map<String, String> domainToCategory = new HashMap<>(); @@ -190,7 +192,7 @@ private Map<String, String> getDomainsWithWebCategories(SleuthkitCase caseDb) th } } - return domainToCategory; + return domainToCategory; } } @@ -269,36 +271,36 @@ public void process(ResultSet rs) { } } } - + /** - * Organizes the domain instances by normalized domain value. - * This helps reduce the complexity of updating ResultDomain instances - * after the query has been executed. - * - * Example: query for notable status of google.com. Result: notable - * With this map, all domain instances that represent google.com can - * be updated after one simple lookup. + * Organizes the domain instances by normalized domain value. This helps + * reduce the complexity of updating ResultDomain instances after the query + * has been executed. + * + * Example: query for notable status of google.com. Result: notable With + * this map, all domain instances that represent google.com can be updated + * after one simple lookup. */ private static Map<String, List<ResultDomain>> organizeByValue(List<ResultDomain> domainsBatch, CorrelationAttributeInstance.Type attributeType) { - final Map<String, List<ResultDomain>> resultDomainTable = new HashMap<>(); - for (ResultDomain domainInstance : domainsBatch) { - try { - final String domainValue = domainInstance.getDomain(); - final String normalizedDomain = CorrelationAttributeNormalizer.normalize(attributeType, domainValue); - final List<ResultDomain> bucket = resultDomainTable.getOrDefault(normalizedDomain, new ArrayList<>()); - bucket.add(domainInstance); - resultDomainTable.put(normalizedDomain, bucket); - } catch (CorrelationAttributeNormalizationException ex) { - logger.log(Level.INFO, String.format("Domain [%s] failed normalization, skipping...", domainInstance.getDomain())); - } + final Map<String, List<ResultDomain>> resultDomainTable = new HashMap<>(); + for (ResultDomain domainInstance : domainsBatch) { + try { + final String domainValue = domainInstance.getDomain(); + final String normalizedDomain = CorrelationAttributeNormalizer.normalize(attributeType, domainValue); + final List<ResultDomain> bucket = resultDomainTable.getOrDefault(normalizedDomain, new ArrayList<>()); + bucket.add(domainInstance); + resultDomainTable.put(normalizedDomain, bucket); + } catch (CorrelationAttributeNormalizationException ex) { + 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. - * Each value is wrapped in `'`. This method is used to bundle up - * a collection of values for use in a SQL WHERE IN (...) clause. + * Helper function to create a string of comma separated values. Each value + * is wrapped in `'`. This method is used to bundle up a collection of + * values for use in a SQL WHERE IN (...) clause. */ private static String createCSV(Set<String> values) { StringJoiner joiner = new StringJoiner(", "); @@ -307,30 +309,30 @@ private static String createCSV(Set<String> values) { } return joiner.toString(); } - + /** * Attribute for grouping/sorting by notability in the CR. */ static class PreviouslyNotableAttribute extends AttributeType { - + static final int DOMAIN_BATCH_SIZE = 500; // Number of domains to look up at one time @Override public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) { return new DiscoveryKeyUtils.PreviouslyNotableGroupKey(result); } - + @Override public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException { - + if (centralRepoDb != null) { processFilesWithCr(results, centralRepoDb); - } + } } - + private void processFilesWithCr(List<Result> results, CentralRepository centralRepo) throws DiscoveryException { - + List<ResultDomain> domainsBatch = new ArrayList<>(); for (Result result : results) { if (result.getType() == SearchData.Type.DOMAIN) { @@ -341,15 +343,15 @@ private void processFilesWithCr(List<Result> results, CentralRepository centralR } } } - + queryPreviouslyNotable(domainsBatch, centralRepo); } - + private void queryPreviouslyNotable(List<ResultDomain> domainsBatch, CentralRepository centralRepo) throws DiscoveryException { if (domainsBatch.isEmpty()) { return; } - + try { final CorrelationAttributeInstance.Type attributeType = centralRepo.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID); final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsBatch, attributeType); @@ -371,16 +373,16 @@ private void queryPreviouslyNotable(List<ResultDomain> domainsBatch, CentralRepo throw new DiscoveryException("Fatal exception encountered querying the CR.", ex); } } - + private static class DomainPreviouslyNotableCallback implements InstanceTableCallback { - + private final Map<String, List<ResultDomain>> domainLookup; private SQLException sqlCause; private DomainPreviouslyNotableCallback(Map<String, List<ResultDomain>> domainLookup) { this.domainLookup = domainLookup; } - + @Override public void process(ResultSet resultSet) { try { @@ -401,7 +403,7 @@ public void process(ResultSet resultSet) { */ SQLException getCause() { return this.sqlCause; - } + } } } @@ -499,12 +501,13 @@ private static void queryDomainFrequency(List<ResultDomain> domainsToQuery, Cent final CorrelationAttributeInstance.Type attributeType = centralRepository.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID); final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsToQuery, attributeType); final String values = createCSV(resultDomainTable.keySet()); - final String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType); - final String domainFrequencyQuery = " value AS domain_name, COUNT(*) AS frequency " - + "FROM " + tableName + " " - + "WHERE value IN (" + values + ") " - + "GROUP BY value"; + final String domainFrequencyQuery = " value AS domain_name, COUNT(value) AS frequency FROM" + + "(SELECT DISTINCT case_id, value FROM " + + tableName + + " WHERE value IN (" + + values + + ")) AS foo GROUP BY value"; final DomainFrequencyCallback frequencyCallback = new DomainFrequencyCallback(resultDomainTable); centralRepository.processSelectClause(domainFrequencyQuery, frequencyCallback); @@ -784,8 +787,8 @@ public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) { } /** - * Attribute for grouping/sorting domains by number of page views. - * Page views is defined at the number of TSK_WEB_HISTORY artifacts. + * Attribute for grouping/sorting domains by number of page views. Page + * views is defined at the number of TSK_WEB_HISTORY artifacts. */ static class PageViewsAttribute extends AttributeType { @@ -1074,4 +1077,4 @@ private static String createSetNameClause(List<Result> results, private DiscoveryAttributes() { // Class should not be instantiated } - } +} diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryEventUtils.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryEventUtils.java index 7dbfb8848c414c16a25b37c2f2d4462ef025e917..e228ce5f21b57ce3f1517ccf904791337b554a51 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryEventUtils.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryEventUtils.java @@ -277,17 +277,20 @@ public BlackboardArtifact.ARTIFACT_TYPE getArtifactType() { public static final class MiniTimelineResultEvent { private final List<MiniTimelineResult> results = new ArrayList<>(); + private final String domain; /** * Construct a new MiniTimelineResultEvent. * * @param results The list of MiniTimelineResults contained in this * event. + * @param domain The domain the results are for. */ - public MiniTimelineResultEvent(List<MiniTimelineResult> results) { + public MiniTimelineResultEvent(List<MiniTimelineResult> results, String domain) { if (results != null) { this.results.addAll(results); } + this.domain = domain; } /** @@ -298,6 +301,15 @@ public MiniTimelineResultEvent(List<MiniTimelineResult> results) { public List<MiniTimelineResult> getResultList() { 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; + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java index 0ceda46203d1ae1c43c6ea3c7f23e94b53022d8e..61efc8cfbe3836e6f0c1825a74838173a5f84022 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java @@ -201,7 +201,7 @@ public List<BlackboardArtifact> getArtifacts(DomainSearchArtifactsRequest artifa * @throws DiscoveryException if unable to get the artifacts or the date * 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<>(); Map<String, List<BlackboardArtifact>> dateMap = new HashMap<>(); if (!StringUtils.isBlank(domain)) { diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchCacheLoader.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchCacheLoader.java index 9aaa375ebaf09e3cea715eea63d602fe9565c7e0..31006d7e936a5ad75faae75f2ecf30b7b84a8cbe 100755 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchCacheLoader.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchCacheLoader.java @@ -63,7 +63,7 @@ public Map<GroupKey, List<Result>> load(SearchKey key) throws DiscoveryException // Grouping by CR Frequency, for example, will require further processing // in order to make the correct decision. The attribute types that require // more information implement their logic by overriding `addAttributeToResults`. - List<AttributeType> searchAttributes = new ArrayList<>(); + Set<AttributeType> searchAttributes = new HashSet<>(); searchAttributes.add(key.getGroupAttributeType()); searchAttributes.addAll(key.getFileSortingMethod().getRequiredAttributes()); for (AttributeType attr : searchAttributes) { @@ -304,7 +304,7 @@ public void process(ResultSet resultSet) { Content dataSource = skc.getContentById(dataSourceID); resultDomains.add(new ResultDomain(domain, activityStart, - activityEnd, totalPageViews, pageViewsInLast60, filesDownloaded, + activityEnd, totalPageViews, pageViewsInLast60, filesDownloaded, countOfKnownAccountTypes, dataSource)); } } catch (SQLException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/ResultDomain.java b/Core/src/org/sleuthkit/autopsy/discovery/search/ResultDomain.java index 5da956788746de446fd1db7f7e9097faa1cbc02c..94f0da9d0a1a0a330825123add3cd4b42c72a15e 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/ResultDomain.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/ResultDomain.java @@ -20,7 +20,6 @@ import org.openide.util.NbBundle; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; /** diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/ResultsSorter.java b/Core/src/org/sleuthkit/autopsy/discovery/search/ResultsSorter.java index 2a880083bf39f5df4160c4e98d4b353fd75039e3..97de8c66919b6c730671da315d1d83d0a207bcc3 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/ResultsSorter.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/ResultsSorter.java @@ -257,60 +257,60 @@ private static Comparator<Result> getDomainNameComparator() { return compareStrings(first.getDomain().toLowerCase(), second.getDomain().toLowerCase()); }; } - + /** * Sorts domains by page view count. - * + * * This comparator sorts results in descending order (largest -> smallest). */ private static Comparator<Result> getPageViewComparator() { return (Result domain1, Result domain2) -> { - if (domain1.getType() != SearchData.Type.DOMAIN || - domain2.getType() != SearchData.Type.DOMAIN) { + if (domain1.getType() != SearchData.Type.DOMAIN + || domain2.getType() != SearchData.Type.DOMAIN) { return 0; } ResultDomain first = (ResultDomain) domain1; ResultDomain second = (ResultDomain) domain2; - + long firstPageViews = first.getTotalPageViews(); long secondPageViews = second.getTotalPageViews(); 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. */ private static Comparator<Result> getLastActivityDateTimeComparator() { return (Result domain1, Result domain2) -> { - if (domain1.getType() != SearchData.Type.DOMAIN || - domain2.getType() != SearchData.Type.DOMAIN) { + if (domain1.getType() != SearchData.Type.DOMAIN + || domain2.getType() != SearchData.Type.DOMAIN) { return 0; } ResultDomain first = (ResultDomain) domain1; ResultDomain second = (ResultDomain) domain2; - + long firstActivityEnd = first.getActivityEnd(); long secondActivityEnd = second.getActivityEnd(); 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. */ private static Comparator<Result> getWebDownloadsComparator() { return (Result domain1, Result domain2) -> { - if (domain1.getType() != SearchData.Type.DOMAIN || - domain2.getType() != SearchData.Type.DOMAIN) { + if (domain1.getType() != SearchData.Type.DOMAIN + || domain2.getType() != SearchData.Type.DOMAIN) { return 0; } ResultDomain first = (ResultDomain) domain1; ResultDomain second = (ResultDomain) domain2; - + long firstFilesDownloaded = first.getFilesDownloaded(); long secondFilesDownloaded = second.getFilesDownloaded(); return Long.compare(secondFilesDownloaded, firstFilesDownloaded); @@ -388,10 +388,10 @@ public enum SortingMethod { Bundle.FileSorter_SortingMethod_keywordlist_displayName()), // Sort alphabetically by list of keyword list names found BY_FULL_PATH(new ArrayList<>(), Bundle.FileSorter_SortingMethod_fullPath_displayName()), // Sort alphabetically by path - BY_DOMAIN_NAME(new ArrayList<>(),Bundle.FileSorter_SortingMethod_domain_displayName()), - BY_PAGE_VIEWS(new ArrayList<>(), Bundle.FileSorter_SortingMethod_pageViews_displayName()), - BY_DOWNLOADS(new ArrayList<>(), Bundle.FileSorter_SortingMethod_downloads_displayName()), - BY_LAST_ACTIVITY(new ArrayList<>(), Bundle.FileSorter_SortingMethod_activity_displayName()); + BY_DOMAIN_NAME(Arrays.asList(new DiscoveryAttributes.DomainCategoryAttribute()), Bundle.FileSorter_SortingMethod_domain_displayName()), + BY_PAGE_VIEWS(Arrays.asList(new DiscoveryAttributes.DomainCategoryAttribute()), Bundle.FileSorter_SortingMethod_pageViews_displayName()), + BY_DOWNLOADS(Arrays.asList(new DiscoveryAttributes.DomainCategoryAttribute()), Bundle.FileSorter_SortingMethod_downloads_displayName()), + BY_LAST_ACTIVITY(Arrays.asList(new DiscoveryAttributes.DomainCategoryAttribute()), Bundle.FileSorter_SortingMethod_activity_displayName()); private final String displayName; private final List<DiscoveryAttributes.AttributeType> requiredAttributes; diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/AbstractArtifactDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/AbstractArtifactDetailsPanel.java index b56e60aa8f7be43974668c9df844ccc08ce3350c..93ae57db36aa27cfbb0e48e50f443c69bcaa4409 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/AbstractArtifactDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/AbstractArtifactDetailsPanel.java @@ -20,7 +20,6 @@ import java.awt.Component; import javax.swing.JPanel; -import javax.swing.JScrollPane; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -30,14 +29,13 @@ */ public abstract class AbstractArtifactDetailsPanel extends JPanel { + private static final long serialVersionUID = 1L; + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public Component getComponent() { - // Slap a vertical scrollbar on the panel. - return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + return this; } - private static final long serialVersionUID = 1L; - /** * Called to display the contents of the given artifact. * diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.form index 3b9f9f4c7a6c429195d2ff39ec364227229e20b2..002baadab75f1a8e5d7374531cdba0456ffe5fad 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.form @@ -4,7 +4,7 @@ <Properties> <Property name="opaque" type="boolean" value="false"/> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[300, 0]"/> + <Dimension value="[350, 10]"/> </Property> </Properties> <AuxValues> @@ -22,12 +22,12 @@ <Layout> <DimensionLayout dim="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> </DimensionLayout> <DimensionLayout dim="1"> <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> </DimensionLayout> </Layout> @@ -37,9 +37,13 @@ <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> <Border info="null"/> </Property> + <Property name="horizontalScrollBarPolicy" type="int" value="31"/> <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="[350, 10]"/> + </Property> </Properties> <AuxValues> <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/> diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.java index 6cce1aeeb90fe5f5d8c346b0097abd78a32d1956..33e8b1c8f86270cab63cc03ec0fa04c2731dd95e 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.java @@ -25,6 +25,7 @@ import javax.swing.JPopupMenu; import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableCellRenderer; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import org.openide.util.NbBundle; @@ -32,6 +33,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.guiutils.SimpleTableCellRenderer; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.TimeUtilities; @@ -56,6 +58,11 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel { ArtifactsListPanel(BlackboardArtifact.ARTIFACT_TYPE artifactType) { tableModel = new DomainArtifactTableModel(artifactType); 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); } @@ -94,11 +101,15 @@ boolean selectAtPoint(Point point) { @Override BlackboardArtifact getSelectedArtifact() { - int selectedIndex = artifactsTable.getSelectionModel().getLeadSelectionIndex(); - if (selectedIndex < artifactsTable.getSelectionModel().getMinSelectionIndex() || artifactsTable.getSelectionModel().getMaxSelectionIndex() < 0 || selectedIndex > artifactsTable.getSelectionModel().getMaxSelectionIndex()) { + if (artifactsTable.getModel() instanceof DomainArtifactTableModel) { + 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 tableModel.getArtifactByRow(artifactsTable.convertRowIndexToModel(selectedIndex)); } @Override @@ -124,7 +135,12 @@ void selectFirst() { @ThreadConfined(type = ThreadConfined.ThreadType.AWT) @Override 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.repaint(); tableModel.fireTableDataChanged(); @@ -152,10 +168,12 @@ private void initComponents() { artifactsTable = new javax.swing.JTable(); setOpaque(false); - setPreferredSize(new java.awt.Dimension(300, 0)); + setPreferredSize(new java.awt.Dimension(350, 10)); jScrollPane1.setBorder(null); + jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0)); + jScrollPane1.setPreferredSize(new java.awt.Dimension(350, 10)); artifactsTable.setAutoCreateRowSorter(true); artifactsTable.setModel(tableModel); @@ -166,11 +184,11 @@ private void initComponents() { this.setLayout(layout); layout.setHorizontalGroup( 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.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 @@ -357,6 +375,46 @@ public String getColumnName(int column) { } } } + + /** + * 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 private javax.swing.JTable artifactsTable; // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsWorker.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsWorker.java index 2c919f371f0f256db349216a1384d10d643202c7..1202f20647955f03a4da241b9b55b735d0be3866 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsWorker.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsWorker.java @@ -61,7 +61,8 @@ protected List<BlackboardArtifact> doInBackground() throws Exception { return domainSearch.getArtifacts(new DomainSearchArtifactsRequest(Case.getCurrentCase().getSleuthkitCase(), domain, artifactType)); } catch (DiscoveryException ex) { 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 { throw ex; } @@ -84,6 +85,6 @@ protected void done() { //Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging } } - + } } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties index e6ce6cfda7d088c37511ef0f67f19c7f0155c7ca..5a2bc066bab9ae6bc370efea64326ed374f99c46 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties @@ -53,12 +53,11 @@ DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which ObjectDetectedFilterPanel.text=Object Detected: DateFilterPanel.mostRecentRadioButton.text=Only last: DateFilterPanel.dateFilterCheckBox.text=Date Filter: -DomainSummaryPanel.activityLabel.text= DomainSummaryPanel.pagesLabel.text= DomainSummaryPanel.filesDownloadedLabel.text= -DomainSummaryPanel.totalVisitsLabel.text= FileDetailsPanel.instancesList.border.title=Instances CookieDetailsPanel.jLabel1.text=Artifact: CookieDetailsPanel.jLabel2.text= PreviouslyNotableFilterPanel.text_1=Include only previously notable domains -KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type \ No newline at end of file +KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type +LoadingPanel.detailsLabel.AccessibleContext.accessibleName=detailsLabel diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED index 3fcce54c938ba502910ecc7d9111b25a6f3f6e56..98deeee12fda1fa445a45f3ecde8b1ab5b6f4ff2 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED @@ -3,6 +3,7 @@ ArtifactMenuMouseAdapter_label=Extract Files ArtifactsListPanel.dateColumn.name=Date/Time ArtifactsListPanel.fileNameColumn.name=Name ArtifactsListPanel.mimeTypeColumn.name=MIME Type +ArtifactsListPanel.noResultsFound.text=No results found ArtifactsListPanel.termColumn.name=Term ArtifactsListPanel.titleColumn.name=Title ArtifactsListPanel.urlColumn.name=URL @@ -52,14 +53,20 @@ DocumentPanel.numberOfImages.noImages=No images # {0} - numberOfImages DocumentPanel.numberOfImages.text=1 of {0} images DocumentWrapper.previewInitialValue=Preview not generated yet. -DomainDetailsPanel.miniTimelineTitle.text=Mini Timeline +DomainDetailsPanel.miniTimelineTitle.text=Timeline # {0} - startDate # {1} - endDate DomainSummaryPanel.activity.text=Activity: {0} to {1} +DomainSummaryPanel.category.text=Category: DomainSummaryPanel.downloads.text=Files downloaded: +DomainSummaryPanel.known.text=User role: Known account type(s) 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.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.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 @@ -68,12 +75,16 @@ ImageThumbnailPanel.isDeleted.text=All instances of file are deleted. # {0} - otherInstanceCount ImageThumbnailPanel.nameLabel.more.text=\ and {0} more 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.typeColumn.name=Result Type MiniTimelineArtifactListPanel.value.noValue=No value available. MiniTimelineDateListPanel.countColumn.name=Count MiniTimelineDateListPanel.dateColumn.name=Date MiniTimelineDateListPanel.value.noValue=No value available. +MiniTimelinePanel.loadingPanel.details=the Timeline view MonthAbbreviation.aprilAbbrev=Apr MonthAbbreviation.augustAbbrev=Aug MonthAbbreviation.decemberAbbrev=Dec @@ -156,15 +167,14 @@ DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which ObjectDetectedFilterPanel.text=Object Detected: DateFilterPanel.mostRecentRadioButton.text=Only last: DateFilterPanel.dateFilterCheckBox.text=Date Filter: -DomainSummaryPanel.activityLabel.text= DomainSummaryPanel.pagesLabel.text= DomainSummaryPanel.filesDownloadedLabel.text= -DomainSummaryPanel.totalVisitsLabel.text= FileDetailsPanel.instancesList.border.title=Instances CookieDetailsPanel.jLabel1.text=Artifact: CookieDetailsPanel.jLabel2.text= PreviouslyNotableFilterPanel.text_1=Include only previously notable domains KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type +LoadingPanel.detailsLabel.AccessibleContext.accessibleName=detailsLabel VideoThumbnailPanel.bytes.text=bytes VideoThumbnailPanel.deleted.text=All instances of file are deleted. VideoThumbnailPanel.gigaBytes.text=GB diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/ContentViewerDetailsPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/ContentViewerDetailsPanel.form index 9281965410f2d2c2753a4a93cfaaf03431a91a5b..f3718bad997cf2496a496ca5380983e6b01285fa 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/ContentViewerDetailsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/ContentViewerDetailsPanel.form @@ -3,7 +3,7 @@ <Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Properties> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[500, 0]"/> + <Dimension value="[300, 0]"/> </Property> </Properties> <AuxValues> diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/ContentViewerDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/ContentViewerDetailsPanel.java index 9667ff94d8fc90f068c1b0a7969f62e0a1b5599f..41329479183844802fb73bdd274e3373a805f454 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/ContentViewerDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/ContentViewerDetailsPanel.java @@ -50,7 +50,7 @@ final class ContentViewerDetailsPanel extends AbstractArtifactDetailsPanel { // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { - setPreferredSize(new java.awt.Dimension(500, 0)); + setPreferredSize(new java.awt.Dimension(300, 0)); setLayout(new java.awt.BorderLayout()); }// </editor-fold>//GEN-END:initComponents @@ -59,7 +59,9 @@ private void initComponents() { public void setArtifact(BlackboardArtifact artifact) { Node node = Node.EMPTY; 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); } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryTopComponent.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryTopComponent.java index 299e1285dfec9fafc86c97f801c4a33abe63c15a..a74b32ca26c07cc33a9bb016a06e66a7a44d051e 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryTopComponent.java @@ -60,9 +60,11 @@ public final class DiscoveryTopComponent extends TopComponent { private volatile static int previousDividerLocation = 250; private final GroupListPanel groupListPanel; private final ResultsPanel resultsPanel; + private JPanel detailsPanel = new JPanel(); private String selectedDomainTabName; private Type searchType; private int dividerLocation = JSplitPane.UNDEFINED_CONDITION; + private SwingAnimator animator = null; /** @@ -83,25 +85,25 @@ public BasicSplitPaneDivider createDefaultDivider() { } }); + rightSplitPane.setTopComponent(resultsPanel); + resetBottomComponent(); rightSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { @Override 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 - if ((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)) { - previousDividerLocation = (int) evt.getNewValue(); - - } + previousDividerLocation = (int) evt.getNewValue(); } } - }); - rightSplitPane.setTopComponent(resultsPanel); - rightSplitPane.setBottomComponent(new JPanel()); + } + ); + } /** @@ -131,7 +133,8 @@ private final class BasicSplitPaneDividerImpl extends BasicSplitPaneDivider { * @return The open DiscoveryTopComponent or null if it has not been opened. */ 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 void componentOpened() { DiscoveryEventUtils.getDiscoveryEventBus().register(groupListPanel); } + private void resetBottomComponent() { + rightSplitPane.setBottomComponent(new JPanel()); + rightSplitPane.setDividerLocation(JSplitPane.UNDEFINED_CONDITION); + } + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) @Override protected void componentClosed() { @@ -162,11 +170,12 @@ protected void componentClosed() { DiscoveryEventUtils.getDiscoveryEventBus().unregister(this); DiscoveryEventUtils.getDiscoveryEventBus().unregister(groupListPanel); DiscoveryEventUtils.getDiscoveryEventBus().unregister(resultsPanel); - DiscoveryEventUtils.getDiscoveryEventBus().unregister(rightSplitPane.getBottomComponent()); - if (rightSplitPane.getBottomComponent() instanceof DomainDetailsPanel) { - selectedDomainTabName = ((DomainDetailsPanel) rightSplitPane.getBottomComponent()).getSelectedTabName(); + DiscoveryEventUtils.getDiscoveryEventBus().unregister(detailsPanel); + if (detailsPanel instanceof DomainDetailsPanel) { + ((DomainDetailsPanel) detailsPanel).unregister(); + selectedDomainTabName = ((DomainDetailsPanel) detailsPanel).getSelectedTabName(); } - rightSplitPane.setDividerLocation(JSplitPane.UNDEFINED_CONDITION); + resetBottomComponent(); super.componentClosed(); } @@ -336,12 +345,13 @@ void handleSearchCompleteEvent(DiscoveryEventUtils.SearchCompleteEvent searchCom } selectedDomainTabName = validateLastSelectedType(searchCompleteEvent); DomainDetailsPanel domainDetailsPanel = new DomainDetailsPanel(); - rightSplitPane.setBottomComponent(domainDetailsPanel); domainDetailsPanel.configureArtifactTabs(selectedDomainTabName); + detailsPanel = domainDetailsPanel; } 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("; ")); progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText)); progressMessageTextArea.setCaretPosition(0); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.form index fbd55756e99d1fec5ad9a67ebb905395f193d65f..bdef0da96fcf937f738eb45cf3907e6161324232 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.form @@ -1,11 +1,21 @@ <?xml version="1.0" encoding="UTF-8" ?> <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> - <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[0, 0]"/> + <Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor"> + <Color id="Default Cursor"/> </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]"/> </Property> </Properties> @@ -23,23 +33,4 @@ </AuxValues> <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> diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.java index 3bc5ae14a78eb62356b10c4ebf367af5e42a85ed..1cc29aef1d1da50921b86e7b28d9690a61158c5a 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.java @@ -22,7 +22,6 @@ import com.google.common.eventbus.Subscribe; import java.util.logging.Level; import javax.swing.JPanel; -import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; @@ -64,7 +63,8 @@ public void valueChanged(ListSelectionEvent event) { this.artifactType = type; listPanel = new ArtifactsListPanel(artifactType); listPanel.addMouseListener(new ArtifactMenuMouseAdapter(listPanel)); - jSplitPane1.setLeftComponent(listPanel); + mainSplitPane.setLeftComponent(listPanel); + add(mainSplitPane); setRightComponent(); listPanel.addSelectionListener(listener); } @@ -91,7 +91,7 @@ private void setRightComponent() { break; } if (rightPanel != null) { - jSplitPane1.setRightComponent(rightPanel.getComponent()); + mainSplitPane.setRightComponent(rightPanel.getComponent()); } } @@ -115,10 +115,14 @@ void setStatus(ArtifactRetrievalStatus status) { this.status = status; if (status == ArtifactRetrievalStatus.UNPOPULATED) { listPanel.clearList(); - if (rightPanel != null){ + removeAll(); + add(mainSplitPane); + if (rightPanel != null) { rightPanel.setArtifact(null); } - + } else if (status == ArtifactRetrievalStatus.POPULATING) { + removeAll(); + add(new LoadingPanel(artifactType.getDisplayName())); } } @@ -130,7 +134,7 @@ void setStatus(ArtifactRetrievalStatus status) { */ @Subscribe void handleArtifactSearchResultEvent(DiscoveryEventUtils.ArtifactSearchResultEvent artifactresultEvent) { - if (artifactType == artifactresultEvent.getArtifactType()) { + if (artifactType == artifactresultEvent.getArtifactType() && status == ArtifactRetrievalStatus.POPULATING) { SwingUtilities.invokeLater(() -> { listPanel.removeSelectionListener(listener); listPanel.addArtifacts(artifactresultEvent.getListOfArtifacts()); @@ -138,6 +142,8 @@ void handleArtifactSearchResultEvent(DiscoveryEventUtils.ArtifactSearchResultEve setEnabled(!listPanel.isEmpty()); listPanel.addSelectionListener(listener); listPanel.selectFirst(); + removeAll(); + add(mainSplitPane); revalidate(); repaint(); try { @@ -169,20 +175,19 @@ BlackboardArtifact.ARTIFACT_TYPE getArtifactType() { // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN: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)); - setPreferredSize(new java.awt.Dimension(0, 0)); 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 // 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 /** diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.java index b7c372aa64990b5afc020ceb8191a80d8ceaac44..df77f939fbb6301146088b120ceba2d1f778ec77 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.java @@ -39,7 +39,6 @@ final class DomainDetailsPanel extends JPanel { private static final long serialVersionUID = 1L; private ArtifactsWorker singleArtifactDomainWorker; - private MiniTimelineWorker miniTimelineWorker; private String domain; private String selectedTabName = null; @@ -51,7 +50,9 @@ final class DomainDetailsPanel extends JPanel { @ThreadConfined(type = ThreadConfined.ThreadType.AWT) DomainDetailsPanel() { 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()) { 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. */ - @NbBundle.Messages({"DomainDetailsPanel.miniTimelineTitle.text=Mini Timeline"}) + @NbBundle.Messages({"DomainDetailsPanel.miniTimelineTitle.text=Timeline"}) @ThreadConfined(type = ThreadConfined.ThreadType.AWT) void configureArtifactTabs(String tabName) { selectedTabName = tabName; @@ -104,6 +105,20 @@ private void selectTab() { } } + /** + * 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 * populate the details area. @@ -127,14 +142,9 @@ private void runDomainWorker(DomainArtifactsTabPanel domainArtifactsTabPanel) { * mini timeline view to populate. */ private void runMiniTimelineWorker(MiniTimelinePanel miniTimelinePanel) { - if (miniTimelineWorker != null && !miniTimelineWorker.isDone()) { - miniTimelineWorker.cancel(true); - } if (miniTimelinePanel.getStatus() == DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED) { - DiscoveryEventUtils.getDiscoveryEventBus().register(miniTimelinePanel); - miniTimelinePanel.setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING); - miniTimelineWorker = new MiniTimelineWorker(domain); - miniTimelineWorker.execute(); + miniTimelinePanel.setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING, domain); + new MiniTimelineWorker(domain).execute(); } } @@ -146,20 +156,19 @@ private void runMiniTimelineWorker(MiniTimelinePanel miniTimelinePanel) { */ @Subscribe void handlePopulateDomainTabsEvent(DiscoveryEventUtils.PopulateDomainTabsEvent populateEvent) { - domain = populateEvent.getDomain(); SwingUtilities.invokeLater(() -> { - resetTabsStatus(); - selectTab(); - Component selectedComponent = jTabbedPane1.getSelectedComponent(); - if (selectedComponent instanceof DomainArtifactsTabPanel) { - runDomainWorker((DomainArtifactsTabPanel) selectedComponent); - } else if (selectedComponent instanceof MiniTimelinePanel) { - runMiniTimelineWorker((MiniTimelinePanel) selectedComponent); - } - if (StringUtils.isBlank(domain)) { + if (StringUtils.isBlank(populateEvent.getDomain())) { + resetTabsStatus(); //send fade out event DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(false)); } 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 DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(true)); } @@ -176,7 +185,7 @@ private void resetTabsStatus() { if (comp instanceof DomainArtifactsTabPanel) { ((DomainArtifactsTabPanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED); } else if (comp instanceof MiniTimelinePanel) { - ((MiniTimelinePanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED); + ((MiniTimelinePanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED, domain); } } } @@ -215,4 +224,15 @@ private void initComponents() { // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JTabbedPane jTabbedPane1; // 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); + } + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.form index 19ecc82ca2eb8403480542527e5ab0e4fd024bc1..9e7fd5fd3c570d8a933bd5d0825b0df7c2445a76 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.form @@ -24,18 +24,25 @@ <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" 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"> + <Component id="domainNameLabel" alignment="0" max="32767" attributes="0"/> <Group type="102" attributes="0"> - <Component id="domainNameLabel" pref="539" max="32767" attributes="0"/> - <EmptySpace min="-2" pref="47" max="-2" attributes="0"/> + <Group type="103" groupAlignment="1" 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> - <Component id="activityLabel" alignment="0" max="32767" attributes="0"/> - <Component id="pagesLabel" max="32767" attributes="0"/> - <Component id="filesDownloadedLabel" alignment="0" max="32767" attributes="0"/> - <Component id="totalVisitsLabel" max="32767" attributes="0"/> + <Component id="pagesLabel" alignment="0" max="32767" attributes="0"/> + <Component id="totalVisitsLabel" alignment="0" max="32767" attributes="0"/> </Group> - <EmptySpace max="-2" attributes="0"/> + <EmptySpace min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" max="-2" attributes="0"> <Component id="numberOfImagesLabel" max="32767" attributes="0"/> <Component id="sampleImageLabel" max="32767" attributes="0"/> @@ -51,20 +58,26 @@ <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" 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"/> </Group> <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"/> - <Component id="activityLabel" min="-2" max="-2" attributes="0"/> - <EmptySpace min="-2" pref="11" max="-2" attributes="0"/> - <Component id="totalVisitsLabel" min="-2" max="-2" attributes="0"/> - <EmptySpace min="-2" pref="11" max="-2" attributes="0"/> - <Component id="pagesLabel" min="-2" max="-2" attributes="0"/> - <EmptySpace type="unrelated" max="-2" attributes="0"/> - <Component id="filesDownloadedLabel" min="-2" max="-2" attributes="0"/> - <EmptySpace min="0" pref="0" max="32767" attributes="0"/> + <Group type="103" groupAlignment="0" max="-2" attributes="0"> + <Component id="filesDownloadedLabel" linkSize="4" pref="14" max="32767" attributes="0"/> + <Component id="categoryLabel" max="32767" attributes="0"/> + </Group> + <EmptySpace max="-2" attributes="0"/> + <Component id="totalVisitsLabel" linkSize="2" pref="14" max="32767" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="pagesLabel" min="-2" pref="15" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="1" max="-2" attributes="0"/> </Group> </Group> <EmptySpace max="-2" attributes="0"/> @@ -97,11 +110,6 @@ <Component class="javax.swing.JLabel" name="numberOfImagesLabel"> </Component> <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, "{key}")"/> - </Property> - </Properties> </Component> <Component class="javax.swing.JLabel" name="pagesLabel"> <Properties> @@ -118,11 +126,10 @@ </Properties> </Component> <Component class="javax.swing.JLabel" name="totalVisitsLabel"> - <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.totalVisitsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> - </Property> - </Properties> + </Component> + <Component class="javax.swing.JLabel" name="domainNotabilityLabel"> + </Component> + <Component class="javax.swing.JLabel" name="categoryLabel"> </Component> </SubComponents> </Form> diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java index ef3e4967f15c0dcdedd5e2238d22c0ebd403a3d3..bcf004b89bfb01b5a7d97c3dbcab9609b1606e88 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java @@ -32,6 +32,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.discovery.search.SearchData; /** * Class which displays a preview and details about a domain. @@ -66,6 +67,8 @@ private void initComponents() { pagesLabel = new javax.swing.JLabel(); filesDownloadedLabel = new javax.swing.JLabel(); totalVisitsLabel = new javax.swing.JLabel(); + domainNotabilityLabel = new javax.swing.JLabel(); + categoryLabel = new javax.swing.JLabel(); setBorder(javax.swing.BorderFactory.createEtchedBorder()); @@ -75,14 +78,10 @@ private void initComponents() { sampleImageLabel.setMinimumSize(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(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); this.setLayout(layout); layout.setHorizontalGroup( @@ -90,12 +89,17 @@ private void initComponents() { .addGroup(layout.createSequentialGroup() .addContainerGap() .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() - .addComponent(domainNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 539, Short.MAX_VALUE) - .addGap(47, 47, 47)) - .addComponent(activityLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(filesDownloadedLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 300, 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(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)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) @@ -103,6 +107,9 @@ private void initComponents() { .addComponent(sampleImageLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {activityLabel, filesDownloadedLabel}); + layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() @@ -110,27 +117,38 @@ private void initComponents() { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .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)) .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) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(filesDownloadedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 14, Short.MAX_VALUE) + .addComponent(categoryLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(totalVisitsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 14, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(activityLabel) - .addGap(11, 11, 11) - .addComponent(totalVisitsLabel) - .addGap(11, 11, 11) - .addComponent(pagesLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(filesDownloadedLabel) - .addGap(0, 0, Short.MAX_VALUE))) + .addComponent(pagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(1, 1, 1))) .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 // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel activityLabel; + private javax.swing.JLabel categoryLabel; private javax.swing.JLabel domainNameLabel; + private javax.swing.JLabel domainNotabilityLabel; private javax.swing.JLabel filesDownloadedLabel; private javax.swing.JLabel numberOfImagesLabel; private javax.swing.JLabel pagesLabel; @@ -145,13 +163,27 @@ private void initComponents() { "DomainSummaryPanel.pages.text=Page views in past 60 days: ", "DomainSummaryPanel.totalPages.text=Total page views: ", "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 public Component getListCellRendererComponent(JList<? extends DomainWrapper> list, DomainWrapper value, int index, boolean isSelected, boolean cellHasFocus) { domainNameLabel.setText(value.getResultDomain().getDomain()); TimeZone timeZone = ContentUtils.getTimeZone(value.getResultDomain().getDataSource()); String startDate = formatDate(value.getResultDomain().getActivityStart(), 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)); totalVisitsLabel.setText(Bundle.DomainSummaryPanel_totalPages_text() + value.getResultDomain().getTotalPageViews()); pagesLabel.setText(Bundle.DomainSummaryPanel_pages_text() + value.getResultDomain().getPageViewsInLast60Days()); @@ -166,20 +198,21 @@ public Component getListCellRendererComponent(JList<? extends DomainWrapper> lis setBackground(isSelected ? SELECTION_COLOR : list.getBackground()); return this; } - + /** * Formats an epoch time in a given time zone using the following pattern - * - * MMM dd YYYY - * - * The pattern below is formatted manually to reuse the MonthAbbreviation utility. + * + * MMM dd YYYY + * + * The pattern below is formatted manually to reuse the MonthAbbreviation + * utility. */ private String formatDate(long epochSeconds, TimeZone timeZone) { Instant epochSecondsAsInstant = Instant.ofEpochSecond(epochSeconds); ZonedDateTime dateTime = ZonedDateTime.ofInstant(epochSecondsAsInstant, timeZone.toZoneId()); MonthAbbreviation currentCutOffMonth = MonthAbbreviation.fromMonthValue(dateTime.getMonthValue()); - return String.format("%s %02d %04d", - currentCutOffMonth.toString(), + return String.format("%s %02d %04d", + currentCutOffMonth.toString(), dateTime.getDayOfMonth(), dateTime.getYear()); } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.form index 9f1e3516b9bacd597c575bbf3f8ce86bc47044ed..ee52e40121e20d2b824a690a415df5fe0688f2ca 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.form @@ -33,6 +33,7 @@ <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Connection code="domainListModel" type="code"/> </Property> + <Property name="selectionMode" type="int" value="0"/> <Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Connection code="new DomainSummaryPanel()" type="code"/> </Property> diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.java index 66125e36d0f72a663d411bc3ab63035677586d11..ef3c7d6ad6019b0cd8fe56d22f6c4f955e8cd891 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.java @@ -64,6 +64,7 @@ private void initComponents() { setLayout(new java.awt.BorderLayout()); domainList.setModel(domainListModel); + domainList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); domainList.setCellRenderer(new DomainSummaryPanel()); domainScrollPane.setViewportView(domainList); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/LoadingPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/LoadingPanel.form new file mode 100644 index 0000000000000000000000000000000000000000..649519a353ff237e8e642b81525a26f3c945fb05 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/LoadingPanel.form @@ -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, "{key}")"/> + </Property> + </AccessibilityProperties> + </Component> + </SubComponents> +</Form> diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/LoadingPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/LoadingPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..1b92fe67cf09e99d437a84bb73013eb7729fc51a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/LoadingPanel.java @@ -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 + +} diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineArtifactListPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineArtifactListPanel.java index 01f3d67183596cf6314993005b41486520627ebe..87a59edcf4c87da5bde110aa43dbed2292df437f 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineArtifactListPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineArtifactListPanel.java @@ -25,10 +25,12 @@ import javax.swing.JPopupMenu; import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableCellRenderer; import org.apache.commons.lang.StringUtils; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; +import org.sleuthkit.autopsy.guiutils.SimpleTableCellRenderer; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.Type; @@ -54,6 +56,11 @@ class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel { MiniTimelineArtifactListPanel() { tableModel = new TypeDescriptionTableModel(); 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); } @@ -138,8 +145,8 @@ private void initComponents() { artifactsTable = new javax.swing.JTable(); 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.setMinimumSize(new java.awt.Dimension(0, 0)); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineDateListPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineDateListPanel.java index 6bff7eb150f2053f1230ef6224ce25c72b38f14f..39f5f288137ea84e5372fe915bd3bebfdecf9976 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineDateListPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineDateListPanel.java @@ -23,9 +23,11 @@ import javax.swing.JPanel; import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableCellRenderer; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.discovery.search.MiniTimelineResult; +import org.sleuthkit.autopsy.guiutils.SimpleTableCellRenderer; import org.sleuthkit.datamodel.BlackboardArtifact; /** @@ -42,6 +44,11 @@ class MiniTimelineDateListPanel extends JPanel { @ThreadConfined(type = ThreadConfined.ThreadType.AWT) MiniTimelineDateListPanel() { 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); } @@ -139,8 +146,8 @@ private void initComponents() { jTable1 = new javax.swing.JTable(); 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.setMinimumSize(new java.awt.Dimension(0, 0)); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.form index 3e133fcf77dfa837acd72026e807d732b59e1600..0f0598a148904626e9b2905b65097823856be10c 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.form @@ -1,57 +1,26 @@ <?xml version="1.0" encoding="UTF-8" ?> <Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> - <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> - <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"/> - <SubComponents> + <NonVisualComponents> <Container class="javax.swing.JSplitPane" name="mainSplitPane"> <Properties> - <Property name="resizeWeight" type="double" value="0.4"/> + <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> - <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="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> - <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"> @@ -63,5 +32,24 @@ </Container> </SubComponents> </Container> - </SubComponents> + </NonVisualComponents> + <Properties> + <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> + <Dimension value="[0, 0]"/> + </Property> + </Properties> + <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> diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.java index f577622a36867a9e77ed1d5015038275067a595c..32c9e6bf6c7b85b29e50799da6b0a458387ab2d5 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.java @@ -19,12 +19,11 @@ package org.sleuthkit.autopsy.discovery.ui; import com.google.common.eventbus.Subscribe; -import java.util.logging.Level; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifactViewer; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -32,7 +31,7 @@ /** * 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; @@ -40,10 +39,11 @@ class MiniTimelinePanel extends javax.swing.JPanel { private final MiniTimelineArtifactListPanel artifactListPanel = new MiniTimelineArtifactListPanel(); private DomainArtifactsTabPanel.ArtifactRetrievalStatus status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED; 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 dateListener; + @NbBundle.Messages({"MiniTimelinePanel.loadingPanel.details=the Timeline view"}) /** * Creates new form MiniTimelinePanel. */ @@ -62,8 +62,8 @@ public void valueChanged(ListSelectionEvent event) { } else { rightPanel = new GeneralPurposeArtifactViewer(); } - rightPanel.setArtifact(artifact); mainSplitPane.setRightComponent(rightPanel.getComponent()); + rightPanel.setArtifact(artifact); validate(); repaint(); } @@ -88,6 +88,7 @@ public void valueChanged(ListSelectionEvent event) { leftSplitPane.setLeftComponent(dateListPanel); leftSplitPane.setRightComponent(artifactListPanel); mainSplitPane.setRightComponent(rightPanel.getComponent()); + add(mainSplitPane); } /** @@ -103,17 +104,24 @@ DomainArtifactsTabPanel.ArtifactRetrievalStatus getStatus() { /** * 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) - void setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus status) { + void setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus status, String domain) { this.status = status; + this.selectedDomain = domain; if (status == DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED) { artifactListPanel.clearList(); dateListPanel.clearList(); + removeAll(); + add(mainSplitPane); if (rightPanel != null) { rightPanel.setArtifact(null); } + } else if (status == DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING) { + removeAll(); + add(new LoadingPanel(Bundle.MiniTimelinePanel_loadingPanel_details())); } } @@ -126,21 +134,19 @@ void setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus status) { @Subscribe void handleMiniTimelineResultEvent(DiscoveryEventUtils.MiniTimelineResultEvent miniTimelineResultEvent) { SwingUtilities.invokeLater(() -> { - dateListPanel.removeListSelectionListener(dateListener); - artifactListPanel.removeSelectionListener(artifactListener); - dateListPanel.addArtifacts(miniTimelineResultEvent.getResultList()); - status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATED; - setEnabled(!dateListPanel.isEmpty()); - dateListPanel.addSelectionListener(dateListener); - artifactListPanel.addSelectionListener(artifactListener); - dateListPanel.selectFirst(); - revalidate(); - repaint(); - try { - DiscoveryEventUtils.getDiscoveryEventBus().unregister(this); - } catch (IllegalArgumentException notRegistered) { - logger.log(Level.INFO, "Attempting to unregister mini timeline view which was not registered"); - // attempting to remove a tab that was never registered + if (miniTimelineResultEvent.getDomain().equals(selectedDomain)) { + dateListPanel.removeListSelectionListener(dateListener); + artifactListPanel.removeSelectionListener(artifactListener); + dateListPanel.addArtifacts(miniTimelineResultEvent.getResultList()); + status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATED; + setEnabled(!dateListPanel.isEmpty()); + dateListPanel.addSelectionListener(dateListener); + artifactListPanel.addSelectionListener(artifactListener); + dateListPanel.selectFirst(); + removeAll(); + add(mainSplitPane); + revalidate(); + repaint(); } }); } @@ -157,21 +163,18 @@ private void initComponents() { mainSplitPane = new javax.swing.JSplitPane(); leftSplitPane = new javax.swing.JSplitPane(); - setMinimumSize(new java.awt.Dimension(0, 0)); - setPreferredSize(new java.awt.Dimension(0, 0)); - setLayout(new java.awt.BorderLayout()); - - mainSplitPane.setResizeWeight(0.4); + mainSplitPane.setDividerLocation(400); + mainSplitPane.setResizeWeight(0.1); mainSplitPane.setToolTipText(""); mainSplitPane.setMinimumSize(new java.awt.Dimension(0, 0)); - mainSplitPane.setPreferredSize(new java.awt.Dimension(0, 0)); + leftSplitPane.setDividerLocation(198); leftSplitPane.setResizeWeight(0.5); leftSplitPane.setMinimumSize(new java.awt.Dimension(0, 0)); - leftSplitPane.setPreferredSize(new java.awt.Dimension(300, 0)); 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 diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineWorker.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineWorker.java index 5df30ba3f62ff769aec7bba8ae333d4f78104c97..7ef49ca5d1b81383b555e552795d4944b2312cd7 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineWorker.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineWorker.java @@ -35,7 +35,7 @@ /** * 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 String domain; @@ -51,37 +51,35 @@ class MiniTimelineWorker extends SwingWorker<List<MiniTimelineResult>, Void> { } @Override - protected List<MiniTimelineResult> doInBackground() throws Exception { + protected Void doInBackground() throws Exception { List<MiniTimelineResult> results = new ArrayList<>(); if (!StringUtils.isBlank(domain)) { DomainSearch domainSearch = new DomainSearch(); try { results.addAll(domainSearch.getAllArtifactsForDomain(Case.getCurrentCase().getSleuthkitCase(), domain)); - + DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.MiniTimelineResultEvent(results, domain)); } catch (DiscoveryException ex) { 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 { throw ex; } } } - return results; + return null; } @Override protected void done() { - List<MiniTimelineResult> results = new ArrayList<>(); if (!isCancelled()) { try { - results.addAll(get()); - DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.MiniTimelineResultEvent(results)); + get(); } 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); } catch (CancellationException ignored) { //Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging } } - } } diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java index f0ec1e50d3225367ae1655bb9edf8f92a8ec61f7..223b1e9cb3fc87693c54bfb7cfc240e6f2294f3f 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java @@ -198,8 +198,13 @@ static Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> getAttribute try { List<BlackboardAttribute> attributeList = artifact.getAttributes(); for (BlackboardAttribute attribute : attributeList) { - BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID()); - attributeMap.put(type, attribute); + try{ + 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) { throw new GeoLocationDataException("Unable to get attributes from artifact", ex); diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java index 0a8ccfd7aa3a7ee3463207024008fe4c8c0f9b69..e46c707eeccbdcfa054c84235d227d729bef5bae 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java @@ -1281,7 +1281,7 @@ private CoordinationService.Lock getExclusiveDbLock() throws HealthMonitorExcept } /** - * 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. * * @return The lock diff --git a/Core/src/org/sleuthkit/autopsy/images/yara_16.png b/Core/src/org/sleuthkit/autopsy/images/yara_16.png new file mode 100644 index 0000000000000000000000000000000000000000..2ac132169106541de9d4122266882b3625bc99d3 Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/yara_16.png differ diff --git a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/ALeappAnalyzerIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/ALeappAnalyzerIngestModule.java index 705468ae15f4092c565090b966b542d6bd0374a3..1492670adad56c21028fe4913602f095fd60eb42 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/ALeappAnalyzerIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/ALeappAnalyzerIngestModule.java @@ -155,7 +155,7 @@ public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper.switchToDeterminate(aLeappFilesToProcess.size()); processALeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString()); } else { - aLeappFilesToProcess = findaLeappFilesToProcess(dataSource); + aLeappFilesToProcess = LeappFileProcessor.findLeappFilesToProcess(dataSource); statusHelper.switchToDeterminate(aLeappFilesToProcess.size()); Integer filesProcessedCount = 0; @@ -268,40 +268,7 @@ private void processALeappFs(Content dataSource, Case currentCase, DataSourceIng } - /** - * 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 diff --git a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/ILeappAnalyzerIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/ILeappAnalyzerIngestModule.java index 19cf3449423bffca245d63a4520dc6c2785cb5c0..d124801046a9f008cb596ba97ab89e0a0e38a59a 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/ILeappAnalyzerIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/ILeappAnalyzerIngestModule.java @@ -155,7 +155,7 @@ public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper.switchToDeterminate(iLeappFilesToProcess.size()); processILeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString()); } else { - iLeappFilesToProcess = findiLeappFilesToProcess(dataSource); + iLeappFilesToProcess = LeappFileProcessor.findLeappFilesToProcess(dataSource); statusHelper.switchToDeterminate(iLeappFilesToProcess.size()); Integer filesProcessedCount = 0; @@ -268,41 +268,6 @@ private void processILeappFs(Content dataSource, Case currentCase, DataSourceIng } - /** - * 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 * @param moduleOutputPath output path for xLeapp diff --git a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/LeappFileProcessor.java b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/LeappFileProcessor.java index 9f7418f8f94111c4f59c9c28125b75bc86fd04ce..3e992f0c0580a180765fa64fa8681702ed6a82a3 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/LeappFileProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/LeappFileProcessor.java @@ -35,18 +35,24 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import static java.util.Locale.US; import java.util.Map; +import java.util.Set; import java.util.logging.Level; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.io.FilenameUtils; import org.openide.util.NbBundle; 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.services.FileManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; @@ -69,6 +75,52 @@ */ 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 String MODULE_NAME = ILeappAnalyzerModuleFactory.getModuleName(); @@ -77,7 +129,7 @@ public final class LeappFileProcessor { private final Map<String, String> tsvFiles; private final Map<String, String> tsvFileArtifacts; private final Map<String, String> tsvFileArtifactComments; - private final Map<String, List<List<String>>> tsvFileAttributes; + private final Map<String, List<TsvColumn>> tsvFileAttributes; Blackboard blkBoard; @@ -104,9 +156,7 @@ public LeappFileProcessor(String xmlFile) throws IOException, IngestModuleExcept "LeappFileProcessor.Leapp.cancelled=Leapp run was canceled", "LeappFileProcessor.completed=Leapp Processing Completed", "LeappFileProcessor.error.reading.Leapp.directory=Error reading Leapp Output Directory"}) - public ProcessResult processFiles(Content dataSource, Path moduleOutputPath, AbstractFile LeappFile) { - try { List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath); processLeappFiles(LeappTsvOutputFiles, LeappFile); @@ -123,7 +173,7 @@ public ProcessResult processFileSystem(Content dataSource, Path moduleOutputPath try { List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath); 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 return ProcessResult.ERROR; } @@ -162,7 +212,7 @@ private List<String> findTsvFiles(Path LeappOutputDir) throws IngestModuleExcept * Process the Leapp files that were found that match the xml mapping file * * @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 IOException @@ -174,7 +224,7 @@ private void processLeappFiles(List<String> LeappFilesToProcess, AbstractFile Le String fileName = FilenameUtils.getName(LeappFileName); File LeappFile = new File(LeappFileName); if (tsvFileAttributes.containsKey(fileName)) { - List<List<String>> attrList = tsvFileAttributes.get(fileName); + List<TsvColumn> attrList = tsvFileAttributes.get(fileName); try { BlackboardArtifact.Type artifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(tsvFileArtifacts.get(fileName)); @@ -197,26 +247,34 @@ private void processLeappFiles(List<String> LeappFilesToProcess, AbstractFile Le * Process the Leapp files that were found that match the xml mapping file * * @param LeappFilesToProcess List of files to process - * @param dataSource The data source. + * @param dataSource The data source. * * @throws FileNotFoundException * @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<>(); for (String LeappFileName : LeappFilesToProcess) { String fileName = FilenameUtils.getName(LeappFileName); File LeappFile = new File(LeappFileName); if (tsvFileAttributes.containsKey(fileName)) { - List<List<String>> attrList = tsvFileAttributes.get(fileName); + List<TsvColumn> attrList = tsvFileAttributes.get(fileName); + BlackboardArtifact.Type artifactType = null; try { - BlackboardArtifact.Type artifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(tsvFileArtifacts.get(fileName)); + artifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(tsvFileArtifacts.get(fileName)); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error getting Blackboard Artifact Type for %s", tsvFileArtifacts.get(fileName)), ex); + } - processFile(LeappFile, attrList, fileName, artifactType, bbartifacts, dataSource); + if (artifactType == null) { + continue; + } - } catch (TskCoreException ex) { - throw new IngestModuleException(String.format("Error getting Blackboard Artifact Type for %s", tsvFileArtifacts.get(fileName)), ex); + 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 @@ private void processLeappFiles(List<String> LeappFilesToProcess, Content dataSou } - 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, 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))) { - 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. - if (line != null) { - Map<Integer, String> columnNumberToProcess = findColumnsToProcess(line, attrList); - line = reader.readLine(); + if (header != null) { + Map<Integer, String> columnNumberToProcess = findColumnsToProcess(fileName, header, attrList); + String line = reader.readLine(); while (line != null) { 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)) { BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType.getTypeID(), dataSource, bbattributes); if (bbartifact != null) { bbartifacts.add(bbartifact); } } + line = reader.readLine(); } } @@ -258,20 +324,28 @@ private void processFile(File LeappFile, List<List<String>> attrList, String fil /** * 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 fileName name of file begin processed + * @param fileName name of file begin processed * * @return */ 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; - + // 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 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); } else { columnValues = line.split("\\t"); @@ -283,15 +357,17 @@ private Collection<BlackboardAttribute> processReadLine(String line, Map<Integer Integer columnNumber = columnToProcess.getKey(); String attributeName = columnToProcess.getValue(); - try { - BlackboardAttribute.Type attributeType = Case.getCurrentCase().getSleuthkitCase().getAttributeType(attributeName.toUpperCase()); - if (attributeType == null) { - break; + if (columnValues[columnNumber] != null) { + try { + BlackboardAttribute.Type attributeType = Case.getCurrentCase().getSleuthkitCase().getAttributeType(attributeName.toUpperCase()); + 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 @@ private Collection<BlackboardAttribute> processReadLine(String line, Map<Integer } - 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) { + + 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")) { - bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValues[columnNumber])); + bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValue)); } 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")) { - 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")) { - 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")) { - 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")) { // 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); Long dateLong = Long.valueOf(0); try { - Date newDate = dateFormat.parse(columnValues[columnNumber]); + Date newDate = dateFormat.parse(columnValue); dateLong = newDate.getTime() / 1000; bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, dateLong)); } catch (ParseException ex) { // catching error and displaying date that could not be parsed // 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")) { - bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValues[columnNumber])); + bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValue)); } else { // Log this and continue on with processing logger.log(Level.WARNING, String.format("Attribute Type %s not defined.", attrType)); //NON-NLS @@ -343,29 +445,43 @@ private void checkAttributeType(Collection<BlackboardAttribute> bbattributes, St * headings to the columns in the XML mapping file so we know which columns * 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 * * @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"); HashMap<Integer, String> columnsToProcess = new HashMap<>(); Integer columnPosition = 0; for (String columnName : columnNames) { // for some reason the first column of the line has unprintable characters so removing them - String cleanColumnName = columnName.replaceAll("[^\\n\\r\\t\\p{Print}]", ""); - for (List<String> atList : attrList) { - if (atList.contains(cleanColumnName.toLowerCase())) { - columnsToProcess.put(columnPosition, atList.get(0)); + String cleanColumnName = columnName.trim().replaceAll("[^\\n\\r\\t\\p{Print}]", ""); + for (TsvColumn tsvColumn : attrList) { + if (cleanColumnName.equalsIgnoreCase(tsvColumn.getColumnName())) { + columnsToProcess.put(columnPosition, tsvColumn.getAttributeName()); break; } } 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; } @@ -424,6 +540,18 @@ private void getArtifactNode(Document xmlinput) { String comment = nnm.getNamedItem("comment").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); if (!comment.toLowerCase().matches("null")) { @@ -433,29 +561,66 @@ private void getArtifactNode(Document xmlinput) { } + 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) { NodeList attributeNlist = xmlinput.getElementsByTagName("AttributeName"); //NON-NLS for (int k = 0; k < attributeNlist.getLength(); k++) { - List<String> attributeList = new ArrayList<>(); NamedNodeMap nnm = attributeNlist.item(k).getAttributes(); String attributeName = nnm.getNamedItem("attributename").getNodeValue(); + if (!attributeName.toLowerCase().matches("null")) { String columnName = nnm.getNamedItem("columnName").getNodeValue(); String required = nnm.getNamedItem("required").getNodeValue(); String parentName = attributeNlist.item(k).getParentNode().getParentNode().getAttributes().getNamedItem("filename").getNodeValue(); - attributeList.add(attributeName.toLowerCase()); - attributeList.add(columnName.toLowerCase()); - attributeList.add(required.toLowerCase()); + BlackboardAttribute.Type foundAttrType = null; + try { + 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)) { - List<List<String>> attrList = tsvFileAttributes.get(parentName); - attrList.add(attributeList); + List<TsvColumn> attrList = tsvFileAttributes.get(parentName); + attrList.add(thisCol); tsvFileAttributes.replace(parentName, attrList); } else { - List<List<String>> attrList = new ArrayList<>(); - attrList.add(attributeList); + List<TsvColumn> attrList = new ArrayList<>(); + attrList.add(thisCol); tsvFileAttributes.put(parentName, attrList); } } @@ -466,13 +631,12 @@ private void getAttributeNodes(Document xmlinput) { /** * Generic method for creating a blackboard artifact with attributes * - * @param type is a blackboard.artifact_type enum to determine which - * type the artifact should be + * @param type is a blackboard.artifact_type enum to determine which type + * the artifact should be * @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 - * to be added to the artifact after the artifact has - * been created + * to be added to the artifact after the artifact has been created * * @return The newly-created artifact, or null on error */ @@ -490,13 +654,12 @@ private BlackboardArtifact createArtifactWithAttributes(int type, AbstractFile a /** * Generic method for creating a blackboard artifact with attributes * - * @param type is a blackboard.artifact_type enum to determine which - * type the artifact should be - * @param dataSource is the Content object that needs to have the artifact - * added for it + * @param type is a blackboard.artifact_type enum to determine which type + * the artifact should be + * @param dataSource is the Content object that needs to have the artifact + * added for it * @param bbattributes is the collection of blackboard attributes that need - * to be added to the artifact after the artifact has - * been created + * to be added to the artifact after the artifact has been created * * @return The newly-created artifact, or null on error */ @@ -515,7 +678,7 @@ private BlackboardArtifact createArtifactWithAttributes(int type, Content dataSo * Method to post a list of BlackboardArtifacts to the blackboard. * * @param artifacts A list of artifacts. IF list is empty or null, the - * function will return. + * function will return. */ void postArtifacts(Collection<BlackboardArtifact> artifacts) { if (artifacts == null || artifacts.isEmpty()) { @@ -535,7 +698,44 @@ void postArtifacts(Collection<BlackboardArtifact> artifacts) { * @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException */ 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; + } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/aleap-artifact-attribute-reference.xml b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/aleap-artifact-attribute-reference.xml index 6a573e0abbd0e01c8b0dce274542623f23d19a9d..784ccefa18eb82501c0b5129b950678fa20e347c 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/aleap-artifact-attribute-reference.xml +++ b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/aleap-artifact-attribute-reference.xml @@ -31,18 +31,18 @@ <FileName filename="accounts ce 0.tsv" description="Accounts_ce"> <ArtifactName artifactname="TSK_SERVICE_ACCOUNT" comment="accounts ce 0"> <AttributeName attributename="TSK_USER_ID" columnName="Name" required="yes" /> - <AttributeName attributename="TSK_PROG_NAME" columnName=" Type" required="yes" /> - <AttributeName attributename="TSK_PASSWORD" columnName=" Password" required="yes" /> + <AttributeName attributename="TSK_PROG_NAME" columnName="Type" required="yes" /> + <AttributeName attributename="TSK_PASSWORD" columnName="Password" required="yes" /> </ArtifactName> </FileName> <FileName filename="authtokens 0.tsv" description="Authtokens"> <ArtifactName artifactname="TSK_SERVICE_ACCOUNT" comment="Authtokens"> <AttributeName attributename="null" columnName="ID" required="no" /> - <AttributeName attributename="TSK_USER_ID" columnName=" Name" required="yes" /> - <AttributeName attributename="TSK_PROG_NAME" columnName=" Account Type" required="yes" /> + <AttributeName attributename="TSK_USER_ID" columnName="Name" required="yes" /> + <AttributeName attributename="TSK_PROG_NAME" columnName="Account Type" required="yes" /> <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> </FileName> @@ -56,17 +56,17 @@ <FileName filename="Browser Bookmarks.tsv" description="Browser Bookmarks"> <ArtifactName artifactname="TSK_WEB_BOOKMARK" comment="Browser 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" /> + <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="Browser cookies.tsv" description="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_NAME" columnName="Name" required="yes" /> <AttributeName attributename="TSK_VALUE" columnName="Value" required="yes" /> @@ -108,11 +108,11 @@ <ArtifactName artifactname="TSK_WEB_HISTORY" comment="Browser 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" /> + <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> @@ -153,17 +153,17 @@ <FileName filename="Chrome Bookmarks.tsv" description="Chrome 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" /> + <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="Chrome cookies.tsv" description="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_NAME" columnName="Name" required="yes" /> <AttributeName attributename="TSK_VALUE" columnName="Value" required="yes" /> @@ -197,11 +197,11 @@ <ArtifactName artifactname="TSK_WEB_HISTORY" comment="Chrome 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" /> + <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> @@ -224,6 +224,79 @@ </ArtifactName> </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"> <ArtifactName artifactname="TSK_WEB_SEARCH_QUERY" comment="Google Play Search"> <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Timestamp" required="yes" /> @@ -233,10 +306,11 @@ </FileName> <FileName filename="google quick search box.tsv" description="Google quick search box"> - <ArtifactName artifactname="TSK_WEB_SEARCH_QUERY" comment="Google Quick Search Search"> - <AttributeName attributename="TSK_DATETIME" columnName="File Timestamp" required="yes" /> + <ArtifactName artifactname="TSK_WEB_SEARCH_QUERY" comment="Google Quick Search"> + <AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="File Timestamp" required="yes" /> <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" /> </ArtifactName> </FileName> @@ -256,12 +330,12 @@ </FileName> <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_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=" Auto Update?" required="no" /> + <AttributeName attributename="null" columnName="Auto Update?" required="no" /> </ArtifactName> </FileName> diff --git a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/ileap-artifact-attribute-reference.xml b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/ileap-artifact-attribute-reference.xml index a4169395aae21cce3b9cb1360d4bca0d14a11200..c0c1b43985fafc2a59bcc86f048006c352066138 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/ileap-artifact-attribute-reference.xml +++ b/Core/src/org/sleuthkit/autopsy/modules/leappanalyzers/ileap-artifact-attribute-reference.xml @@ -42,8 +42,8 @@ <FileName filename="Application State.tsv" description="Application State"> <ArtifactName artifactname="TSK_INSTALLED_PROG" comment="Application State"> <AttributeName attributename="TSK_PROG_NAME" columnName="Bundle ID" required="no" /> - <AttributeName attributename="TSK_INSTALLED_PATH" columnName="Bundle Path" required="yes" /> - <AttributeName attributename="TSK_INSTALLED_SOURCE" columnName="Sandbox Path" required="yes" /> + <AttributeName attributename="TSK_PATH" columnName="Bundle Path" required="yes" /> + <AttributeName attributename="TSK_PATH_SOURCE" columnName="Sandbox Path" required="yes" /> </ArtifactName> </FileName> @@ -84,7 +84,7 @@ <AttributeName attributename="TSK_DATETIME_END" columnName="End Date" required="yes" /> <AttributeName attributename="null" columnName="End Timezone" 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="Last Modified" required="no" /> </ArtifactName> @@ -113,7 +113,7 @@ <AttributeName attributename="null" columnName="Process Name" required="no" /> <AttributeName attributename="null" columnName="WIFI In" 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="null" columnName="Table ID" required="no" /> </ArtifactName> @@ -143,13 +143,13 @@ <AttributeName attributename="null" columnName="Start" required="no" /> <AttributeName attributename="null" columnName="End" required="no" /> <AttributeName attributename="null" columnName="ZSTREAMNAME" required="no" /> - <AttributeName attributename="TSK_PROG_NAME" columnName=" ZVALUESTRING" required="no" /> - <AttributeName attributename="null" columnName=" Activity Type" required="no" /> - <AttributeName attributename="null" columnName=" Title" required="no" /> - <AttributeName attributename="null" columnName=" Expiration Date" required="no" /> - <AttributeName attributename="null" columnName=" Content URL" required="no" /> - <AttributeName attributename="null" columnName=" Calendar Date" required="no" /> - <AttributeName attributename="null" columnName=" Calendar End Date" required="no" /> + <AttributeName attributename="TSK_PROG_NAME" columnName="ZVALUESTRING" required="no" /> + <AttributeName attributename="null" columnName="Activity Type" required="no" /> + <AttributeName attributename="null" columnName="Title" required="no" /> + <AttributeName attributename="null" columnName="Expiration Date" required="no" /> + <AttributeName attributename="null" columnName="Content URL" required="no" /> + <AttributeName attributename="null" columnName="Calendar Date" required="no" /> + <AttributeName attributename="null" columnName="Calendar End Date" required="no" /> </ArtifactName> </FileName> @@ -160,7 +160,7 @@ <AttributeName attributename="null" columnName="Bundle ID" required="no" /> <AttributeName attributename="TSK_CALENDAR_ENTRY_TYPE" columnName="Activity Type" 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 End Date" required="no" /> <AttributeName attributename="TSK_LOCATION" columnName="Source ID" required="yes" /> @@ -209,7 +209,7 @@ <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Device Backlit"> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" 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 Minutes" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" /> @@ -226,12 +226,12 @@ <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Battery Level"> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" 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="Day of the Week" required="no" /> <AttributeName attributename="null" columnName="GMT Offset" 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> </FileName> @@ -255,7 +255,7 @@ <ArtifactName artifactname="TSK_DEVICE_INFO" comment="KnowledgeC Car Play Connections"> <AttributeName attributename="TSK_DATETIME" columnName="Start" required="yes" /> <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 Minutes" 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_END" columnName="End" 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 Minutes" 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"> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" 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 Minutes" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" /> @@ -301,7 +301,7 @@ <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Inferred Motion"> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" 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 Minutes" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" /> @@ -344,12 +344,12 @@ <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Device Locked"> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" 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="Day of the Week" required="no" /> <AttributeName attributename="null" columnName="GMT Offset" 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> </FileName> @@ -362,7 +362,7 @@ <AttributeName attributename="null" columnName="Now Playing Artists" required="no" /> <AttributeName attributename="null" columnName="Playing Genre" required="no" /> <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 Minutes" 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="Entry Creation" 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" /> </ArtifactName> </FileName> @@ -397,7 +397,7 @@ <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Screen Orientation"> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" 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 Minutes" required="no" /> <AttributeName attributename="null" columnName="Day of Week" required="no" /> @@ -412,14 +412,14 @@ <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Plugged In"> <AttributeName attributename="TSK_DATETIME_START" columnName="Start" 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="Day of the Week" required="no" /> <AttributeName attributename="null" columnName="GMT Offset" required="no" /> <AttributeName attributename="null" columnName="Start" required="no" /> <AttributeName attributename="null" columnName="End" 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> </FileName> @@ -528,8 +528,8 @@ <AttributeName attributename="null" columnName="Location Date" required="no" /> <AttributeName attributename="null" columnName="Coordinates" required="no" /> <AttributeName attributename="null" columnName="Vehicle Identifier" required="no" /> - <AttributeName attributename="null" columnName=" Location Identifier" required="no" /> - <AttributeName attributename="null" columnName=" Identifier" required="no" /> + <AttributeName attributename="null" columnName="Location Identifier" required="no" /> + <AttributeName attributename="null" columnName="Identifier" required="no" /> <AttributeName attributename="null" columnName="Location Quality" required="no" /> <AttributeName attributename="null" columnName="User Set 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="null" columnName="Comment" required="no" /> <AttributeName attributename="TSK_SUBJECT" columnName="Subject" required="yes" /> - <AttributeName attributename="TSK_EMAIL_CONTENT_PLAIN" columnName=" Summary" required="yes" /> - <AttributeName attributename="TSK_READ_STATUS" columnName=" Read?" required="yes" /> - <AttributeName attributename="TSK_FLAG" columnName=" Flagged?" required="yes" /> - <AttributeName attributename="TSK_ISDELETED" columnName=" Deleted" required="yes" /> - <AttributeName attributename="null" columnName=" Mailbox" required="no" /> + <AttributeName attributename="TSK_EMAIL_CONTENT_PLAIN" columnName="Summary" required="yes" /> + <AttributeName attributename="TSK_READ_STATUS" columnName="Read?" required="yes" /> + <AttributeName attributename="TSK_FLAG" columnName="Flagged?" required="yes" /> + <AttributeName attributename="TSK_ISDELETED" columnName="Deleted" required="yes" /> + <AttributeName attributename="null" columnName="Mailbox" required="no" /> </ArtifactName> </FileName> --> @@ -596,10 +596,10 @@ <FileName filename="Notifications.tsv" description="iOS Notificatons"> <ArtifactName artifactname="TSK_PROG_NOTIFICATIONS" comment="iOS Notificatons"> <AttributeName attributename="TSK_DATETIME" columnName="Creation Time" required="yes" /> - <AttributeName attributename="TSK_PROG_NAME" columnName=" Bundle" required="yes" /> - <AttributeName attributename="TSK_TITLE" columnName=" Title[Subtitle]" required="yes" /> - <AttributeName attributename="TSK_VALUE" columnName=" Message" required="yes" /> - <AttributeName attributename="null" columnName=" Other Details" required="no" /> + <AttributeName attributename="TSK_PROG_NAME" columnName="Bundle" required="yes" /> + <AttributeName attributename="TSK_TITLE" columnName="Title[Subtitle]" required="yes" /> + <AttributeName attributename="TSK_VALUE" columnName="Message" required="yes" /> + <AttributeName attributename="null" columnName="Other Details" required="no" /> </ArtifactName> </FileName> @@ -651,7 +651,7 @@ <FileName filename="Powerlog Lightning Connector.tsv" description="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_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="Offset Timestamp" required="no" /> <AttributeName attributename="null" columnName="Table ID" required="no" /> @@ -680,7 +680,7 @@ <ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Torch"> <AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" /> <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="Offset Timestamp" 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="null" columnName="App Executable Name" required="no" /> <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="null" columnName="App TYpe" required="no" /> <AttributeName attributename="null" columnName="App Deleted Date" required="no" /> diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestHelper.java b/Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestHelper.java index e8f57657f98361d3738a5b18f1ea1fb432bc7231..e13f41fe7b68d95ceb58f666d345b8c2602b3e67 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestHelper.java +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestHelper.java @@ -109,7 +109,10 @@ static List<BlackboardArtifact> scanFileForMatches(AbstractFile file, File baseR } /** - * + * 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 baseRuleSetDirectory Base directory of the compiled rule sets. * @param localFile Local copy of file. @@ -138,8 +141,8 @@ static List<BlackboardArtifact> scanFileForMatches(AbstractFile file, File baseR * Scan the given file byte array for rule matches using the YaraJNIWrapper * API. * - * @param fileBytes - * @param ruleSetDirectory + * @param fileBytes An array of the file data. + * @param ruleSetDirectory Base directory of the compiled rule sets. * * @return List of rules that match from the given file from the given rule * set. Empty list is returned if no matches where found. @@ -158,6 +161,17 @@ private static List<String> scanFileForMatches(byte[] fileBytes, int fileSize, F 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 { List<String> matchingRules = new ArrayList<>(); @@ -228,7 +242,7 @@ static private void compileRuleSet(RuleSet set, Path outputDir, File yarac) thro ProcessBuilder builder = new ProcessBuilder(commandList); try { 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)); } } catch (SecurityException | IOException ex) { @@ -249,7 +263,7 @@ static private void compileRuleSet(RuleSet set, Path outputDir, File yarac) thro private static List<RuleSet> getRuleSetsForNames(List<String> names) { List<RuleSet> ruleSetList = new ArrayList<>(); - RuleSetManager manager = new RuleSetManager(); + RuleSetManager manager = RuleSetManager.getInstance(); for (RuleSet set : manager.getRuleSetList()) { if (names.contains(set.getName())) { ruleSetList.add(set); diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestModuleFactory.java index 2330f5f448691c2a9ac278b63c32b83bcf6c6128..8924c9bace91ca24a90917b312244ce9bbd9ea2c 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestModuleFactory.java @@ -25,8 +25,10 @@ import org.sleuthkit.autopsy.ingest.FileIngestModule; import org.sleuthkit.autopsy.ingest.IngestModuleFactory; import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; +import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; +import org.sleuthkit.autopsy.modules.yara.ui.YaraGlobalSettingsPanel; import org.sleuthkit.autopsy.modules.yara.ui.YaraIngestSettingsPanel; /** @@ -63,7 +65,7 @@ public boolean hasIngestJobSettingsPanel() { @Override public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) { - return new YaraIngestSettingsPanel((YaraIngestJobSettings)settings); + return new YaraIngestSettingsPanel((YaraIngestJobSettings) settings); } @Override @@ -89,4 +91,16 @@ public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings set static String getModuleName() { return Bundle.Yara_Module_Name(); } + + @Override + public boolean hasGlobalSettingsPanel() { + return true; + } + + @Override + public IngestModuleGlobalSettingsPanel getGlobalSettingsPanel() { + YaraGlobalSettingsPanel globalOptionsPanel = new YaraGlobalSettingsPanel(); + globalOptionsPanel.load(); + return globalOptionsPanel; + } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/rules/RuleSet.java b/Core/src/org/sleuthkit/autopsy/modules/yara/rules/RuleSet.java index 112cf9206c66938aeaf79fecbab10cd4efa808ce..3ab248f12fcc0bb97ee35dd3ba5beb49f91a2df1 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/yara/rules/RuleSet.java +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/rules/RuleSet.java @@ -21,6 +21,7 @@ import java.io.File; import java.io.Serializable; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -69,7 +70,15 @@ public Path getPath() { * @return List of Files in current directory. */ 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 diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/rules/RuleSetManager.java b/Core/src/org/sleuthkit/autopsy/modules/yara/rules/RuleSetManager.java index a40d69a07b77e6064d2140e2919bcc255090ce7f..ae0e3f661705e177b19586739294c0feb67a582e 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/yara/rules/RuleSetManager.java +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/rules/RuleSetManager.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2020 Basis Technology Corp. + * Copyright 2020 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,11 +18,14 @@ */ package org.sleuthkit.autopsy.modules.yara.rules; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import javax.swing.SwingUtilities; import org.sleuthkit.autopsy.coreutils.PlatformUtil; /** @@ -34,6 +37,54 @@ public class RuleSetManager { private final static String BASE_FOLDER = "yara"; 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. * @@ -43,12 +94,11 @@ public class RuleSetManager { * * @throws RuleSetException RuleSet with given name already exists. */ - public RuleSet createRuleSet(String name) throws RuleSetException { - - if(name == null || name.isEmpty()) { - throw new RuleSetException("YARA rule set name cannot be null or empty string" ); + public synchronized RuleSet createRuleSet(String name) throws RuleSetException { + if (name == null || name.isEmpty()) { + throw new RuleSetException("YARA rule set name cannot be null or empty string"); } - + if (isRuleSetExists(name)) { throw new RuleSetException(String.format("Yara rule set with name %s already exits.", name)); } @@ -58,7 +108,42 @@ public RuleSet createRuleSet(String name) throws RuleSetException { 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 RuleSet createRuleSet(String name) throws RuleSetException { * * @return */ - public List<RuleSet> getRuleSetList() { + public synchronized List<RuleSet> getRuleSetList() { List<RuleSet> ruleSets = new ArrayList<>(); Path basePath = getRuleSetPath(); @@ -86,7 +171,7 @@ public List<RuleSet> getRuleSetList() { * * @return True if the rule set exist. */ - public boolean isRuleSetExists(String name) { + public synchronized boolean isRuleSetExists(String name) { Path basePath = getRuleSetPath(); Path setPath = Paths.get(basePath.toString(), name); @@ -110,4 +195,30 @@ private Path getRuleSetPath() { 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(); + } + } diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/Bundle.properties-MERGED index 6b8d6f9b7b17523d0caed234373cfe367247c43a..0a00a19a58d9a4d4f8a9d6a25495398693f94bb4 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/Bundle.properties-MERGED @@ -10,6 +10,7 @@ RuleSetDetailsPanel.setDetailsLabel.text=Set Details 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. 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.allFilesButton.text=All Files 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_new_rule_set_name_msg=Supply a new unique 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 diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/RuleSetDetailsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/RuleSetDetailsPanel.form index 955199108aa62cccb98ada7012b86fec0f9e7d10..7501a732243d0d28d4b492b58999018b33cb5800 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/RuleSetDetailsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/RuleSetDetailsPanel.form @@ -99,10 +99,6 @@ <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="refreshButtonActionPerformed"/> </Events> - <AuxValues> - <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/> - <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/> - </AuxValues> <Constraints> <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"/> diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/RuleSetDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/RuleSetDetailsPanel.java index 7e3e506948d0d3795ab5bb70d2cf88a443098fc5..ef6a347921962b5fd3ad9e8feca839bc6302f5dd 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/RuleSetDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/RuleSetDetailsPanel.java @@ -20,7 +20,6 @@ import java.awt.Component; import java.awt.Desktop; -import java.awt.Graphics; import java.io.File; import java.io.IOException; import java.util.List; @@ -58,7 +57,7 @@ public RuleSetDetailsPanel() { fileList.setCellRenderer(new FileRenderer()); openFolderButton.setEnabled(false); scrollPane.setViewportView(fileList); - + refreshButton.setEnabled(false); } /** @@ -82,6 +81,7 @@ void setRuleSet(RuleSet ruleSet) { } openFolderButton.setEnabled(ruleSet != null); + refreshButton.setEnabled(ruleSet != null); } /** @@ -120,7 +120,7 @@ private void initComponents() { openFolderButton = new javax.swing.JButton(); openLabel = new javax.swing.JLabel(); scrollPane = new javax.swing.JScrollPane(); - javax.swing.JButton refreshButton = new javax.swing.JButton(); + refreshButton = new javax.swing.JButton(); setLayout(new java.awt.GridBagLayout()); @@ -223,6 +223,7 @@ private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton openFolderButton; private javax.swing.JLabel openLabel; + private javax.swing.JButton refreshButton; private javax.swing.JScrollPane scrollPane; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraGlobalSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraGlobalSettingsPanel.form new file mode 100755 index 0000000000000000000000000000000000000000..2c7924e2a4178535a69140e08538958b1ff20ca0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraGlobalSettingsPanel.form @@ -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> diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraGlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraGlobalSettingsPanel.java new file mode 100755 index 0000000000000000000000000000000000000000..6257a91b58e2d6cb71438f3a80457c5cfabf5b6d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraGlobalSettingsPanel.java @@ -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 +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraIngestSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraIngestSettingsPanel.java index e68b11ccb232896ea73d1aefa7c62de611e47cb1..9261d779b1529964bab86ee4aa35247231fe61f4 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraIngestSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraIngestSettingsPanel.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.modules.yara.ui; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; @@ -49,24 +51,42 @@ public class YaraIngestSettingsPanel extends IngestModuleIngestJobSettingsPanel checkboxList = new CheckBoxJList<>(); scrollPane.setViewportView(checkboxList); } - + + /** + * Constructs a new panel with the given JobSetting objects. + * + * @param settings Ingest job settings. + */ public YaraIngestSettingsPanel(YaraIngestJobSettings settings) { this(); - + List<String> setNames = settings.getSelectedRuleSetNames(); - + checkboxList.setModel(listModel); checkboxList.setOpaque(false); - RuleSetManager manager = new RuleSetManager(); - List<RuleSet> ruleSetList = manager.getRuleSetList(); + List<RuleSet> ruleSetList = RuleSetManager.getInstance().getRuleSetList(); for (RuleSet set : ruleSetList) { RuleSetListItem item = new RuleSetListItem(set); item.setChecked(setNames.contains(set.getName())); listModel.addElement(item); } - + allFilesButton.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 @@ -84,6 +104,36 @@ public IngestModuleIngestJobSettings getSettings() { 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. */ diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraRuleSetOptionPanel.form b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraRuleSetOptionPanel.form index c7b05ede35d6c0c031aad6d3cfd9793b24952d26..41be11b2d734b49ddec264ffc585b07f26d285ab 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraRuleSetOptionPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraRuleSetOptionPanel.form @@ -42,6 +42,14 @@ <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <SubComponents> <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> <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/> <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/> diff --git a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraRuleSetOptionPanel.java b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraRuleSetOptionPanel.java index 71d4749cae0e82a52df3015f099e637556e85998..885ca9b8243ef2bcc33134450d32134f9083566b 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraRuleSetOptionPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/yara/ui/YaraRuleSetOptionPanel.java @@ -20,13 +20,12 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.io.File; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JOptionPane; import javax.swing.event.ListSelectionEvent; 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.RuleSetException; 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 final RuleSetManager manager; - /** * Creates new form YaraRuleSetOptionPanel */ public YaraRuleSetOptionPanel() { initComponents(); - manager = new RuleSetManager(); - ruleSetPanel.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { @@ -77,25 +72,41 @@ public void actionPerformed(ActionEvent e) { * Update the panel with the current rule set. */ 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 * selected rule. */ 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_title=Rule Set Name", "# {0} - rule set name", "YaraRuleSetOptionPanel_badName_msg=Rule set name {0} already exists.\nRule set names must be unique.", "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, * create the new set and update the rule set list. @@ -105,16 +116,21 @@ private void handleNewRuleSet() { Bundle.YaraRuleSetOptionPanel_new_rule_set_name_msg(), 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, Bundle.YaraRuleSetOptionPanel_badName2_msg(), Bundle.YaraRuleSetOptionPanel_badName_title(), JOptionPane.ERROR_MESSAGE); return; } - + try { - ruleSetPanel.addRuleSet(manager.createRuleSet(value)); + ruleSetPanel.addRuleSet(RuleSetManager.getInstance().createRuleSet(value)); } catch (RuleSetException ex) { JOptionPane.showMessageDialog(this, Bundle.YaraRuleSetOptionPanel_badName_msg(value), @@ -124,31 +140,29 @@ private void handleNewRuleSet() { } } + @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 * rule set list. */ private void handleDeleteRuleSet() { RuleSet ruleSet = ruleSetPanel.getSelectedRule(); - ruleSetPanel.removeRuleSet(ruleSet); - deleteDirectory(ruleSet.getPath().toFile()); - } - - /** - * 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); + if (ruleSet != null) { + try { + RuleSetManager.getInstance().deleteRuleSet(ruleSet); + } catch (RuleSetException ex) { + JOptionPane.showMessageDialog(this, + Bundle.YaraRuleSetOptionPanel_rule_set_delete(ruleSet.getName()), + Bundle.YaraRuleSetOptionPanel_badName_title(), + JOptionPane.ERROR_MESSAGE); + logger.log(Level.WARNING, String.format("Failed to delete YARA rule set %s", ruleSet.getName()), ex); } + ruleSetPanel.removeRuleSet(ruleSet); } - return directoryToBeDeleted.delete(); } /** @@ -172,6 +186,8 @@ private void initComponents() { 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()); separator.setOrientation(javax.swing.SwingConstants.VERTICAL); diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/TableReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/TableReportGenerator.java index 0e2d9dc9769abb312c136e0f2416bb501499408c..ca6722911e3cbd9dd10fe53c48d6bb8512d660be 100644 --- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/TableReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/TableReportGenerator.java @@ -1348,7 +1348,7 @@ private List<Column> getArtifactTableColumns(int artifactTypeId, Set<BlackboardA new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH))); 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)); } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_INSTALLED_PROG.getTypeID() == artifactTypeId) { diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/html/HTMLReport.java b/Core/src/org/sleuthkit/autopsy/report/modules/html/HTMLReport.java index c99939377de0ba6815a2904e95f9cf3ba6538ab6..d1008f110b3a9da0d8d470bff8006106bd4193ba 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/html/HTMLReport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/html/HTMLReport.java @@ -390,6 +390,9 @@ private String useDataTypeIcon(String dataType) { case TSK_WEB_CATEGORIZATION: in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/domain-16.png"); //NON-NLS break; + case TSK_YARA_HIT: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/yara_16.png"); //NON-NLS + break; default: logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/threadutils/TaskRetryUtil.java b/Core/src/org/sleuthkit/autopsy/threadutils/TaskRetryUtil.java index 764208e628e1c4087a086b0d53bcacb1d2e6135e..e00cedab83fa88394d2da5d01b4aff0d407de6bf 100755 --- a/Core/src/org/sleuthkit/autopsy/threadutils/TaskRetryUtil.java +++ b/Core/src/org/sleuthkit/autopsy/threadutils/TaskRetryUtil.java @@ -56,11 +56,6 @@ public static class TaskAttempt { * Constructs an object that encapsulates the specification of a task * attempt for the attemptTask() utility. The attempt will have neither * 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() { this.delay = 0L; @@ -168,7 +163,7 @@ public interface Terminator { * each attempt and an optional timeout for each attempt. If an attempt * 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 attempts The defining details for each attempt of the task. * @param executor The scheduled task executor to be used to attempt the diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/DomainCategorizer.java b/Core/src/org/sleuthkit/autopsy/url/analytics/DomainCategorizer.java index 513fd3bb0841d5a3c25308d011d1672210deccc3..4bce3c58d71c8147af7748dbba541ad1fea9bf9d 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/DomainCategorizer.java +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/DomainCategorizer.java @@ -26,7 +26,7 @@ * and should have a class annotation of '(at)ServiceProvider(service = * 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 * since Exception can encompass the InterruptedException. See the following * github issue and bugs for more information: diff --git a/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummaryTest.java b/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummaryTest.java index ff73c428b9689ee23e1d167f9551dab72cc2789e..0acda3954a070591980afff1259a8242c30f2d16 100644 --- a/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummaryTest.java +++ b/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummaryTest.java @@ -256,7 +256,7 @@ private BlackboardArtifact getArtifact(DataSource ds, long artifactId, ARTIFACT_ */ private BlackboardArtifact getRecentDocumentArtifact(DataSource ds, long artifactId, Long dateTime, String path) { 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) )); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java index feeea64957f8cd0221cce0489d2c39baa9dfbae2..24a42041dd8e3e45a589ba3a08306dcc79d7d39c 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java @@ -284,7 +284,11 @@ private void indexChunk(String chunk, String lowerCasedChunk, String sourceName, //Make a SolrInputDocument out of the field map SolrInputDocument updateDoc = new SolrInputDocument(); 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 { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LanguageSpecificContentIndexingHelper.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LanguageSpecificContentIndexingHelper.java index 38fcfd429e2cf7e8d911a1e2c044ede395f6e16c..387399d7ae955991181020e250cf33b7e59677e9 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LanguageSpecificContentIndexingHelper.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LanguageSpecificContentIndexingHelper.java @@ -62,8 +62,12 @@ void indexMiniChunk(Chunker.Chunk chunk, String sourceName, Map<String, Object> //Make a SolrInputDocument out of the field map SolrInputDocument updateDoc = new SolrInputDocument(); 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 { updateDoc.setField(Server.Schema.ID.toString(), Chunker.sanitize(MiniChunkHelper.getChunkIdString(baseChunkID)).toString()); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 2645e9f60ad0771e5e8cfef1226b4e1d8528a24a..b6b6590f4ec9ef55af44c29f11f57bf91165fc0b 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -1013,16 +1013,34 @@ void addDocument(SolrInputDocument doc) throws KeywordSearchModuleException, NoO "# {0} - colelction name", "Server.deleteCore.exception.msg=Failed to delete Solr colelction {0}",}) void deleteCollection(String coreName, CaseMetadata metadata) throws KeywordSearchServiceException, KeywordSearchModuleException { try { - IndexingServerProperties properties = getMultiUserServerProperties(metadata.getCaseDirectory()); - HttpSolrClient 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 + HttpSolrClient solrServer; + if (metadata.getCaseType() == CaseType.SINGLE_USER_CASE) { + solrServer = getSolrClient("http://localhost:" + localSolrServerPort + "/solr"); //NON-NLS + CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, solrServer); + if (null != response.getCoreStatus(coreName).get("instanceDir")) { //NON-NLS + /* + * Send a core unload request to the Solr server, with the + * 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 { - 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) { // We will get a RemoteSolrException with cause == null and detailsMessage diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java index df44b2347ae2fe63c96c47888044d33aedfa0002..920ac1aaba44f0b9de4601a2be917f03ebe13d39 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java @@ -238,8 +238,11 @@ public void deleteTextIndex(CaseMetadata metadata) throws KeywordSearchServiceEx } catch (KeywordSearchModuleException ex) { throw new KeywordSearchServiceException(Bundle.SolrSearchService_exceptionMessage_unableToDeleteCollection(index.getIndexName()), ex); } - if (!FileUtil.deleteDir(new File(index.getIndexPath()).getParentFile())) { - throw new KeywordSearchServiceException(Bundle.SolrSearchService_exceptionMessage_failedToDeleteIndexFiles(index.getIndexPath())); + File indexDir = new File(index.getIndexPath()).getParentFile(); + if (indexDir.exists()) { + if (!FileUtil.deleteDir(indexDir)) { + throw new KeywordSearchServiceException(Bundle.SolrSearchService_exceptionMessage_failedToDeleteIndexFiles(index.getIndexPath())); + } } } } diff --git a/NEWS.txt b/NEWS.txt index 7f37faa9c8a612c8f5bd0d4de2ff11e243aa5cac..b51c0948de73002f503c1088c41d860defab2c0d 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -1,36 +1,76 @@ +---------------- VERSION 4.18.0 -------------- +Keyword Search: +- A major upgrade from Solr 4 to Solr 8.6.3. Single user cases continue to use the embedded server. +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: +- New YARA ingest module to flag files based on regular expression patterns. +- New “Android Analyzer (aLEAPP)” module based on aLEAPP. Previous “Android Analyzer” also still exists. +- Updated “iOS Analyzer (iLEAPP)” module to create more artifacts and work on disk images. +- Hash Database module will calculate SHA-256 hash in addition to MD5. +- Removed Interesting Item rule that flagged existence of Bitlocker (since it ships with Windows). +- Fixed a major bug in the PhotoRec module that could result in an incorrect file layout if the carved file spanned non-contiguous sectors. +- Fixed MBOX detection bug in Email module. + +Reporting: +- Attachments from tagged messages are now included in a Portable Case. + +Misc: +- Added support for Ext4 inline data and sparse blocks (via TSK fix). +- Updated PostgreSQL JDBC driver to support any recent version of PostgreSQL for multi-user cases and PostgreSQL Central Repository. +- Added personas to the summary viewer in CVT. +- Handling of bad characters in auto ingest manifest files. +- Assorted small bug fixes. + + ---------------- 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. +- 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. +- 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. +- 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. +- 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. +- 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 -------------- diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DefaultDomainCategorizer.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DefaultDomainCategorizer.java index 058b639091ae7705aceb32b5adaf34cf17e0789a..0d64661f6c0cd544e1a7ee5431bb8bdc44edaff2 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DefaultDomainCategorizer.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DefaultDomainCategorizer.java @@ -45,7 +45,7 @@ * messaging: * 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 * since Exception can encompass the InterruptedException. See the following * github issue and bugs for more information: diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index bdcd9fbe309bee9adc126a058482def1bbe7e0d6..38552dfd2e6e6114b7344fe687ad4881efa231f9 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -68,7 +68,6 @@ import java.util.HashSet; import static java.util.Locale.US; import static java.util.TimeZone.getTimeZone; -import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -98,7 +97,6 @@ import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; import org.sleuthkit.datamodel.Report; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskDataException; /** * Extract windows registry data using regripper. Runs two versions of @@ -539,7 +537,7 @@ private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFil //sometimes etime will be an empty string and therefore can not be parsed into a date if (etime != null && !etime.isEmpty()) { 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(); mtime = Long.valueOf(Tempdate) / MS_IN_SEC; } catch (ParseException ex) { @@ -604,7 +602,7 @@ private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFil case "InstallDate": //NON-NLS if (value != null && !value.isEmpty()) { 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(); installtime = Long.valueOf(Tempdate) / MS_IN_SEC; } catch (ParseException e) { @@ -783,7 +781,7 @@ private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFil try { String mTimeAttr = artnode.getAttribute("mtime"); 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; } } catch (ParseException ex) { @@ -866,6 +864,8 @@ private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFil parentModuleName, sid)); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, parentModuleName, homeDir)); + + newArtifacts.add(bbart); } else { //add attributes to existing artifact BlackboardAttribute bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_NAME)); @@ -878,10 +878,10 @@ private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFil if (bbattr == null) { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, parentModuleName, homeDir)); - } + } } bbart.addAttributes(bbattributes); - newArtifacts.add(bbart); + } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding account artifact to blackboard.", ex); //NON-NLS } @@ -1163,7 +1163,7 @@ private boolean parseSamPluginOutput(String regFilePath, AbstractFile regAbstrac Collection<BlackboardAttribute> getAttributesForAccount(Map<String, String> userInfo, List<String> groupList, boolean existingUser, AbstractFile regAbstractFile) throws ParseException { 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")); if (!existingUser) { @@ -1501,7 +1501,7 @@ private void parseAdobeMRUList(AbstractFile regFile, BufferedReader reader, Stri } Collection<BlackboardAttribute> attributes = new ArrayList<>(); 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)); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); if (bba != null) { @@ -1742,7 +1742,7 @@ private void parseOfficeDocs2010MRUList(AbstractFile regFile, BufferedReader rea String fileName = fileNameTokens[1]; Collection<BlackboardAttribute> attributes = new ArrayList<>(); 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)); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); if (bba != null) { @@ -1805,7 +1805,7 @@ private void parseOfficeTrustRecords(AbstractFile regFile, BufferedReader reader } Collection<BlackboardAttribute> attributes = new ArrayList<>(); 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)); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); if (bba != null) { diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java index 64619518fdf7980352ed4e9145fcf3f249de7aca..367c68e3c4628cea558ef4f251459276d161585e 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java @@ -294,6 +294,7 @@ private final static class ZoneIdentifierInfo { private static final String REFERRER_URL = "ReferrerUrl"; //NON-NLS private static final String HOST_URL = "HostUrl"; //NON-NLS private static final String FAMILY_NAME = "LastWriterPackageFamilyName"; //NON-NLS + private static String fileName; private final Properties properties = new Properties(null); @@ -307,6 +308,7 @@ private final static class ZoneIdentifierInfo { * @throws IOException */ ZoneIdentifierInfo(AbstractFile zoneFile) throws IOException { + fileName = zoneFile.getName(); properties.load(new ReadContentInputStream(zoneFile)); } @@ -318,8 +320,13 @@ private final static class ZoneIdentifierInfo { private int getZoneId() { int zoneValue = -1; String value = properties.getProperty(ZONE_ID); - if (value != null) { - zoneValue = Integer.parseInt(value); + try { + 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; diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RecentDocumentsByLnk.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RecentDocumentsByLnk.java index 2b93720fcd4f29bf0a59f9fcb2bb64f030cdc8c1..7b65b0c8246c105af7e90bfe89d0c1731a033f3e 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RecentDocumentsByLnk.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RecentDocumentsByLnk.java @@ -119,7 +119,7 @@ private void getRecentDocuments() { NbBundle.getMessage(this.getClass(), "RecentDocumentsByLnk.parentModuleName.noSpace"), 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(), "RecentDocumentsByLnk.parentModuleName.noSpace"), recentFile.getCrtime())); diff --git a/docs/doxygen-dev/footer.html b/docs/doxygen-dev/footer.html index b874c74742c907e06213c3399f7b659f128a8f63..abc63ecc265a26009eccf19a3f10026296edd21b 100755 --- a/docs/doxygen-dev/footer.html +++ b/docs/doxygen-dev/footer.html @@ -1,5 +1,5 @@ <hr/> -<p><i>Copyright © 2012-2020 Basis Technology. Generated on $date<br/> +<p><i>Copyright © 2012-2021 Basis Technology. Generated on $date<br/> 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>. </i></p> diff --git a/docs/doxygen-user/aleapp.dox b/docs/doxygen-user/aleapp.dox index d62a6edc6dbd305922fbb14739a0d64bc1d67d4a..220b07de5177ed3818ee24c7f92e54c48fa585a9 100644 --- a/docs/doxygen-user/aleapp.dox +++ b/docs/doxygen-user/aleapp.dox @@ -8,7 +8,7 @@ The Android Analyzer ingest module runs aLEAPP (https://github.com/abrignoni/aLE \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 diff --git a/docs/doxygen-user/communications.dox b/docs/doxygen-user/communications.dox index fb2031f4b44434b23ed137b51c5c6623ce028216..929661fe196a26b8549a3e7739f44f4028cb120c 100644 --- a/docs/doxygen-user/communications.dox +++ b/docs/doxygen-user/communications.dox @@ -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. <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 diff --git a/docs/doxygen-user/data_source_summary.dox b/docs/doxygen-user/data_source_summary.dox index f656e5a6ab05f5ea37e77af12e6409147a89e5da..459dd7f6115ac6a5fc24caac3e8279ffa39f27fd 100644 --- a/docs/doxygen-user/data_source_summary.dox +++ b/docs/doxygen-user/data_source_summary.dox @@ -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 -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 @@ -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 -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 @@ -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. +\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 The Ingest History tab shows which ingest modules have been run on the data source and the version of each module. diff --git a/docs/doxygen-user/drone.dox b/docs/doxygen-user/drone.dox index 978e199917f6aa2f1cdc9f35178ae2545207c1e7..baf2366a72b9ddd5f7b3d7f51091064487c5e4f4 100755 --- a/docs/doxygen-user/drone.dox +++ b/docs/doxygen-user/drone.dox @@ -1,13 +1,13 @@ -/*! \page drone_page Drone Analyzer +/*! \page drone_page DJI Drone Analyzer [TOC] \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 4 - 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 -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 diff --git a/docs/doxygen-user/footer.html b/docs/doxygen-user/footer.html index b874c74742c907e06213c3399f7b659f128a8f63..abc63ecc265a26009eccf19a3f10026296edd21b 100644 --- a/docs/doxygen-user/footer.html +++ b/docs/doxygen-user/footer.html @@ -1,5 +1,5 @@ <hr/> -<p><i>Copyright © 2012-2020 Basis Technology. Generated on $date<br/> +<p><i>Copyright © 2012-2021 Basis Technology. Generated on $date<br/> 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>. </i></p> diff --git a/docs/doxygen-user/hashdb_lookup.dox b/docs/doxygen-user/hashdb_lookup.dox index c32e4adc740819e5c4758d854ee7c0d0704a15ed..29fc6f459d545076a4cee31ace3b1eb6e17fdc0c 100644 --- a/docs/doxygen-user/hashdb_lookup.dox +++ b/docs/doxygen-user/hashdb_lookup.dox @@ -6,7 +6,7 @@ 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 diff --git a/docs/doxygen-user/ileapp.dox b/docs/doxygen-user/ileapp.dox index 21a314e0ac6f17eb33326128e338aa5f6a9fe2b1..5abae340d9b3c586914a9ef0494d55f533b74004 100644 --- a/docs/doxygen-user/ileapp.dox +++ b/docs/doxygen-user/ileapp.dox @@ -8,7 +8,7 @@ The iOS Analyzer ingest module runs iLEAPP (https://github.com/abrignoni/iLEAPP) \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 diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_analysis.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_analysis.png index 15957a0712a8a9b411e12967237ee61a621c63a7..a1e6bfb84202909dc2eb30aa299a864358560b06 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_analysis.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_analysis.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_container.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_container.png index 3adf7ec40dda72de4e1886adfeeff0ddec6328fa..3d51efee735083a743421cc5a37c9760da7a9bbf 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_container.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_container.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_geo.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_geo.png new file mode 100644 index 0000000000000000000000000000000000000000..4ab4440b55035c7033fd0a8db7addef28203a03a Binary files /dev/null and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_geo.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_ingest.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_ingest.png index 203e9c44ee7574c56d650c1c9dc61345c1fc8289..d3a1a970a9fe7bf04ab7a947bd7a0fc7ea7a0958 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_ingest.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_ingest.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_past_cases.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_past_cases.png index 591cb6686716b0b6a2c744d1db794ca71644275a..3b47578b0e7b81c25fb6d62c7c803ce354e76f45 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_past_cases.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_past_cases.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_recent_files.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_recent_files.png index af63e103f7ae9f6c6804cbef82b33dff43970f61..3eeaeffd8958165c772410d4f42b858b1b71e146 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_recent_files.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_recent_files.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_result_viewer.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_result_viewer.png index ad16f2e031cc6c7401162fec9246d2927e36b45e..8f2b4d3f47e15807aca580de9a396c9f52e6aee2 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_result_viewer.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_result_viewer.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_timeline.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_timeline.png new file mode 100644 index 0000000000000000000000000000000000000000..1a376723d6e0e54d43584d2bf5c991c53e1a2fdf Binary files /dev/null and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_timeline.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_types.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_types.png index db3f4a14f9b1ee22569dcb6d6a98c4ea22540ebf..2bf93d3f90ac0c60d148fe66e07b20fedefcbb45 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_types.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_types.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_user_activity.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_user_activity.png index 2ce9dcaf16d1d6e23b0b20b0efc4ccfd157a93ec..7b6d4d09f55258e998cd7c917beb24ba30493b06 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_user_activity.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_user_activity.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_window.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_window.png index 745c3b1d970768b51f647cd794d1319be71b1aa7..a9d61e908459e534131ddc52a093040ec640eacd 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_window.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_window.png differ diff --git a/docs/doxygen-user/images/aleapp_main.jpg b/docs/doxygen-user/images/aleapp_main.jpg index 82d8d2c7783fd47b586ee0af6b7bcc723af39df2..d318748a6d0e6b2d9432d13238205e38a16c7bce 100644 Binary files a/docs/doxygen-user/images/aleapp_main.jpg and b/docs/doxygen-user/images/aleapp_main.jpg differ diff --git a/docs/doxygen-user/images/cvt_summary_tab.png b/docs/doxygen-user/images/cvt_summary_tab.png index 53f4ee148cca54027283bfd26717ae050ab5c048..405448d31e6c984c8bd6d2ea49b1c1bc11d10ebd 100644 Binary files a/docs/doxygen-user/images/cvt_summary_tab.png and b/docs/doxygen-user/images/cvt_summary_tab.png differ diff --git a/docs/doxygen-user/images/yara_ingest_settings.png b/docs/doxygen-user/images/yara_ingest_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..0917a73bd01cb11abfcf98e17eb6afaa5330f883 Binary files /dev/null and b/docs/doxygen-user/images/yara_ingest_settings.png differ diff --git a/docs/doxygen-user/images/yara_new_rule_set.png b/docs/doxygen-user/images/yara_new_rule_set.png new file mode 100644 index 0000000000000000000000000000000000000000..1470f8f45c1682828f942a34a2932ab7f333dd0f Binary files /dev/null and b/docs/doxygen-user/images/yara_new_rule_set.png differ diff --git a/docs/doxygen-user/images/yara_options.png b/docs/doxygen-user/images/yara_options.png new file mode 100644 index 0000000000000000000000000000000000000000..e04a1e62f4dd76a507e4b4ac359bbb7b3d1a472c Binary files /dev/null and b/docs/doxygen-user/images/yara_options.png differ diff --git a/docs/doxygen-user/images/yara_results.png b/docs/doxygen-user/images/yara_results.png new file mode 100644 index 0000000000000000000000000000000000000000..5e1d447935a74cd15684cf5575677db4f4824283 Binary files /dev/null and b/docs/doxygen-user/images/yara_results.png differ diff --git a/docs/doxygen-user/main.dox b/docs/doxygen-user/main.dox index c9649aeef6226d0301d95e444a3770cd011fb618..b8778cd36b167de799af599c260bdbf29333d8de 100644 --- a/docs/doxygen-user/main.dox +++ b/docs/doxygen-user/main.dox @@ -51,6 +51,8 @@ The following topics are available here: - \subpage drone_page - \subpage gpx_page - \subpage ileapp_page + - \subpage aleapp_page + - \subpage yara_page - Reviewing the Results - \subpage uilayout_page diff --git a/docs/doxygen-user/yara.dox b/docs/doxygen-user/yara.dox new file mode 100644 index 0000000000000000000000000000000000000000..19bba86384659800dd15d8fa99f09c364a606199 --- /dev/null +++ b/docs/doxygen-user/yara.dox @@ -0,0 +1,50 @@ +/*! \page yara_page YARA Analyzer + +[TOC] + + +\section yara_overview Overview + +The YARA Analyzer module uses a set of rules to search files for textual or binary patterns. YARA was designed for malware analysis but can be used to search for any type of files. For more information on YARA see <a href="https://virustotal.github.io/yara/">https://virustotal.github.io/yara/</a>. + +\section yara_config Configuration + +To create and edit your rule sets, go to "Tools", "Options" and then select the "YARA" tab. + +\image html yara_options.png + +YARA rule sets are stored in folders in the user's Autopsy folder. To create a new rule set, click the "New Set" button in the lower left and enter the name for your new set. + +\image html yara_new_rule_set.png + +With your new rule set selected, click the "Open Folder" button to go to the newly created rules folder. You can now copy existing YARA files into this folder to include them in the rule set. Information on writing YARA rules can be found <a href="https://yara.readthedocs.io/en/stable/writingrules.html">here</a> and many existing YARA rules can be found through a web search. As a very simple example, we will add this rule to the sample rule set to find files that contain the words "hello" and "world": + +\verbatim +rule HelloWorldRule +{ + strings: + $part1 = "hello" nocase + $part2 = "world" nocase + + condition: + $part1 and $part2 +} +\endverbatim + +Once you've added your rules to the folder, click the "Refresh File List" button to show them in the options panel. + +\section yara_running Running the Module + +To enable the YARA Analyzer ingest module select the checkbox in the \ref ingest_configure "Ingest Modules configuration screen". + +\image html yara_ingest_settings.png + +Make sure all rule sets you want to run are checked. You can also choose between running on all files or only running on executable files. + +\section yara_results Viewing Results + +Results are show in the Results tree under "Extracted Content". + +\image html yara_results.png + +*/ diff --git a/docs/doxygen/footer.html b/docs/doxygen/footer.html index f703eb2a5e0fd1bca235a9eeb042cf4f884d1339..65eaf1d7dd2a712557c155e4b1a6a8ad8c33ba02 100644 --- a/docs/doxygen/footer.html +++ b/docs/doxygen/footer.html @@ -1,5 +1,5 @@ <hr/> -<p><i>Copyright © 2012-2020 Basis Technology. Generated on: $date<br/> +<p><i>Copyright © 2012-2021 Basis Technology. Generated on: $date<br/> 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>. </i></p> diff --git a/test/script/tskdbdiff.py b/test/script/tskdbdiff.py index e202c3e111a490cb7bb3006a062d8f95fd1b4a10..c6c6dfacdcec77817c1a16022536680bf5281d23 100644 --- a/test/script/tskdbdiff.py +++ b/test/script/tskdbdiff.py @@ -627,10 +627,24 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info fields_list[4] = files_table[object_id] if legacy_artifact_id != 'NULL' and legacy_artifact_id in artifact_table.keys(): fields_list[6] = artifact_table[legacy_artifact_id] + + + if fields_list[1] == fields_list[2] and fields_list[1] == fields_list[3]: + fields_list[1] = cleanupEventDescription(fields_list[1]) + fields_list[2] = cleanupEventDescription(fields_list[2]) + fields_list[3] = cleanupEventDescription(fields_list[3]) + newLine = ('INSERT INTO "tsk_event_descriptions" VALUES(' + ','.join(fields_list[1:]) + ');') # remove report_id return newLine else: return line + +def cleanupEventDescription(description): + test = re.search("^'\D+:\d+'$", description) + if test is not None: + return re.sub(":\d+", ":<artifact_id>", description) + else: + return description def getAssociatedArtifactType(cur, artifact_id, isMultiUser): if isMultiUser: diff --git a/thirdparty/aLeapp/aleapp.exe b/thirdparty/aLeapp/aleapp.exe index 52fab109eafdacc4d4566b192699a6235b862a7a..179d7b633157d711a31c4cc5b67ad05fb85b8415 100644 Binary files a/thirdparty/aLeapp/aleapp.exe and b/thirdparty/aLeapp/aleapp.exe differ