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)