diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form index 2a656008c2..1aba67e76c 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form @@ -66,7 +66,7 @@ - + @@ -95,7 +95,7 @@ - + @@ -464,7 +464,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 5ba6b9b89a..76c2f189ad 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -53,6 +53,7 @@ import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.ingest.IngestManager; +import static org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent.COMPLETED; import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.Account; @@ -63,6 +64,7 @@ import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter; import org.sleuthkit.datamodel.CommunicationsFilter.MostRecentFilter; +import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.DataSource; import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG; import static org.sleuthkit.datamodel.Relationship.Type.CONTACT; @@ -96,9 +98,10 @@ final public class FiltersPanel extends JPanel { * Listens to ingest events to enable refresh button */ private final PropertyChangeListener ingestListener; + private final PropertyChangeListener ingestJobListener; /** - * Flag that indicates the UI is not up-sto-date with respect to the case DB + * Flag that indicates the UI is not up-to-date with respect to the case DB * and it should be refreshed (by reapplying the filters). */ private boolean needsRefresh; @@ -123,6 +126,11 @@ final public class FiltersPanel extends JPanel { @NbBundle.Messages({"refreshText=Refresh Results", "applyText=Apply"}) public FiltersPanel() { initComponents(); + + CheckBoxIconPanel panel = createAccoutTypeCheckBoxPanel(Account.Type.DEVICE, true); + accountTypeMap.put(Account.Type.DEVICE, panel.getCheckBox()); + accountTypeListPane.add(panel); + deviceRequiredLabel.setVisible(false); accountTypeRequiredLabel.setVisible(false); startDatePicker.setDate(LocalDate.now().minusWeeks(3)); @@ -155,18 +163,30 @@ final public class FiltersPanel extends JPanel { // Indicate that a refresh may be needed, unless the data added is Keyword or Hashset hits ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue(); if (null != eventData - && eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() - && eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { - updateFilters(false); + && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() + || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID() + || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() + || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID())) + { + updateFilters(true); + needsRefresh = true; + validateFilters(); + } + } + }; + + this.ingestJobListener = pce -> { + String eventType = pce.getPropertyName(); + if (eventType.equals(COMPLETED.toString()) && + updateFilters(true)) { + needsRefresh = true; validateFilters(); - } } }; applyFiltersButton.addActionListener(e -> applyFilters()); refreshButton.addActionListener(e -> applyFilters()); - } /** @@ -219,19 +239,26 @@ final public class FiltersPanel extends JPanel { /** * Updates the filter widgets to reflect he data sources/types in the case. */ - private void updateFilters(boolean initialState) { - updateAccountTypeFilter(); - updateDeviceFilter(initialState); + private boolean updateFilters(boolean initialState) { + boolean newAccountType = updateAccountTypeFilter(initialState); + boolean newDeviceFilter = updateDeviceFilter(initialState); + + // both or either are true, return true; + return newAccountType || newDeviceFilter; } @Override public void addNotify() { super.addNotify(); IngestManager.getInstance().addIngestModuleEventListener(ingestListener); + IngestManager.getInstance().addIngestJobEventListener(ingestJobListener); Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { //clear the device filter widget when the case changes. devicesMap.clear(); devicesListPane.removeAll(); + + accountTypeMap.clear(); + accountTypeListPane.removeAll(); }); } @@ -239,64 +266,107 @@ final public class FiltersPanel extends JPanel { public void removeNotify() { super.removeNotify(); IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); + IngestManager.getInstance().removeIngestJobEventListener(ingestJobListener); } /** * Populate the Account Types filter widgets + * + * @param selected the initial value for the account type checkbox + * + * @return True, if a new accountType was found */ - private void updateAccountTypeFilter() { - - //TODO: something like this commented code could be used to show only - //the account types that are found: - //final CommunicationsManager communicationsManager = Case.getCurrentOpenCase().getSleuthkitCase().getCommunicationsManager(); - //List accountTypesInUse = communicationsManager.getAccountTypesInUse(); - //accountTypesInUSe.forEach(...) - Account.Type.PREDEFINED_ACCOUNT_TYPES.forEach(type -> { - if (type.equals(Account.Type.CREDIT_CARD)) { - //don't show a check box for credit cards - } else { - accountTypeMap.computeIfAbsent(type, t -> { - - CheckBoxIconPanel panel = new CheckBoxIconPanel( - type.getDisplayName(), - new ImageIcon(FiltersPanel.class.getResource(Utils.getIconFilePath(type)))); - panel.setSelected(true); - panel.addItemListener(validationListener); + private boolean updateAccountTypeFilter(boolean selected) { + boolean newOneFound = false; + try { + final CommunicationsManager communicationsManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager(); + List accountTypesInUse = communicationsManager.getAccountTypesInUse(); + + for (Account.Type type : accountTypesInUse) { + + if (!accountTypeMap.containsKey(type) && !type.equals(Account.Type.CREDIT_CARD)) { + CheckBoxIconPanel panel = createAccoutTypeCheckBoxPanel(type, selected); + accountTypeMap.put(type, panel.getCheckBox()); accountTypeListPane.add(panel); - if (t.equals(Account.Type.DEVICE)) { - //Deveice type filter is enabled based on whether we are in table or graph view. - panel.setEnabled(deviceAccountTypeEnabled); - } - return panel.getCheckBox(); - }); + + newOneFound = true; + } } - }); + + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to update to update Account Types Filter", ex); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "A case is required to update the account types filter.", ex); + } + + if (newOneFound) { + accountTypeListPane.revalidate(); + } + + return newOneFound; + } + + /** + * Helper function to create a new instance of the CheckBoxIconPanel base on + * the Account.Type and initalState (check box state). + * + * @param type Account.Type to display on the panel + * @param initalState initial check box state + * + * @return instance of the CheckBoxIconPanel + */ + private CheckBoxIconPanel createAccoutTypeCheckBoxPanel(Account.Type type, boolean initalState) { + CheckBoxIconPanel panel = new CheckBoxIconPanel( + type.getDisplayName(), + new ImageIcon(FiltersPanel.class.getResource(Utils.getIconFilePath(type)))); + + panel.setSelected(initalState); + panel.addItemListener(validationListener); + if (type.equals(Account.Type.DEVICE)) { + //Deveice type filter is enabled based on whether we are in table or graph view. + panel.setEnabled(deviceAccountTypeEnabled); + } + + return panel; } /** * Populate the devices filter widgets * - * @param initialState + * @param selected Sets the initial state of device check box + * + * @return true if a new device was found */ - private void updateDeviceFilter(boolean initialState) { + private boolean updateDeviceFilter(boolean selected) { + boolean newOneFound = false; try { final SleuthkitCase sleuthkitCase = Case.getCurrentCaseThrows().getSleuthkitCase(); for (DataSource dataSource : sleuthkitCase.getDataSources()) { String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName(); - //store the device id in the map, but display a datasource name in the UI. - devicesMap.computeIfAbsent(dataSource.getDeviceId(), ds -> { - final JCheckBox jCheckBox = new JCheckBox(dsName, initialState); - jCheckBox.addItemListener(validationListener); - devicesListPane.add(jCheckBox); - return jCheckBox; - }); + if(devicesMap.containsKey(dataSource.getDeviceId())) { + continue; + } + + final JCheckBox jCheckBox = new JCheckBox(dsName, selected); + jCheckBox.addItemListener(validationListener); + devicesListPane.add(jCheckBox); + devicesMap.put(dataSource.getDeviceId(), jCheckBox); + + newOneFound = true; + } } catch (NoCurrentCaseException ex) { logger.log(Level.INFO, "Filter update cancelled. Case is closed."); } catch (TskCoreException tskCoreException) { logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", tskCoreException); } + + if(newOneFound) { + devicesListPane.revalidate(); + } + + return newOneFound; } /** @@ -319,7 +389,7 @@ final public class FiltersPanel extends JPanel { } /** - * Sets the state of the device filter checkboxes + * Sets the state of the device filter check boxes * * @param deviceFilter Selected devices */ @@ -366,6 +436,12 @@ final public class FiltersPanel extends JPanel { endDatePicker.setEnabled(state.isEnabled()); } + /** + * Sets the state of the most recent UI controls based on the current values + * in MostRecentFilter. + * + * @param filter The MostRecentFilter state to be set + */ private void setMostRecentFilter(MostRecentFilter filter) { int limit = filter.getLimit(); if(limit > 0) { @@ -424,6 +500,7 @@ final public class FiltersPanel extends JPanel { gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 0; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 5); topPane.add(applyFiltersButton, gridBagConstraints); needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N @@ -697,7 +774,7 @@ final public class FiltersPanel extends JPanel { accountTypesScrollPane.setPreferredSize(new java.awt.Dimension(2, 200)); - accountTypeListPane.setLayout(new javax.swing.BoxLayout(accountTypeListPane, javax.swing.BoxLayout.Y_AXIS)); + accountTypeListPane.setLayout(new javax.swing.BoxLayout(accountTypeListPane, javax.swing.BoxLayout.PAGE_AXIS)); accountTypesScrollPane.setViewportView(accountTypeListPane); gridBagConstraints = new java.awt.GridBagConstraints(); @@ -740,6 +817,7 @@ final public class FiltersPanel extends JPanel { gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0); add(scrollPane, gridBagConstraints); }// //GEN-END:initComponents @@ -808,6 +886,11 @@ final public class FiltersPanel extends JPanel { endCheckBox.isSelected() ? endDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0); } + /** + * Get a MostRecentFilter that based on the current state of the ui controls. + * + * @return A new instance of MostRecentFilter + */ private MostRecentFilter getMostRecentFilter() { String value = (String)limitComboBox.getSelectedItem(); if(value.trim().equalsIgnoreCase("all")){ diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java index 4cd6e330e0..a75bca2972 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java @@ -1845,7 +1845,8 @@ final public class Accounts implements AutopsyVisitableItem { return ICON_BASE_PATH + "WhatsApp.png"; } else { //there could be a default icon instead... - throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName()); + return ICON_BASE_PATH + "face.png"; +// throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName()); } } } diff --git a/InternalPythonModules/android/wwfmessage.py b/InternalPythonModules/android/wwfmessage.py index f2f0e213ec..faf62bb4e2 100644 --- a/InternalPythonModules/android/wwfmessage.py +++ b/InternalPythonModules/android/wwfmessage.py @@ -41,6 +41,8 @@ from org.sleuthkit.datamodel import Content from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel import Relationship +from org.sleuthkit.autopsy.ingest import IngestServices +from org.sleuthkit.autopsy.ingest import ModuleDataEvent import traceback import general @@ -78,7 +80,8 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer): def __findWWFMessagesInDB(self, databasePath, abstractFile, dataSource): if not databasePath: return - + + bbartifacts = list() try: Class.forName("org.sqlite.JDBC"); # load JDBC driver connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath) @@ -115,6 +118,7 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer): attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MSG_ID, general.MODULE_NAME, game_id)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, message)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Words With Friends Message")) + attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID, general.MODULE_NAME, user_id)) artifact.addAttributes(attributes) @@ -124,6 +128,7 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer): # create relationship between accounts Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [wwfAccountInstance], artifact,Relationship.Type.MESSAGE, created_at); + bbartifacts.append(artifact) try: # index the artifact for keyword search blackboard = Case.getCurrentCase().getServices().getBlackboard() @@ -140,6 +145,10 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer): self._logger.log(Level.SEVERE, "Error parsing WWF messages to the blackboard", ex) self._logger.log(Level.SEVERE, traceback.format_exc()) finally: + if bbartifacts: + + IngestServices.getInstance().fireModuleDataEvent(ModuleDataEvent(general.MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE, bbartifacts)) + try: if resultSet is not None: resultSet.close()