Merge pull request #2906 from narfindustries/hashdb_imports

Hashdb import error handling and code cleanup - resolved merge conflicts
This commit is contained in:
Richard Cordovano 2017-06-28 15:10:12 -04:00 committed by GitHub
commit 5c456e51a3
7 changed files with 198 additions and 191 deletions

View File

@ -32,6 +32,7 @@ import java.sql.Types;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -48,10 +49,8 @@ public abstract class AbstractSqlEamDb implements EamDb {
protected final List<EamArtifact.Type> DEFAULT_ARTIFACT_TYPES; protected final List<EamArtifact.Type> DEFAULT_ARTIFACT_TYPES;
private int bulkArtifactsCount; private int bulkArtifactsCount;
private int bulkGlobalArtifactsCount;
protected int bulkArtifactsThreshold; protected int bulkArtifactsThreshold;
private final Map<String, Collection<EamArtifact>> bulkArtifacts; private final Map<String, Collection<EamArtifact>> bulkArtifacts;
private final Map<String, Collection<EamGlobalFileInstance>> bulkGlobalArtifacts;
private final List<String> badTags; private final List<String> badTags;
/** /**
@ -60,18 +59,15 @@ public abstract class AbstractSqlEamDb implements EamDb {
* @throws UnknownHostException, EamDbException * @throws UnknownHostException, EamDbException
*/ */
protected AbstractSqlEamDb() { protected AbstractSqlEamDb() {
badTags = new ArrayList<String>(); badTags = new ArrayList<>();
bulkArtifactsCount = 0; bulkArtifactsCount = 0;
bulkGlobalArtifactsCount = 0;
bulkArtifacts = new HashMap<>(); bulkArtifacts = new HashMap<>();
bulkGlobalArtifacts = new HashMap<>();
DEFAULT_ARTIFACT_TYPES = EamArtifact.getDefaultArtifactTypes(); DEFAULT_ARTIFACT_TYPES = EamArtifact.getDefaultArtifactTypes();
for (EamArtifact.Type type : DEFAULT_ARTIFACT_TYPES) { DEFAULT_ARTIFACT_TYPES.forEach((type) -> {
bulkArtifacts.put(type.getName(), new ArrayList<>()); bulkArtifacts.put(type.getName(), new ArrayList<>());
bulkGlobalArtifacts.put(type.getName(), new ArrayList<>()); });
}
} }
/** /**
@ -1449,70 +1445,37 @@ public abstract class AbstractSqlEamDb implements EamDb {
} }
} }
/**
* Add a new global file instance to the bulk collection
*
* @param eamGlobalFileInstance The global file instance to add
*
* @throws EamDbException
*/
@Override
public void prepareGlobalFileInstance(EamGlobalFileInstance eamGlobalFileInstance) throws EamDbException {
synchronized (bulkGlobalArtifacts) {
bulkGlobalArtifacts.get("FILES").add(eamGlobalFileInstance); // NON-NLS
bulkGlobalArtifactsCount++;
if (bulkGlobalArtifactsCount >= bulkArtifactsThreshold) {
bulkInsertGlobalFileInstances();
}
}
}
/** /**
* Insert the bulk collection of Global File Instances * Insert the bulk collection of Global File Instances
* *
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
public void bulkInsertGlobalFileInstances() throws EamDbException { public void bulkInsertGlobalFileInstances(Set<EamGlobalFileInstance> globalInstances, EamArtifact.Type contentType) throws EamDbException {
List<EamArtifact.Type> artifactTypes = getCorrelationArtifactTypes();
Connection conn = connect(); Connection conn = connect();
synchronized (bulkGlobalArtifacts) {
if (bulkGlobalArtifactsCount == 0) { PreparedStatement bulkPs = null;
return; try {
// FUTURE: have a separate global_files table for each Type.
String sql = "INSERT INTO global_files(global_reference_set_id, value, known_status, comment) VALUES (?, ?, ?, ?) "
+ getConflictClause();
bulkPs = conn.prepareStatement(sql);
for (EamGlobalFileInstance globalInstance : globalInstances) {
bulkPs.setInt(1, globalInstance.getGlobalSetID());
bulkPs.setString(2, globalInstance.getMD5Hash());
bulkPs.setString(3, globalInstance.getKnownStatus().name());
bulkPs.setString(4, globalInstance.getComment());
bulkPs.addBatch();
} }
PreparedStatement bulkPs = null; bulkPs.executeBatch();
try { } catch (SQLException ex) {
for (EamArtifact.Type type : artifactTypes) { throw new EamDbException("Error inserting bulk artifacts.", ex); // NON-NLS
String sql = "INSERT INTO global_files(global_reference_set_id, value, known_status, comment) VALUES (?, ?, ?, ?) " } finally {
+ getConflictClause(); EamDbUtil.closePreparedStatement(bulkPs);
EamDbUtil.closeConnection(conn);
bulkPs = conn.prepareStatement(sql);
Collection<EamGlobalFileInstance> eamGlobalFileInstances = bulkGlobalArtifacts.get(type.getName());
for (EamGlobalFileInstance eamGlobalFileInstance : eamGlobalFileInstances) {
bulkPs.setInt(1, eamGlobalFileInstance.getGlobalSetID());
bulkPs.setString(2, eamGlobalFileInstance.getMD5Hash());
bulkPs.setString(3, eamGlobalFileInstance.getKnownStatus().name());
bulkPs.setString(4, eamGlobalFileInstance.getComment());
bulkPs.addBatch();
}
bulkPs.executeBatch();
bulkGlobalArtifacts.get(type.getName()).clear();
}
// Reset state
bulkGlobalArtifactsCount = 0;
} catch (SQLException ex) {
throw new EamDbException("Error inserting bulk artifacts.", ex); // NON-NLS
} finally {
EamDbUtil.closePreparedStatement(bulkPs);
EamDbUtil.closeConnection(conn);
}
} }
} }

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.centralrepository.datamodel; package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* Main interface for interacting with the database * Main interface for interacting with the database
@ -422,14 +423,17 @@ public interface EamDb {
* *
* @throws EamDbException * @throws EamDbException
*/ */
void prepareGlobalFileInstance(EamGlobalFileInstance eamGlobalFileInstance) throws EamDbException; // void prepareGlobalFileInstance(EamGlobalFileInstance eamGlobalFileInstance) throws EamDbException;
/** /**
* Insert the bulk collection of Global File Instances * Insert the bulk collection of Global File Instances
* *
* @param globalInstances a Set of EamGlobalFileInstances to insert into the db.
* @param contentType the Type of the global instances
*
* @throws EamDbException * @throws EamDbException
*/ */
void bulkInsertGlobalFileInstances() throws EamDbException; void bulkInsertGlobalFileInstances(Set<EamGlobalFileInstance> globalInstances, EamArtifact.Type contentType) throws EamDbException;
/** /**
* Get all global file instances having a given MD5 hash * Get all global file instances having a given MD5 hash

View File

@ -43,5 +43,4 @@ public class EamDbException extends Exception {
public EamDbException(String message, Throwable cause) { public EamDbException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
} }

View File

@ -18,9 +18,9 @@
*/ */
package org.sleuthkit.autopsy.centralrepository.datamodel; package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.util.Objects;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
/** /**
* Global file hash instance * Global file hash instance
*/ */
@ -53,6 +53,25 @@ public class EamGlobalFileInstance {
this(-1, globalSetID, MD5Hash, knownStatus, comment); this(-1, globalSetID, MD5Hash, knownStatus, comment);
} }
@Override
public boolean equals(Object otherInstance) {
if (this == otherInstance) {
return true;
} else if (!(otherInstance instanceof EamGlobalFileInstance)) {
return false;
} else {
return (this.hashCode() == otherInstance.hashCode());
}
}
@Override
public int hashCode() {
int hash = 5;
hash = 59 * hash + this.globalSetID;
hash = 59 * hash + Objects.hashCode(this.MD5Hash);
hash = 59 * hash + this.knownStatus.hashCode();
return hash;
}
/** /**
* @return the instanceID * @return the instanceID
*/ */

View File

@ -2,13 +2,6 @@ OptionsCategory_Name_Central_Repository_Options=Central Repository
OptionsCategory_Keywords_Central_Repository_Options=Central Repository Settings OptionsCategory_Keywords_Central_Repository_Options=Central Repository Settings
ImportHashDatabaseDialog.known.text=Known ImportHashDatabaseDialog.known.text=Known
ImportHashDatabaseDialog.knownBad.text=Known Bad ImportHashDatabaseDialog.knownBad.text=Known Bad
ImportHashDatabaseDialog.importHashDbMsg=Import Hash Database
ImportHashDatabaseDialog.fileNameExtFilter.text=Hash Database File
ImportHashDatabaseDialog.failedToGetDbPathMsg=Failed to get the path of the selected database.
ImportHashDatabaseDialog.importHashDbErr=Import Hash Database Error
ImportHashDatabaseDialog.mustSelectHashDbFilePathMsg=A hash database file path must be selected.
ImportHashDatabaseDialog.hashDbDoesNotExistMsg=The selected hash database does not exist.
ImportHashDatabaseDialog.errorMessage.failedToOpenHashDbMsg=Failed to open hash database at {0}.
EamPostgresSettingsDialog.lbUserPassword.text=User Password : EamPostgresSettingsDialog.lbUserPassword.text=User Password :
EamPostgresSettingsDialog.lbUserName.text=User Name : EamPostgresSettingsDialog.lbUserName.text=User Name :
EamPostgresSettingsDialog.bnCancel.text=Cancel EamPostgresSettingsDialog.bnCancel.text=Cancel

View File

@ -27,13 +27,12 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="lbInstructions" max="32767" attributes="0"/> <Component id="lbInstructions" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="lbWarningMsg" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="okButton" linkSize="1" min="-2" max="-2" attributes="0"/> <Component id="okButton" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="cancelButton" linkSize="1" min="-2" max="-2" attributes="0"/> <Component id="cancelButton" linkSize="1" min="-2" max="-2" attributes="0"/>
@ -87,6 +86,7 @@
</Group> </Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
<Component id="lbWarningMsg" alignment="0" max="32767" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -96,49 +96,55 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="lbInstructions" max="32767" attributes="0"/> <Component id="lbInstructions" min="-2" pref="26" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="openButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="openButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="tfFilePath" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="tfFilePath" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbDatabasePath" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="lbDatabasePath" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0">
<Component id="lbDatabaseType" min="-2" max="-2" attributes="0"/> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="knownRadioButton" min="-2" max="-2" attributes="0"/> <Component id="lbDatabaseType" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="knownBadRadioButton" min="-2" max="-2" attributes="0"/> <Component id="knownRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="lbDatabaseAttribution" min="-2" max="-2" attributes="0"/> <Component id="knownBadRadioButton" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0"> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="bnNewOrganization" min="-2" max="-2" attributes="0"/> <Component id="lbDatabaseAttribution" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0"> <Group type="103" groupAlignment="0" max="-2" attributes="0">
<EmptySpace min="3" pref="3" max="-2" attributes="0"/> <Component id="bnNewOrganization" min="-2" max="-2" attributes="0"/>
<Component id="lbSourceOrganization" min="-2" pref="20" max="-2" attributes="0"/> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="3" pref="3" max="-2" attributes="0"/>
<Component id="lbSourceOrganization" min="-2" pref="20" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="comboboxSourceOrganization" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="tfDatabaseName" max="32767" attributes="0"/>
<Component id="lbDatabaseName" min="-2" pref="20" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbDatabaseVersion" alignment="3" min="-2" pref="20" max="-2" attributes="0"/>
<Component id="tfDatabaseVersion" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="lbWarningMsg" min="-2" pref="23" max="-2" attributes="0"/>
<EmptySpace pref="44" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="comboboxSourceOrganization" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="tfDatabaseName" max="32767" attributes="0"/>
<Component id="lbDatabaseName" min="-2" pref="20" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbDatabaseVersion" alignment="3" min="-2" pref="20" max="-2" attributes="0"/>
<Component id="tfDatabaseVersion" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Group type="103" alignment="0" groupAlignment="3" attributes="0">
<Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="lbWarningMsg" min="-2" pref="23" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>

View File

@ -29,22 +29,28 @@ import java.nio.file.Files;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.JTextField; import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.spi.options.OptionsPanelController; import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifact;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactInstance;
@ -53,6 +59,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamGlobalFileInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamGlobalSet; import org.sleuthkit.autopsy.centralrepository.datamodel.EamGlobalSet;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization; import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
/** /**
@ -76,9 +83,10 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
* and add it to the set of hash databases used to classify files as * and add it to the set of hash databases used to classify files as
* unknown, known, or known bad. * unknown, known, or known bad.
*/ */
@Messages({"ImportHashDatabaseDialog.importHashDbMsg=Import Hash Database"})
ImportHashDatabaseDialog() { ImportHashDatabaseDialog() {
super((JFrame) WindowManager.getDefault().getMainWindow(), super((JFrame) WindowManager.getDefault().getMainWindow(),
NbBundle.getMessage(ImportHashDatabaseDialog.class, "ImportHashDatabaseDialog.importHashDbMsg"), Bundle.ImportHashDatabaseDialog_importHashDbMsg(),
true); // NON-NLS true); // NON-NLS
textBoxes = new ArrayList<>(); textBoxes = new ArrayList<>();
textBoxChangedListener = new TextBoxChangedListener(); textBoxChangedListener = new TextBoxChangedListener();
@ -88,12 +96,14 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
display(); display();
} }
@Messages({"ImportHashDatabaseDialog.fileNameExtFilter.text=Hash Database File",})
private void initFileChooser() { private void initFileChooser() {
fileChooser.setDragEnabled(false); fileChooser.setDragEnabled(false);
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
String[] EXTENSION = new String[]{"idx"}; //NON-NLS String[] EXTENSION = new String[]{"idx"}; //NON-NLS
FileNameExtensionFilter filter = new FileNameExtensionFilter( FileNameExtensionFilter filter = new FileNameExtensionFilter(
NbBundle.getMessage(this.getClass(), "ImportHashDatabaseDialog.fileNameExtFilter.text"), EXTENSION); // NON-NLS Bundle.ImportHashDatabaseDialog_fileNameExtFilter_text(),
EXTENSION); // NON-NLS
fileChooser.setFileFilter(filter); fileChooser.setFileFilter(filter);
fileChooser.setMultiSelectionEnabled(false); fileChooser.setMultiSelectionEnabled(false);
} }
@ -108,6 +118,7 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
* Register for notifications when the text boxes get updated. * Register for notifications when the text boxes get updated.
*/ */
private void setTextBoxListeners() { private void setTextBoxListeners() {
textBoxes.add(tfFilePath);
textBoxes.add(tfDatabaseName); textBoxes.add(tfDatabaseName);
textBoxes.add(tfDatabaseVersion); textBoxes.add(tfDatabaseVersion);
addDocumentListeners(textBoxes, textBoxChangedListener); addDocumentListeners(textBoxes, textBoxChangedListener);
@ -339,8 +350,7 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lbInstructions, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lbInstructions, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(lbWarningMsg, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGap(0, 0, Short.MAX_VALUE)
.addGap(18, 18, 18)
.addComponent(okButton) .addComponent(okButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton)) .addComponent(cancelButton))
@ -379,7 +389,8 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
.addComponent(knownRadioButton) .addComponent(knownRadioButton)
.addComponent(knownBadRadioButton))) .addComponent(knownBadRadioButton)))
.addComponent(lbDatabaseAttribution)) .addComponent(lbDatabaseAttribution))
.addGap(0, 0, Short.MAX_VALUE))) .addGap(0, 0, Short.MAX_VALUE))
.addComponent(lbWarningMsg, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap()) .addContainerGap())
); );
@ -389,47 +400,52 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addComponent(lbInstructions, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lbInstructions, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(openButton) .addComponent(openButton)
.addComponent(tfFilePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(tfFilePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbDatabasePath)) .addComponent(lbDatabasePath))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lbDatabaseType)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(knownRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(knownBadRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(lbDatabaseAttribution)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(bnNewOrganization)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(3, 3, 3) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lbSourceOrganization, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))) .addComponent(lbDatabaseType)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(comboboxSourceOrganization, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(knownRadioButton)
.addGap(18, 18, 18) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(knownBadRadioButton)
.addComponent(tfDatabaseName) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(lbDatabaseName, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(lbDatabaseAttribution)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(bnNewOrganization)
.addComponent(lbDatabaseVersion, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createSequentialGroup()
.addComponent(tfDatabaseVersion, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGap(3, 3, 3)
.addGap(18, 18, 18) .addComponent(lbSourceOrganization, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(comboboxSourceOrganization, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(okButton) .addGap(18, 18, 18)
.addComponent(cancelButton)) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(lbWarningMsg, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(tfDatabaseName)
.addContainerGap()) .addComponent(lbDatabaseName, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbDatabaseVersion, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(tfDatabaseVersion, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lbWarningMsg, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(44, Short.MAX_VALUE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(okButton)
.addComponent(cancelButton))
.addContainerGap())))
); );
pack(); pack();
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@Messages({"ImportHashDatabaseDialog.failedToGetDbPathMsg=Failed to get the path of the selected database.",})
private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openButtonActionPerformed private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openButtonActionPerformed
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
File databaseFile = fileChooser.getSelectedFile(); File databaseFile = fileChooser.getSelectedFile();
@ -441,9 +457,7 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
ModuleSettings.setConfigSetting(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY, databaseFile.getParent()); ModuleSettings.setConfigSetting(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY, databaseFile.getParent());
} catch (IOException ex) { } catch (IOException ex) {
Logger.getLogger(ImportHashDatabaseDialog.class.getName()).log(Level.SEVERE, "Failed to get path of selected database", ex); // NON-NLS Logger.getLogger(ImportHashDatabaseDialog.class.getName()).log(Level.SEVERE, "Failed to get path of selected database", ex); // NON-NLS
JOptionPane.showMessageDialog(this, lbWarningMsg.setText(Bundle.ImportHashDatabaseDialog_failedToGetDbPathMsg());
NbBundle.getMessage(this.getClass(),
"ImportHashDatabaseDialog.failedToGetDbPathMsg")); // NON-NLS
} }
} }
valid(); valid();
@ -470,7 +484,11 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
} }
@Messages({"ImportHashDatabaseDialog.createGlobalSet.failedMsg.text=Failed to store attribution details.", @Messages({"ImportHashDatabaseDialog.createGlobalSet.failedMsg.text=Failed to store attribution details.",
"ImportHashDatabaseDialog.createGlobalSet.failedTitle.text=Import hashdb error."}) "ImportHashDatabaseDialog.mustSelectHashDbFilePathMsg=Missing hash database file path.",
"ImportHashDatabaseDialog.hashDbDoesNotExistMsg=The selected hash database does not exist.",
"# {0} - selected file path",
"ImportHashDatabaseDialog.errorMessage.failedToOpenHashDbMsg=Failed to open hash database at {0}.",
})
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
// Note that the error handlers in this method call return without disposing of the // Note that the error handlers in this method call return without disposing of the
// dialog to allow the user to try again, if desired. // dialog to allow the user to try again, if desired.
@ -478,34 +496,22 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
// have valid file path // have valid file path
if (selectedFilePath.isEmpty()) { if (selectedFilePath.isEmpty()) {
JOptionPane.showMessageDialog(this, lbWarningMsg.setText(Bundle.ImportHashDatabaseDialog_mustSelectHashDbFilePathMsg());
NbBundle.getMessage(this.getClass(),
"ImportHashDatabaseDialog.mustSelectHashDbFilePathMsg"),
NbBundle.getMessage(this.getClass(),
"ImportHashDatabaseDialog.importHashDbErr"),
JOptionPane.ERROR_MESSAGE); // NON-NLS
return; return;
} }
File file = new File(selectedFilePath); File file = new File(selectedFilePath);
if (!file.exists()) { if (!file.exists()) {
JOptionPane.showMessageDialog(this, lbWarningMsg.setText(Bundle.ImportHashDatabaseDialog_hashDbDoesNotExistMsg());
NbBundle.getMessage(this.getClass(),
"ImportHashDatabaseDialog.hashDbDoesNotExistMsg"),
NbBundle.getMessage(this.getClass(),
"ImportHashDatabaseDialog.importHashDbErr"),
JOptionPane.ERROR_MESSAGE); // NON-NLS
return; return;
} }
// create global set // create global set
int globalSetID = -1; int globalSetID;
try { try {
globalSetID = createGlobalSet(); globalSetID = createGlobalSet();
} catch (EamDbException ex) { } catch (EamDbException ex) {
JOptionPane.showMessageDialog(this, LOGGER.log(Level.SEVERE, "Failed to create global set.", ex);
Bundle.ImportHashDatabaseDialog_createGlobalSet_failedMsg_text(), lbWarningMsg.setText(Bundle.ImportHashDatabaseDialog_createGlobalSet_failedMsg_text());
Bundle.ImportHashDatabaseDialog_createGlobalSet_failedTitle_text(),
JOptionPane.ERROR_MESSAGE);
return; return;
} }
@ -517,22 +523,18 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
knownStatus = TskData.FileKnown.BAD; knownStatus = TskData.FileKnown.BAD;
} }
String errorMessage = NbBundle.getMessage(this.getClass(), String errorMessage = Bundle.ImportHashDatabaseDialog_errorMessage_failedToOpenHashDbMsg(selectedFilePath);
"ImportHashDatabaseDialog.errorMessage.failedToOpenHashDbMsg", // Future, make UI handle more than the "FILES" type.
selectedFilePath); // NON-NLS EamArtifact.Type contentType = EamArtifact.getDefaultArtifactTypes().get(0); // get "FILES" type
try { try {
new ImportHashDatabaseWorker(selectedFilePath, knownStatus, globalSetID).execute(); // run in the background and close dialog
} catch (Throwable ex) { SwingUtilities.invokeLater(new ImportHashDatabaseWorker(selectedFilePath, knownStatus, globalSetID, contentType)::execute);
Logger.getLogger(ImportHashDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); dispose();
JOptionPane.showMessageDialog(this, } catch (EamDbException | UnknownHostException ex) {
ex.getMessage(), Logger.getLogger(ImportHashDatabaseDialog.class.getName()).log(Level.SEVERE, errorMessage, ex);
NbBundle.getMessage(this.getClass(), lbWarningMsg.setText(ex.getMessage());
"ImportHashDatabaseDialog.importHashDbErr"),
JOptionPane.ERROR_MESSAGE);
return;
} }
dispose();
}//GEN-LAST:event_okButtonActionPerformed }//GEN-LAST:event_okButtonActionPerformed
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
@ -560,37 +562,57 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
}//GEN-LAST:event_comboboxSourceOrganizationActionPerformed }//GEN-LAST:event_comboboxSourceOrganizationActionPerformed
@NbBundle.Messages({"ImportHashDatabaseDialog.ImportHashDatabaseWorker.displayName=Importing Hash Database"}) @NbBundle.Messages({"ImportHashDatabaseDialog.ImportHashDatabaseWorker.displayName=Importing Hash Database"})
private class ImportHashDatabaseWorker extends SwingWorker<Object, Void> { private class ImportHashDatabaseWorker extends SwingWorker<Void, Void> {
private final File file; private final File file;
private final TskData.FileKnown knownStatus; private final TskData.FileKnown knownStatus;
private final int globalSetID; private final int globalSetID;
private final ProgressHandle progress;
private final EamArtifact.Type contentType;
public ImportHashDatabaseWorker(String filename, TskData.FileKnown knownStatus, int globalSetID) throws EamDbException, UnknownHostException { public ImportHashDatabaseWorker(String filename, TskData.FileKnown knownStatus, int globalSetID, EamArtifact.Type contentType) throws EamDbException, UnknownHostException {
this.file = new File(filename); this.file = new File(filename);
this.knownStatus = knownStatus; this.knownStatus = knownStatus;
this.globalSetID = globalSetID; this.globalSetID = globalSetID;
this.contentType = contentType;
this.progress = ProgressHandle.createHandle(Bundle.ImportHashDatabaseDialog_ImportHashDatabaseWorker_displayName());
if (!EamDb.isEnabled()) { if (!EamDb.isEnabled()) {
throw new EamDbException("Central Repository database settings were not properly initialized"); // NON-NLS throw new EamDbException("Central Repository database is not enabled."); // NON-NLS
} }
} }
@Override @Override
protected Object doInBackground() throws Exception { protected Void doInBackground() throws Exception {
ProgressHandle progress = ProgressHandle.createHandle(Bundle.ImportHashDatabaseDialog_ImportHashDatabaseWorker_displayName()); importHashDatabase();
importHashDatabase(progress);
return null; return null;
} }
@Override
@Messages({"ImportHashDatabaseDialog.ImportHashDatabaseWorker.error=Failed to import hash database."})
protected void done() {
progress.finish();
try {
get();
} catch (InterruptedException | ExecutionException ex) {
Logger.getLogger(ImportHashDatabaseDialog.class.getName()).log(Level.SEVERE, Bundle.ImportHashDatabaseDialog_ImportHashDatabaseWorker_error(), ex);
MessageNotifyUtil.Notify.show(Bundle.ImportHashDatabaseDialog_ImportHashDatabaseWorker_error(),
ex.getMessage(),
MessageNotifyUtil.MessageType.ERROR);
}
}
private long numberOfLinesInFile(File f) throws IOException { private long numberOfLinesInFile(File f) throws IOException {
return Files.lines(f.toPath()).count(); return Files.lines(f.toPath()).count();
} }
private void importHashDatabase(ProgressHandle progress) throws EamDbException, IOException { @Messages({"# {0} - value content",
"ImportHashDatabaseDialog.ImportHashDatabaseWorker.duplicate=Duplicate value {0} found in import file."})
private void importHashDatabase() throws EamDbException, IOException {
BufferedReader reader = new BufferedReader(new FileReader(file)); BufferedReader reader = new BufferedReader(new FileReader(file));
String line; String line;
EamDb dbManager = EamDb.getInstance(); EamDb dbManager = EamDb.getInstance();
Set<EamGlobalFileInstance> globalInstances = new HashSet<>();
long totalLines = numberOfLinesInFile(file); long totalLines = numberOfLinesInFile(file);
if (totalLines <= Integer.MAX_VALUE) { if (totalLines <= Integer.MAX_VALUE) {
@ -616,11 +638,12 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
knownStatus, knownStatus,
""); "");
dbManager.prepareGlobalFileInstance(eamGlobalFileInstance); if (!globalInstances.add(eamGlobalFileInstance)) {
throw new EamDbException(Bundle.ImportHashDatabaseDialog_ImportHashDatabaseWorker_duplicate(parts[0])); // NON-NLS
}
} }
dbManager.bulkInsertGlobalFileInstances(); dbManager.bulkInsertGlobalFileInstances(globalInstances, contentType);
progress.finish();
} }
} }