From 0415a4eef100be98a05ff10e87faf9e94a6ea894 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 11 Mar 2021 14:33:35 -0500 Subject: [PATCH] fixes and changes --- .../uiutils/JTablePanel.java | 11 +- .../AddEditCategoryDialog.form | 10 +- .../AddEditCategoryDialog.java | 35 ++-- .../domaincategorization/Bundle.properties | 2 +- .../Bundle.properties-MERGED | 4 +- .../CustomWebCategorizer.java | 4 +- .../WebCategoriesOptionsPanel.form | 15 ++ .../WebCategoriesOptionsPanel.java | 64 +++--- .../CustomDomainCategorizer.java | 188 ------------------ .../recentactivity/DomainCategoryRunner.java | 14 +- 10 files changed, 104 insertions(+), 243 deletions(-) delete mode 100644 RecentActivity/src/org/sleuthkit/autopsy/recentactivity/CustomDomainCategorizer.java diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java index 007dbc0456..be814d23a9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java @@ -353,15 +353,16 @@ public class JTablePanel extends AbstractLoadableComponent> { } /** - * Returns the selected item or null if no item is selected. - * @return The selected item or null if no item is selected. + * Returns the selected items or null if no item is selected. + * @return The selected items or null if no item is selected. */ - public T getSelectedItem() { + public List getSelectedItems() { int selectedRow = this.table.getSelectedRow(); - if (selectedRow < 0 || this.tableModel == null || selectedRow >= this.tableModel.getDataRows().size()) { + int count = this.table.getSelectedRowCount(); + if (selectedRow < 0 || this.tableModel == null || selectedRow + count > this.tableModel.getDataRows().size()) { return null; } else { - return this.tableModel.getDataRows().get(selectedRow); + return this.tableModel.getDataRows().subList(selectedRow, selectedRow + count); } } diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/AddEditCategoryDialog.form b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/AddEditCategoryDialog.form index 08f9a45535..a863b48212 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/AddEditCategoryDialog.form +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/AddEditCategoryDialog.form @@ -63,13 +63,13 @@ - + - - - + + + - + diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/AddEditCategoryDialog.java b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/AddEditCategoryDialog.java index 7f1fdff3bf..5a74e0d089 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/AddEditCategoryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/AddEditCategoryDialog.java @@ -54,6 +54,7 @@ class AddEditCategoryDialog extends javax.swing.JDialog { onValueUpdate(domainSuffixTextField.getText(), categoryTextField.getText()); } }; + /** * Main constructor if adding a new domain suffix. * @@ -77,8 +78,6 @@ class AddEditCategoryDialog extends javax.swing.JDialog { initComponents(); this.currentSuffixes = currentSuffixes; this.currentDomainCategory = currentDomainCategory; - - // set title based on whether or not we are editing or adding // also don't allow editing of domain suffix if editing @@ -93,9 +92,9 @@ class AddEditCategoryDialog extends javax.swing.JDialog { domainSuffixTextField.setEnabled(false); onValueUpdate(currentDomainCategory.getHostSuffix(), currentDomainCategory.getCategory()); } - + validationLabel.setText(""); - + categoryTextField.getDocument().addDocumentListener(updateListener); domainSuffixTextField.getDocument().addDocumentListener(updateListener); } @@ -103,6 +102,7 @@ class AddEditCategoryDialog extends javax.swing.JDialog { /** * Returns the string value for the name in the input field if Ok pressed or * null if not. + * * @return The string value for the name in the input field if Ok pressed or * null if not. */ @@ -112,6 +112,7 @@ class AddEditCategoryDialog extends javax.swing.JDialog { /** * Returns whether or not the value has been changed and saved by the user. + * * @return Whether or not the value has been changed and saved by the user. */ boolean isChanged() { @@ -126,7 +127,7 @@ class AddEditCategoryDialog extends javax.swing.JDialog { */ @Messages({ "# {0} - maxSuffixLen", - "AddEditCategoryDialog_onValueUpdate_badSuffix=Please provide a domain suffix that is no more than {0} characters.", + "AddEditCategoryDialog_onValueUpdate_badSuffix=Please provide a domain suffix that is no more than {0} characters that includes at least one period.", "# {0} - maxCategoryLen", "AddEditCategoryDialog_onValueUpdate_badCategory=Please provide a category that is no more than {0} characters.", "AddEditCategoryDialog_onValueUpdate_suffixRepeat=Please provide a unique domain suffix.", @@ -147,16 +148,18 @@ class AddEditCategoryDialog extends javax.swing.JDialog { } String validationMessage = null; - if (safeSuffixStr.trim().length() == 0 || safeSuffixStr.trim().length() > WebCategoriesDataModel.getMaxDomainSuffixLength()) { + if (safeSuffixStr.trim().length() == 0 + || safeSuffixStr.trim().length() > WebCategoriesDataModel.getMaxDomainSuffixLength() + || safeSuffixStr.indexOf('.') < 0) { validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_badSuffix(WebCategoriesDataModel.getMaxCategoryLength()); } else if (safeCategoryStr.trim().length() == 0 || safeCategoryStr.trim().length() > WebCategoriesDataModel.getMaxCategoryLength()) { validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_badCategory(WebCategoriesDataModel.getMaxCategoryLength()); - } else if (currentSuffixes.contains(normalizedSuffix) && - (currentDomainCategory == null || - !normalizedSuffix.equals(currentDomainCategory.getHostSuffix()))) { - + } else if (currentSuffixes.contains(normalizedSuffix) + && (currentDomainCategory == null + || !normalizedSuffix.equals(currentDomainCategory.getHostSuffix()))) { + validationMessage = Bundle.AddEditCategoryDialog_onValueUpdate_suffixRepeat(); } else if (currentDomainCategory != null @@ -167,7 +170,7 @@ class AddEditCategoryDialog extends javax.swing.JDialog { } saveButton.setEnabled(validationMessage == null); - validationLabel.setText(validationMessage == null ? "" : validationMessage); + validationLabel.setText(validationMessage == null ? "" : String.format("%s", validationMessage)); } /** @@ -246,12 +249,12 @@ class AddEditCategoryDialog extends javax.swing.JDialog { .addComponent(categoryTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(categoryLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(validationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(validationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 46, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(saveButton) - .addComponent(cancelButton)) - .addContainerGap(12, Short.MAX_VALUE)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(cancelButton) + .addComponent(saveButton)) + .addContainerGap(8, Short.MAX_VALUE)) ); pack(); diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties index 1b0f7aeed8..fae4fa97ec 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties @@ -4,7 +4,7 @@ AddEditCategoryDialog.categoryLabel.text=Category: AddEditCategoryDialog.domainSuffixLabel.text=Domain Suffix: AddEditCategoryDialog.saveButton.text=Save AddEditCategoryDialog.cancelButton.text=Cancel -WebCategoriesOptionsPanel.panelDescription.text=This module allows you to find web categories based on host name. +WebCategoriesOptionsPanel.panelDescription.text=This module allows you to classify web sites based on domain names. WebCategoriesOptionsPanel.categoriesTitle.text=Categories: WebCategoriesOptionsPanel.newEntryButton.text=New Entry WebCategoriesOptionsPanel.editEntryButton.text=Edit Entry diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties-MERGED index cfa7939abf..26ba866dbc 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/Bundle.properties-MERGED @@ -3,7 +3,7 @@ AddEditCategoryDialog_Edit=Edit Entry # {0} - maxCategoryLen AddEditCategoryDialog_onValueUpdate_badCategory=Please provide a category that is no more than {0} characters. # {0} - maxSuffixLen -AddEditCategoryDialog_onValueUpdate_badSuffix=Please provide a domain suffix that is no more than {0} characters. +AddEditCategoryDialog_onValueUpdate_badSuffix=Please provide a domain suffix that is no more than {0} characters that includes at least one period. AddEditCategoryDialog_onValueUpdate_sameCategory=Please provide a new category for this domain suffix. AddEditCategoryDialog_onValueUpdate_suffixRepeat=Please provide a unique domain suffix. WebCategoriesOptionsPanel_categoryTable_categoryColumnName=Category @@ -19,7 +19,7 @@ AddEditCategoryDialog.categoryLabel.text=Category: AddEditCategoryDialog.domainSuffixLabel.text=Domain Suffix: AddEditCategoryDialog.saveButton.text=Save AddEditCategoryDialog.cancelButton.text=Cancel -WebCategoriesOptionsPanel.panelDescription.text=This module allows you to find web categories based on host name. +WebCategoriesOptionsPanel.panelDescription.text=This module allows you to classify web sites based on domain names. WebCategoriesOptionsPanel.categoriesTitle.text=Categories: WebCategoriesOptionsPanel.newEntryButton.text=New Entry WebCategoriesOptionsPanel.editEntryButton.text=Edit Entry diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/CustomWebCategorizer.java b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/CustomWebCategorizer.java index d661385f35..26acd97f3a 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/CustomWebCategorizer.java +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/CustomWebCategorizer.java @@ -28,7 +28,9 @@ import org.sleuthkit.autopsy.url.analytics.DomainCategorizerException; import org.sleuthkit.autopsy.url.analytics.DomainCategory; /** - * A DomainCategoryProvider for custom web categories. + * A DomainCategoryProvider for custom web categories. NOTE: If this class + * package or name change, code in DomainCategoryRunner will also need to change + * to reflect the changing class name for ordering purposes. */ @ServiceProvider(service = DomainCategorizer.class) public class CustomWebCategorizer implements DomainCategorizer { diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/WebCategoriesOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/WebCategoriesOptionsPanel.form index 5cead0d5a8..177919921c 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/WebCategoriesOptionsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/WebCategoriesOptionsPanel.form @@ -89,6 +89,9 @@ + + + @@ -104,6 +107,9 @@ + + + @@ -119,6 +125,9 @@ + + + @@ -134,6 +143,9 @@ + + + @@ -149,6 +161,9 @@ + + + diff --git a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/WebCategoriesOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/WebCategoriesOptionsPanel.java index e5a32860f3..96b74310c5 100644 --- a/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/WebCategoriesOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/url/analytics/domaincategorization/WebCategoriesOptionsPanel.java @@ -35,6 +35,7 @@ import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.filechooser.FileNameExtensionFilter; +import org.apache.commons.collections.CollectionUtils; import org.openide.util.NbBundle.Messages; import org.openide.util.WeakListeners; import org.sleuthkit.autopsy.corecomponents.OptionsPanel; @@ -101,12 +102,12 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i } /** - * Returns the item selected in the table or null if no selection. + * Returns the items selected in the table or null if no selection. * - * @return The item selected in the table or null if no selection. + * @return The items selected in the table or null if no selection. */ - private DomainCategory getSelected() { - return categoryTable.getSelectedItem(); + private List getSelected() { + return categoryTable.getSelectedItems(); } /** @@ -147,12 +148,13 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i * happening. */ private void refreshComponentStates() { - boolean selectedItem = getSelected() != null; + List selectedItems = getSelected(); + int selectedCount = CollectionUtils.isEmpty(selectedItems) ? 0 : selectedItems.size(); boolean isIngestRunning = IngestManager.getInstance().isIngestRunning(); boolean operationsPermitted = !isIngestRunning && !isRefreshing; - deleteEntryButton.setEnabled(selectedItem && operationsPermitted); - editEntryButton.setEnabled(selectedItem && operationsPermitted); + deleteEntryButton.setEnabled(selectedCount > 0 && operationsPermitted); + editEntryButton.setEnabled(selectedCount == 1 && operationsPermitted); newEntryButton.setEnabled(operationsPermitted); exportSetButton.setEnabled(operationsPermitted); @@ -287,6 +289,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i gridBagConstraints.insets = new java.awt.Insets(2, 10, 10, 0); add(categoryTablePanel, gridBagConstraints); + newEntryButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/add16.png"))); // NOI18N newEntryButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.newEntryButton.text")); // NOI18N newEntryButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -301,6 +304,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); add(newEntryButton, gridBagConstraints); + editEntryButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/edit16.png"))); // NOI18N editEntryButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.editEntryButton.text")); // NOI18N editEntryButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -315,6 +319,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5); add(editEntryButton, gridBagConstraints); + deleteEntryButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/delete16.png"))); // NOI18N deleteEntryButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.deleteEntryButton.text")); // NOI18N deleteEntryButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -328,6 +333,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i gridBagConstraints.insets = new java.awt.Insets(0, 0, 5, 5); add(deleteEntryButton, gridBagConstraints); + importSetButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/import16.png"))); // NOI18N importSetButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.importSetButton.text")); // NOI18N importSetButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -342,6 +348,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i gridBagConstraints.insets = new java.awt.Insets(0, 10, 5, 5); add(importSetButton, gridBagConstraints); + exportSetButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/export16.png"))); // NOI18N exportSetButton.setText(org.openide.util.NbBundle.getMessage(WebCategoriesOptionsPanel.class, "WebCategoriesOptionsPanel.exportSetButton.text")); // NOI18N exportSetButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -375,14 +382,20 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i }// //GEN-END:initComponents private void deleteEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteEntryButtonActionPerformed - DomainCategory selected = getSelected(); - if (selected != null && selected.getHostSuffix() != null) { - try { - runUpdateAction(() -> dataModel.deleteRecord(selected.getHostSuffix())); - } catch (IllegalArgumentException | SQLException | IOException ex) { - setDefaultCursor(); - logger.log(Level.WARNING, "There was an error while deleting: " + selected.getHostSuffix(), ex); + List selectedItems = getSelected(); + if (!CollectionUtils.isEmpty(selectedItems)) { + setWaitingCursor(); + for (DomainCategory selected : selectedItems) { + if (selected != null && selected.getHostSuffix() != null) { + try { + dataModel.deleteRecord(selected.getHostSuffix()); + } catch (IllegalArgumentException | SQLException ex) { + logger.log(Level.WARNING, "There was an error while deleting: " + selected.getHostSuffix(), ex); + } + } } + setDefaultCursor(); + refresh(); } }//GEN-LAST:event_deleteEntryButtonActionPerformed @@ -399,16 +412,19 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i }//GEN-LAST:event_newEntryButtonActionPerformed private void editEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editEntryButtonActionPerformed - DomainCategory selected = getSelected(); - if (selected != null && selected.getHostSuffix() != null) { - try { - DomainCategory newCategory = getAddEditValue(selected); - if (newCategory != null) { - runUpdateAction(() -> dataModel.insertUpdateSuffix(newCategory)); + List selectedItems = getSelected(); + if (CollectionUtils.isNotEmpty(selectedItems)) { + DomainCategory selected = selectedItems.get(0); + if (selected != null && selected.getHostSuffix() != null) { + try { + DomainCategory newCategory = getAddEditValue(selected); + if (newCategory != null) { + runUpdateAction(() -> dataModel.insertUpdateSuffix(newCategory)); + } + } catch (IllegalArgumentException | SQLException | IOException ex) { + setDefaultCursor(); + logger.log(Level.WARNING, "There was an error while editing: " + selected.getHostSuffix(), ex); } - } catch (IllegalArgumentException | SQLException | IOException ex) { - setDefaultCursor(); - logger.log(Level.WARNING, "There was an error while editing: " + selected.getHostSuffix(), ex); } } }//GEN-LAST:event_editEntryButtonActionPerformed @@ -417,7 +433,7 @@ public class WebCategoriesOptionsPanel extends IngestModuleGlobalSettingsPanel i "WebCategoriesOptionsPanel_importSetButtonActionPerformed_errorMessage=There was an error importing this json file.", "WebCategoriesOptionsPanel_importSetButtonActionPerformed_errorTitle=Import Error",}) private void importSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importSetButtonActionPerformed - fileChooser.setSelectedFile(null); + fileChooser.setSelectedFile(new File("")); int result = fileChooser.showOpenDialog(this); if (result == JFileChooser.APPROVE_OPTION) { File selectedFile = fileChooser.getSelectedFile(); diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/CustomDomainCategorizer.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/CustomDomainCategorizer.java deleted file mode 100644 index 7f49ec0318..0000000000 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/CustomDomainCategorizer.java +++ /dev/null @@ -1,188 +0,0 @@ -/* UNCLASSIFIED - * - * Viking - * - * Copyright (c) 2021 Basis Technology Corporation. - * Contact: brianc@basistech.com - */ -package org.sleuthkit.autopsy.recentactivity; - -import java.io.File; -import java.nio.file.Paths; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.apache.commons.lang3.StringUtils; -import org.openide.modules.InstalledFileLocator; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.url.analytics.DomainCategorizer; -import org.sleuthkit.autopsy.url.analytics.DomainCategorizerException; -import org.sleuthkit.autopsy.url.analytics.DomainCategory; - -/** - * A DomainCategoryProvider that utilizes a sqlite db for data. - */ -public class CustomDomainCategorizer implements DomainCategorizer { - - private static final String ROOT_FOLDER = "DomainCategorization"; - private static final String FILE_REL_PATH = "custom_categorization.db"; - - private static final String JDBC_SQLITE_PREFIX = "jdbc:sqlite:"; - private static final String SUFFIX_COLUMN = "suffix"; - private static final String CATEGORY_COLUMN = "category_id"; - - // get the suffix and category from the main table and gets the longest matching suffix. - private static final String BASE_QUERY_FMT_STR = String.format( - "SELECT %s, %s FROM domain_suffix WHERE suffix IN (%%s) ORDER BY LENGTH(%s) DESC LIMIT 1", - SUFFIX_COLUMN, CATEGORY_COLUMN, SUFFIX_COLUMN); - - private static final String CATEGORY_ID_COLUMN = "id"; - private static final String CATEGORY_NAME_COLUMN = "name"; - private static final String CATEGORY_IDS_QUERY = String.format("SELECT %s,%s FROM category", CATEGORY_ID_COLUMN, CATEGORY_NAME_COLUMN); - - private static final Logger logger = Logger.getLogger(CustomDomainCategorizer.class.getName()); - - private Connection dbConn = null; - private Map categoryIds = null; - - - /** - * Retrieves all the possible suffixes that could be tracked. For instance, - * if the host was "chatenabled.mail.google.com", the list should be - * ["chatenabled.mail.google.com", "mail.google.com", "google.com", "com"]. - * - * @param host The host. - * @return The possible suffixes. - */ - private List getSuffixes(String host) { - if (host == null) { - return null; - } - - List hostTokens = Arrays.asList(host.split("\\.")); - List hostSegmentations = new ArrayList<>(); - - for (int i = 0; i < hostTokens.size(); i++) { - String searchString = String.join(".", hostTokens.subList(i, hostTokens.size())); - hostSegmentations.add(searchString); - } - - return hostSegmentations; - } - - @Override - public DomainCategory getCategory(String domain, String host) throws DomainCategorizerException { - String hostToUse = (StringUtils.isBlank(host)) ? domain : host; - if (StringUtils.isBlank(hostToUse)) { - return null; - } - - hostToUse = hostToUse.toLowerCase(); - - List segmentations = getSuffixes(host); - String questionMarks = IntStream.range(0, segmentations.size()) - .mapToObj((num) -> "?") - .collect(Collectors.joining(",")); - - String sql = String.format(BASE_QUERY_FMT_STR, questionMarks); - - try (PreparedStatement stmt = dbConn.prepareStatement(sql)) { - for (int i = 0; i < segmentations.size(); i++) { - stmt.setString(i + 1, segmentations.get(i)); - } - - try (ResultSet resultSet = stmt.executeQuery()) { - if (resultSet.next()) { - String suffix = resultSet.getString(SUFFIX_COLUMN); - int categoryId = resultSet.getInt(CATEGORY_COLUMN); - String category = (resultSet.wasNull()) ? null : categoryIds.get(categoryId); - if (StringUtils.isNotBlank(suffix) && StringUtils.isNotBlank(category)) { - return new DomainCategory(suffix, category); - } - } - } - } catch (SQLException ex) { - logger.log(Level.WARNING, "There was an error retrieving results for " + hostToUse, ex); - } - - return null; - } - - /** - * Retrieves a mapping of category ids to the name of the category. - * - * @param dbConn The database connection to the sqlite database with the - * category table. - * @return The mapping of category id to category name. - * @throws SQLException - */ - private Map getCategoryIds(Connection dbConn) throws SQLException { - if (dbConn == null) { - return null; - } - - Map toRet = new HashMap<>(); - try (PreparedStatement stmt = dbConn.prepareStatement(CATEGORY_IDS_QUERY)) { - try (ResultSet resultSet = stmt.executeQuery()) { - while (resultSet.next()) { - toRet.put(resultSet.getInt(CATEGORY_ID_COLUMN), resultSet.getString(CATEGORY_NAME_COLUMN)); - } - } - } - - return toRet; - } - - @Override - public void initialize() throws DomainCategorizerException { - File dir = InstalledFileLocator.getDefault().locate(ROOT_FOLDER, CustomDomainCategorizer.class.getPackage().getName(), false); - - boolean needNew = true; - try { - if (dbConn != null && !dbConn.isClosed()) { - needNew = false; - } - } catch (SQLException ex) { - logger.log(Level.WARNING, "There was an error while checking to see if previously existing connection.", ex); - } - - if (needNew) { - try { - if (dir == null || !dir.exists()) { - throw new DomainCategorizerException("Could not find parent directory of database"); - } - - File dbFile = Paths.get(dir.toString(), FILE_REL_PATH).toFile(); - if (dbFile == null || !dbFile.exists()) { - throw new DomainCategorizerException("Could not find database file in directory: " + dir.toString()); - } - - Class.forName("org.sqlite.JDBC"); - dbConn = DriverManager.getConnection(JDBC_SQLITE_PREFIX + dbFile.toString()); - categoryIds = getCategoryIds(dbConn); - } catch (ClassNotFoundException | SQLException ex) { - throw new DomainCategorizerException("Unable to connect to class resource db.", ex); - } - } - } - - @Override - public void close() throws Exception { - if (dbConn != null && !dbConn.isClosed()) { - dbConn.close(); - } - dbConn = null; - - categoryIds = null; - } -} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DomainCategoryRunner.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DomainCategoryRunner.java index cb06a525c9..94108c5a00 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DomainCategoryRunner.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/DomainCategoryRunner.java @@ -83,6 +83,9 @@ class DomainCategoryRunner extends Extract { private static final Logger logger = Logger.getLogger(DomainCategoryRunner.class.getName()); + // NOTE: if CustomWebCategorizer ever changes name, this will need to be changed as well. + private static final String CUSTOM_CATEGORIZER_PATH = "org.sleuthkit.autopsy.url.analytics.domaincategorization.CustomWebCategorizer"; + /** * Get seconds from epoch from the mapping for the attribute type id. * @@ -444,7 +447,16 @@ class DomainCategoryRunner extends Extract { List foundProviders = lookupList.stream() .filter(provider -> provider != null) - .sorted((a, b) -> a.getClass().getName().compareToIgnoreCase(b.getClass().getName())) + .sorted((a, b) -> { + boolean aIsCustom = a.getClass().getName().contains(CUSTOM_CATEGORIZER_PATH); + boolean bIsCustom = b.getClass().getName().contains(CUSTOM_CATEGORIZER_PATH); + if (aIsCustom != bIsCustom) { + // push custom categorizer to top + return -Boolean.compare(aIsCustom, bIsCustom); + } + + return a.getClass().getName().compareToIgnoreCase(b.getClass().getName()); + }) .collect(Collectors.toList()); // add the default categorizer last as a last resort