19 package org.sleuthkit.autopsy.url.analytics.domaincategorization;
 
   21 import java.awt.Cursor;
 
   22 import java.beans.PropertyChangeListener;
 
   24 import java.io.IOException;
 
   25 import java.sql.SQLException;
 
   26 import java.util.Arrays;
 
   27 import java.util.HashSet;
 
   28 import java.util.List;
 
   30 import java.util.logging.Level;
 
   31 import java.util.logging.Logger;
 
   32 import java.util.stream.Collectors;
 
   33 import javax.swing.JFileChooser;
 
   34 import javax.swing.JFrame;
 
   35 import javax.swing.JOptionPane;
 
   36 import javax.swing.SwingUtilities;
 
   37 import javax.swing.filechooser.FileNameExtensionFilter;
 
   38 import org.apache.commons.collections.CollectionUtils;
 
   39 import org.apache.commons.lang.StringUtils;
 
   40 import org.openide.util.NbBundle.Messages;
 
   41 import org.openide.util.WeakListeners;
 
   58     "WebCategoriesOptionsPanel_categoryTable_suffixColumnName=Domain Suffix",
 
   59     "WebCategoriesOptionsPanel_categoryTable_categoryColumnName=Category",})
 
   63     private static final String DEFAULT_EXTENSION = 
"json";
 
   64     private static final FileNameExtensionFilter DB_FILTER = 
new FileNameExtensionFilter(
"JSON", DEFAULT_EXTENSION);
 
   66     private final JFileChooser fileChooser = 
new JFileChooser();
 
   72                             Bundle.WebCategoriesOptionsPanel_categoryTable_suffixColumnName(),
 
   74                                     .setTooltip(domCat.getHostSuffix()),
 
   78                             Bundle.WebCategoriesOptionsPanel_categoryTable_categoryColumnName(),
 
   80                                     .setTooltip(domCat.getCategory()),
 
   83             )).setKeyFunction((domCat) -> domCat.getHostSuffix());
 
   85     private final PropertyChangeListener ingestListener = (evt) -> refreshComponentStates();
 
   86     private final PropertyChangeListener weakIngestListener = WeakListeners.propertyChange(ingestListener, 
this);
 
   87     private Set<String> domainSuffixes = 
new HashSet<>();
 
   88     private boolean isRefreshing = 
false;
 
   97         this.dataModel = dataModel;
 
   99         fileChooser.addChoosableFileFilter(DB_FILTER);
 
  100         fileChooser.setFileFilter(DB_FILTER);
 
  101         categoryTable.setCellListener((evt) -> refreshComponentStates());
 
  113         return categoryTable.getSelectedItems();
 
  121         refreshComponentStates();
 
  122         categoryTable.showDefaultLoadingMessage();
 
  124                 (noVal) -> this.dataModel.getRecords(),
 
  125                 (data) -> onRefreshedData(data),
 
  136         categoryTable.showDataFetchResult(categoriesResult);
 
  137         if (categoriesResult.getResultType() == 
ResultType.
SUCCESS && categoriesResult.getData() != null) {
 
  138             domainSuffixes = categoriesResult.getData().stream()
 
  139                     .map((dc) -> dc.getHostSuffix())
 
  140                     .collect(Collectors.toSet());
 
  142             domainSuffixes = 
new HashSet<>();
 
  144         isRefreshing = 
false;
 
  145         refreshComponentStates();
 
  154         List<DomainCategory> selectedItems = getSelected();
 
  155         int selectedCount = CollectionUtils.isEmpty(selectedItems) ? 0 : selectedItems.size();
 
  157         boolean operationsPermitted = !isIngestRunning && !isRefreshing;
 
  159         deleteEntryButton.setEnabled(selectedCount > 0 && operationsPermitted);
 
  160         editEntryButton.setEnabled(selectedCount == 1 && operationsPermitted);
 
  162         newEntryButton.setEnabled(operationsPermitted);
 
  163         exportSetButton.setEnabled(operationsPermitted);
 
  164         importSetButton.setEnabled(operationsPermitted);
 
  166         ingestRunningWarning.setVisible(isIngestRunning);
 
  178         JFrame parent = (this.getRootPane() != null && this.getRootPane().getParent() instanceof JFrame)
 
  179                 ? (JFrame) this.getRootPane().getParent()
 
  182         AddEditCategoryDialog addEditDialog = 
new AddEditCategoryDialog(parent, domainSuffixes, original);
 
  183         addEditDialog.setResizable(
false);
 
  184         addEditDialog.setLocationRelativeTo(parent);
 
  185         addEditDialog.setVisible(
true);
 
  186         addEditDialog.toFront();
 
  188         if (addEditDialog.isChanged()) {
 
  189             return addEditDialog.getValue();
 
  199         SwingUtilities.invokeLater(() -> this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)));
 
  206         SwingUtilities.invokeLater(() -> this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)));
 
  221         void run() 
throws IllegalArgumentException, IOException, SQLException;
 
  245     @SuppressWarnings(
"unchecked")
 
  247     private 
void initComponents() {
 
  248         java.awt.GridBagConstraints gridBagConstraints;
 
  250         javax.swing.JLabel panelDescription = 
new javax.swing.JLabel();
 
  251         javax.swing.JLabel categoriesTitle = 
new javax.swing.JLabel();
 
  252         javax.swing.JPanel categoryTablePanel = categoryTable;
 
  253         newEntryButton = 
new javax.swing.JButton();
 
  254         editEntryButton = 
new javax.swing.JButton();
 
  255         deleteEntryButton = 
new javax.swing.JButton();
 
  256         importSetButton = 
new javax.swing.JButton();
 
  257         exportSetButton = 
new javax.swing.JButton();
 
  258         javax.swing.JPanel bottomStrut = 
new javax.swing.JPanel();
 
  259         ingestRunningWarning = 
new javax.swing.JLabel();
 
  261         setLayout(
new java.awt.GridBagLayout());
 
  263         panelDescription.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
 
  264         panelDescription.setText(
org.openide.util.NbBundle.getMessage(
WebCategoriesOptionsPanel.class, 
"WebCategoriesOptionsPanel.panelDescription.text")); 
 
  265         panelDescription.setBorder(javax.swing.BorderFactory.createCompoundBorder(javax.swing.BorderFactory.createEtchedBorder(), javax.swing.BorderFactory.createEmptyBorder(5, 5, 5, 5)));
 
  266         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  267         gridBagConstraints.gridwidth = 3;
 
  268         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
 
  269         gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
 
  270         gridBagConstraints.insets = 
new java.awt.Insets(10, 10, 10, 0);
 
  271         add(panelDescription, gridBagConstraints);
 
  273         categoriesTitle.setText(
org.openide.util.NbBundle.getMessage(
WebCategoriesOptionsPanel.class, 
"WebCategoriesOptionsPanel.categoriesTitle.text")); 
 
  274         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  275         gridBagConstraints.gridx = 0;
 
  276         gridBagConstraints.gridy = 1;
 
  277         gridBagConstraints.gridwidth = 3;
 
  278         gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
 
  279         gridBagConstraints.insets = 
new java.awt.Insets(0, 10, 0, 0);
 
  280         add(categoriesTitle, gridBagConstraints);
 
  282         categoryTablePanel.setAutoscrolls(
true);
 
  283         categoryTablePanel.setMaximumSize(
new java.awt.Dimension(400, 32767));
 
  284         categoryTablePanel.setMinimumSize(
new java.awt.Dimension(400, 300));
 
  285         categoryTablePanel.setPreferredSize(
new java.awt.Dimension(400, 600));
 
  286         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  287         gridBagConstraints.gridx = 0;
 
  288         gridBagConstraints.gridy = 2;
 
  289         gridBagConstraints.gridwidth = 3;
 
  290         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
 
  291         gridBagConstraints.weighty = 1.0;
 
  292         gridBagConstraints.insets = 
new java.awt.Insets(2, 10, 10, 0);
 
  293         add(categoryTablePanel, gridBagConstraints);
 
  295         newEntryButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/add16.png"))); 
 
  296         newEntryButton.setText(
org.openide.util.NbBundle.getMessage(
WebCategoriesOptionsPanel.class, 
"WebCategoriesOptionsPanel.newEntryButton.text")); 
 
  297         newEntryButton.addActionListener(
new java.awt.event.ActionListener() {
 
  298             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
  299                 newEntryButtonActionPerformed(evt);
 
  302         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  303         gridBagConstraints.gridx = 0;
 
  304         gridBagConstraints.gridy = 3;
 
  305         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
 
  306         gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
 
  307         gridBagConstraints.insets = 
new java.awt.Insets(0, 10, 5, 5);
 
  308         add(newEntryButton, gridBagConstraints);
 
  310         editEntryButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/edit16.png"))); 
 
  311         editEntryButton.setText(
org.openide.util.NbBundle.getMessage(
WebCategoriesOptionsPanel.class, 
"WebCategoriesOptionsPanel.editEntryButton.text")); 
 
  312         editEntryButton.addActionListener(
new java.awt.event.ActionListener() {
 
  313             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
  314                 editEntryButtonActionPerformed(evt);
 
  317         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  318         gridBagConstraints.gridx = 1;
 
  319         gridBagConstraints.gridy = 3;
 
  320         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
 
  321         gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
 
  322         gridBagConstraints.insets = 
new java.awt.Insets(0, 0, 5, 5);
 
  323         add(editEntryButton, gridBagConstraints);
 
  325         deleteEntryButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/delete16.png"))); 
 
  326         deleteEntryButton.setText(
org.openide.util.NbBundle.getMessage(
WebCategoriesOptionsPanel.class, 
"WebCategoriesOptionsPanel.deleteEntryButton.text")); 
 
  327         deleteEntryButton.addActionListener(
new java.awt.event.ActionListener() {
 
  328             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
  329                 deleteEntryButtonActionPerformed(evt);
 
  332         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  333         gridBagConstraints.gridx = 2;
 
  334         gridBagConstraints.gridy = 3;
 
  335         gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
 
  336         gridBagConstraints.insets = 
new java.awt.Insets(0, 0, 5, 5);
 
  337         add(deleteEntryButton, gridBagConstraints);
 
  339         importSetButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/import16.png"))); 
 
  340         importSetButton.setText(
org.openide.util.NbBundle.getMessage(
WebCategoriesOptionsPanel.class, 
"WebCategoriesOptionsPanel.importSetButton.text")); 
 
  341         importSetButton.addActionListener(
new java.awt.event.ActionListener() {
 
  342             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
  343                 importSetButtonActionPerformed(evt);
 
  346         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  347         gridBagConstraints.gridx = 0;
 
  348         gridBagConstraints.gridy = 4;
 
  349         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
 
  350         gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
 
  351         gridBagConstraints.insets = 
new java.awt.Insets(0, 10, 5, 5);
 
  352         add(importSetButton, gridBagConstraints);
 
  354         exportSetButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/export16.png"))); 
 
  355         exportSetButton.setText(
org.openide.util.NbBundle.getMessage(
WebCategoriesOptionsPanel.class, 
"WebCategoriesOptionsPanel.exportSetButton.text")); 
 
  356         exportSetButton.addActionListener(
new java.awt.event.ActionListener() {
 
  357             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
  358                 exportSetButtonActionPerformed(evt);
 
  361         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  362         gridBagConstraints.gridx = 1;
 
  363         gridBagConstraints.gridy = 4;
 
  364         gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
 
  365         gridBagConstraints.insets = 
new java.awt.Insets(0, 0, 5, 5);
 
  366         add(exportSetButton, gridBagConstraints);
 
  368         bottomStrut.setPreferredSize(
new java.awt.Dimension(10, 0));
 
  369         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  370         gridBagConstraints.gridx = 3;
 
  371         gridBagConstraints.gridy = 6;
 
  372         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
 
  373         gridBagConstraints.weightx = 1.0;
 
  374         add(bottomStrut, gridBagConstraints);
 
  376         ingestRunningWarning.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/images/warning16.png"))); 
 
  377         ingestRunningWarning.setText(
org.openide.util.NbBundle.getMessage(
WebCategoriesOptionsPanel.class, 
"WebCategoriesOptionsPanel.ingestRunningWarning.text")); 
 
  378         gridBagConstraints = 
new java.awt.GridBagConstraints();
 
  379         gridBagConstraints.gridx = 0;
 
  380         gridBagConstraints.gridy = 5;
 
  381         gridBagConstraints.gridwidth = 3;
 
  382         gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
 
  383         gridBagConstraints.insets = 
new java.awt.Insets(10, 10, 10, 10);
 
  384         add(ingestRunningWarning, gridBagConstraints);
 
  388         List<DomainCategory> selectedItems = getSelected();
 
  389         if (!CollectionUtils.isEmpty(selectedItems)) {
 
  392                 if (selected != null && selected.getHostSuffix() != null) {
 
  394                         dataModel.deleteRecord(selected.getHostSuffix());
 
  395                     } 
catch (IllegalArgumentException | SQLException ex) {
 
  396                         logger.log(Level.WARNING, 
"There was an error while deleting: " + selected.getHostSuffix(), ex);
 
  407         if (newCategory != null) {
 
  409                 runUpdateAction(() -> dataModel.insertUpdateSuffix(newCategory));
 
  410             } 
catch (IllegalArgumentException | SQLException | IOException ex) {
 
  412                 logger.log(Level.WARNING, 
"There was an error while adding new record: " + newCategory.
getHostSuffix(), ex);
 
  418         List<DomainCategory> selectedItems = getSelected();
 
  419         if (CollectionUtils.isNotEmpty(selectedItems)) {
 
  424                     if (newCategory != null) {
 
  425                         runUpdateAction(() -> dataModel.insertUpdateSuffix(newCategory));
 
  427                 } 
catch (IllegalArgumentException | SQLException | IOException ex) {
 
  429                     logger.log(Level.WARNING, 
"There was an error while editing: " + selected.
getHostSuffix(), ex);
 
  436         "WebCategoriesOptionsPanel_importSetButtonActionPerformed_errorMessage=There was an error importing this json file.",
 
  437         "WebCategoriesOptionsPanel_importSetButtonActionPerformed_errorTitle=Import Error",
 
  438         "WebCategoriesOptionsPanel_importSetButtonActionPerformed_onConflictTitle=Domain Suffix Already Exists",
 
  439         "# {0} - domainSuffix",
 
  440         "WebCategoriesOptionsPanel_importSetButtonActionPerformed_onConflictMessage=Domain suffix {0} already exists. What would you like to do?",
 
  441         "WebCategoriesOptionsPanel_importSetButtonActionPerformed_onConflictOverwrite=Overwrite",
 
  442         "WebCategoriesOptionsPanel_importSetButtonActionPerformed_onConflictSkip=Skip",
 
  443         "WebCategoriesOptionsPanel_importSetButtonActionPerformed_onConflictCancel=Cancel"})
 
  445         fileChooser.setSelectedFile(
new File(
""));
 
  446         int result = fileChooser.showOpenDialog(
this);
 
  447         if (result == JFileChooser.APPROVE_OPTION) {
 
  448             File selectedFile = fileChooser.getSelectedFile();
 
  449             if (selectedFile != null && selectedFile.exists()) {
 
  451                     runUpdateAction(() -> {
 
  452                         List<DomainCategory> categories = dataModel.getJsonEntries(selectedFile);
 
  455                             String normalizedCategory = domcat == null ? 
"" : WebCategoriesDataModel.getNormalizedCategory(domcat.getCategory());
 
  456                             String normalizedSuffix = domcat == null ? 
"" : WebCategoriesDataModel.getNormalizedSuffix(domcat.getHostSuffix());
 
  458                             if (StringUtils.isBlank(normalizedCategory) || StringUtils.isBlank(normalizedSuffix)) {
 
  459                                 logger.log(Level.WARNING, String.format(
"Invalid entry [category: %s, domain suffix: %s]", normalizedCategory, normalizedSuffix));
 
  463                             DomainCategory currentCategory = dataModel.getRecordBySuffix(normalizedSuffix);
 
  465                             if (currentCategory != null) {
 
  466                                 if (normalizedCategory.equalsIgnoreCase(currentCategory.
getCategory()))  {
 
  472                                         Bundle.WebCategoriesOptionsPanel_importSetButtonActionPerformed_onConflictOverwrite(),
 
  473                                         Bundle.WebCategoriesOptionsPanel_importSetButtonActionPerformed_onConflictSkip(),
 
  474                                         Bundle.WebCategoriesOptionsPanel_importSetButtonActionPerformed_onConflictCancel()
 
  477                                     int optionItem = JOptionPane.showOptionDialog(null,
 
  478                                             Bundle.WebCategoriesOptionsPanel_importSetButtonActionPerformed_onConflictMessage(normalizedSuffix),
 
  479                                             Bundle.WebCategoriesOptionsPanel_importSetButtonActionPerformed_onConflictTitle(),
 
  480                                             JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]);
 
  482                                     switch (optionItem) {
 
  493                             dataModel.insertUpdateSuffix(
new DomainCategory(normalizedSuffix, normalizedCategory));
 
  496                 } 
catch (IllegalArgumentException | SQLException | IOException ex) {
 
  498                     JOptionPane.showMessageDialog(
 
  500                             Bundle.WebCategoriesOptionsPanel_importSetButtonActionPerformed_errorMessage(),
 
  501                             Bundle.WebCategoriesOptionsPanel_importSetButtonActionPerformed_errorTitle(),
 
  502                             JOptionPane.ERROR_MESSAGE);
 
  503                     logger.log(Level.WARNING, 
"There was an error on import.", ex);
 
  510         "WebCategoriesOptionsPanel_exportSetButtonActionPerformed_duplicateMessage=A file already exists at the selected path.  The categories will not be exported.",
 
  511         "WebCategoriesOptionsPanel_exportSetButtonActionPerformed_duplicateTitle=File Already Exists",
 
  512         "WebCategoriesOptionsPanel_exportSetButtonActionPerformed_errorMessage=There was an error exporting.",
 
  513         "WebCategoriesOptionsPanel_exportSetButtonActionPerformed_errorTitle=Export Error",
 
  514         "WebCategoriesOptionsPanel_exportSetButtonActionPerformed_defaultFileName=Custom Categories Export" 
  517         fileChooser.setSelectedFile(
new File(String.format(
"%s.json", Bundle.WebCategoriesOptionsPanel_exportSetButtonActionPerformed_defaultFileName())));
 
  518         int result = fileChooser.showSaveDialog(
this);
 
  519         if (result == JFileChooser.APPROVE_OPTION) {
 
  520             File selectedFile = fileChooser.getSelectedFile();
 
  521             if (selectedFile != null) {
 
  522                 if (selectedFile.exists()) {
 
  523                     JOptionPane.showMessageDialog(
 
  525                             Bundle.WebCategoriesOptionsPanel_exportSetButtonActionPerformed_duplicateMessage(),
 
  526                             Bundle.WebCategoriesOptionsPanel_exportSetButtonActionPerformed_duplicateTitle(),
 
  527                             JOptionPane.ERROR_MESSAGE);
 
  532                     dataModel.exportToJson(selectedFile);
 
  534                 } 
catch (SQLException | IOException ex) {
 
  536                     JOptionPane.showMessageDialog(
 
  538                             Bundle.WebCategoriesOptionsPanel_importSetButtonActionPerformed_errorMessage(),
 
  539                             Bundle.WebCategoriesOptionsPanel_importSetButtonActionPerformed_errorTitle(),
 
  540                             JOptionPane.ERROR_MESSAGE);
 
  541                     logger.log(Level.WARNING, 
"There was an error on export.", ex);
 
void importSetButtonActionPerformed(java.awt.event.ActionEvent evt)
final WebCategoriesDataModel dataModel
static synchronized IngestManager getInstance()
javax.swing.JButton deleteEntryButton
void editEntryButtonActionPerformed(java.awt.event.ActionEvent evt)
List< DomainCategory > getSelected()
boolean isIngestRunning()
DomainCategory getAddEditValue(DomainCategory original)
static< T, CextendsGuiCellModel > JTablePanel< T > getJTablePanel(List< ColumnModel< T, C >> columns)
javax.swing.JButton exportSetButton
javax.swing.JButton importSetButton
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)
WebCategoriesOptionsPanel(WebCategoriesDataModel dataModel)
javax.swing.JButton newEntryButton
void refreshComponentStates()
javax.swing.JButton editEntryButton
void onRefreshedData(DataFetchResult< List< DomainCategory >> categoriesResult)
synchronized static Logger getLogger(String name)
void runUpdateAction(UpdateAction runnable)
void exportSetButtonActionPerformed(java.awt.event.ActionEvent evt)
javax.swing.JLabel ingestRunningWarning
void deleteEntryButtonActionPerformed(java.awt.event.ActionEvent evt)
void newEntryButtonActionPerformed(java.awt.event.ActionEvent evt)