Merge remote-tracking branch 'upstream/develop' into search_improvements

Conflicts:
	branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties
This commit is contained in:
millmanorama 2016-12-15 12:07:33 +01:00
commit e02ede09e0
124 changed files with 23488 additions and 69 deletions

View File

@ -37,16 +37,19 @@ import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.sleuthkit.autopsy.coreutils.Logger;
import java.util.logging.Level;
import org.openide.util.HelpCtx;
import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.ingest.IngestManager;
/**
* An action that opens an existing case.
*/
@ServiceProvider(service = CaseOpenAction.class)
public final class CaseOpenAction implements ActionListener {
public final class CaseOpenAction extends CallableSystemAction implements ActionListener {
private static final Logger logger = Logger.getLogger(CaseOpenAction.class.getName());
private static final String PROP_BASECASE = "LBL_BaseCase_PATH"; //NON-NLS
private static final long serialVersionUID = 1L;
private final JFileChooser fileChooser = new JFileChooser();
private final FileFilter caseMetadataFileFilter;
@ -140,4 +143,18 @@ public final class CaseOpenAction implements ActionListener {
}).start();
}
}
@Override
public void performAction() {
}
@Override
public String getName() {
return NbBundle.getMessage(CaseOpenAction.class, "CTL_OpenAction");
}
@Override
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
}

View File

@ -18,6 +18,9 @@
*/
package org.sleuthkit.autopsy.casemodule;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.swing.JPanel;
import java.util.ArrayList;
import java.util.Calendar;
@ -26,9 +29,12 @@ import java.util.UUID;
import javax.swing.filechooser.FileFilter;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
import org.sleuthkit.autopsy.corecomponentinterfaces.AutomatedIngestDataSourceProcessor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.DataSourceUtils;
/**
* A image file data source processor that implements the DataSourceProcessor
@ -36,8 +42,11 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
* wizard. It also provides a run method overload to allow it to be used
* independently of the wizard.
*/
@ServiceProvider(service = DataSourceProcessor.class)
public class ImageDSProcessor implements DataSourceProcessor {
@ServiceProviders(value={
@ServiceProvider(service=DataSourceProcessor.class),
@ServiceProvider(service=AutomatedIngestDataSourceProcessor.class)}
)
public class ImageDSProcessor implements AutomatedIngestDataSourceProcessor {
private final static String DATA_SOURCE_TYPE = NbBundle.getMessage(ImageDSProcessor.class, "ImageDSProcessor.dsType.text");
private static final List<String> allExt = new ArrayList<>();
@ -231,4 +240,45 @@ public class ImageDSProcessor implements DataSourceProcessor {
setDataSourceOptionsCalled = true;
}
private static boolean isAcceptedByFiler(File file, List<FileFilter> filters) {
for (FileFilter filter : filters) {
if (filter.accept(file)) {
return true;
}
}
return false;
}
@Override
public int canProcess(Path dataSourcePath) throws AutomatedIngestDataSourceProcessorException {
// check file extension for supported types
if (!isAcceptedByFiler(dataSourcePath.toFile(), filtersList)) {
return 0;
}
try {
// verify that the image has a file system that TSK can process
Case currentCase = Case.getCurrentCase();
if (!DataSourceUtils.imageHasFileSystem(dataSourcePath)) {
// image does not have a file system that TSK can process
return 0;
}
} catch (Exception ex) {
throw new AutomatedIngestDataSourceProcessorException("Exception inside canProcess() method", ex);
}
// able to process the data source
return 100;
}
@Override
public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) throws AutomatedIngestDataSourceProcessorException {
this.deviceId = deviceId;
this.imagePath = dataSourcePath.toString();
this.timeZone = Calendar.getInstance().getTimeZone().getID();
this.ignoreFatOrphanFiles = false;
setDataSourceOptionsCalled = true;
run(deviceId, dataSourcePath.toString(), timeZone, ignoreFatOrphanFiles, progressMonitor, callBack);
}
}

View File

@ -18,14 +18,19 @@
*/
package org.sleuthkit.autopsy.casemodule;
import java.io.File;
import java.nio.file.Path;
import java.util.Calendar;
import java.util.UUID;
import javax.swing.JPanel;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
import org.sleuthkit.autopsy.corecomponentinterfaces.AutomatedIngestDataSourceProcessor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.DriveUtils;
/**
* A local drive data source processor that implements the DataSourceProcessor
@ -33,8 +38,11 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
* wizard. It also provides a run method overload to allow it to be used
* independently of the wizard.
*/
@ServiceProvider(service = DataSourceProcessor.class)
public class LocalDiskDSProcessor implements DataSourceProcessor {
@ServiceProviders(value={
@ServiceProvider(service=DataSourceProcessor.class),
@ServiceProvider(service=AutomatedIngestDataSourceProcessor.class)}
)
public class LocalDiskDSProcessor implements AutomatedIngestDataSourceProcessor {
private static final String DATA_SOURCE_TYPE = NbBundle.getMessage(LocalDiskDSProcessor.class, "LocalDiskDSProcessor.dsType.text");
private final LocalDiskPanel configPanel;
@ -210,4 +218,35 @@ public class LocalDiskDSProcessor implements DataSourceProcessor {
setDataSourceOptionsCalled = true;
}
@Override
public int canProcess(Path dataSourcePath) throws AutomatedIngestDataSourceProcessorException {
// verify that the data source is not a file or a directory
File file = dataSourcePath.toFile();
// ELTODO this needs to be tested more. should I keep isDirectory or just test for isFile?
if (file.isFile() || file.isDirectory()) {
return 0;
}
// check whether data source is an existing disk or partition
// ELTODO this needs to be tested more. do these methods actually work correctly?
// or should I use PlatformUtil.getPhysicalDrives() and PlatformUtil.getPartitions() instead?
String path = dataSourcePath.toString();
if ( (DriveUtils.isPhysicalDrive(path) || DriveUtils.isPartition(path)) && DriveUtils.driveExists(path) ) {
return 90;
}
return 0;
}
@Override
public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) throws AutomatedIngestDataSourceProcessorException {
this.deviceId = deviceId;
this.drivePath = dataSourcePath.toString();
this.timeZone = Calendar.getInstance().getTimeZone().getID();
this.ignoreFatOrphanFiles = false;
setDataSourceOptionsCalled = true;
run(deviceId, drivePath, timeZone, ignoreFatOrphanFiles, progressMonitor, callBack);
}
}

View File

@ -53,6 +53,7 @@ final class LocalDiskPanel extends JPanel {
private static final Logger logger = Logger.getLogger(LocalDiskPanel.class.getName());
private static LocalDiskPanel instance;
private static final long serialVersionUID = 1L;
private List<LocalDisk> disks;
private LocalDiskModel model;
private boolean enableNext = false;

View File

@ -18,12 +18,15 @@
*/
package org.sleuthkit.autopsy.casemodule;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import javax.swing.JPanel;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
import org.sleuthkit.autopsy.corecomponentinterfaces.AutomatedIngestDataSourceProcessor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
@ -34,8 +37,11 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
* integration with the add data source wizard. It also provides a run method
* overload to allow it to be used independently of the wizard.
*/
@ServiceProvider(service = DataSourceProcessor.class)
public class LocalFilesDSProcessor implements DataSourceProcessor {
@ServiceProviders(value={
@ServiceProvider(service=DataSourceProcessor.class),
@ServiceProvider(service=AutomatedIngestDataSourceProcessor.class)}
)
public class LocalFilesDSProcessor implements AutomatedIngestDataSourceProcessor {
private static final String DATA_SOURCE_TYPE = NbBundle.getMessage(LocalFilesDSProcessor.class, "LocalFilesDSProcessor.dsType");
private final LocalFilesPanel configPanel;
@ -200,4 +206,18 @@ public class LocalFilesDSProcessor implements DataSourceProcessor {
setDataSourceOptionsCalled = true;
}
@Override
public int canProcess(Path dataSourcePath) throws AutomatedIngestDataSourceProcessorException {
// Local files DSP can process any file by simply adding it as a logical file.
// It should return lowest possible non-zero confidence level and be treated
// as the "option of last resort" for auto ingest purposes
return 1;
}
@Override
public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) throws AutomatedIngestDataSourceProcessorException {
this.localFilePaths = Arrays.asList(new String[]{dataSourcePath.toString()});
run(deviceId, deviceId, this.localFilePaths, progressMonitor, callBack);
}
}

View File

@ -40,6 +40,7 @@ import org.sleuthkit.autopsy.coreutils.PathValidator;
*/
final class LocalFilesPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final Set<File> currentFiles = new TreeSet<>(); //keep currents in a set to disallow duplicates per add
private boolean enableNext = false;
private static LocalFilesPanel instance;

View File

@ -59,12 +59,10 @@ public class StartupWindowProvider implements StartupWindowInterface {
= Lookup.getDefault().lookupAll(StartupWindowInterface.class);
int windowsCount = startupWindows.size();
if (windowsCount > 2) {
logger.log(Level.WARNING, "More than 2 (" + windowsCount + ") start up windows discovered, will use the first custom one"); //NON-NLS
} else if (windowsCount == 1) {
if (windowsCount == 1) {
startupWindowToUse = startupWindows.iterator().next();
logger.log(Level.INFO, "Will use the default startup window: " + startupWindowToUse.toString()); //NON-NLS
} else {
} else if (windowsCount == 2) {
//pick the non default one
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
while (it.hasNext()) {
@ -73,17 +71,25 @@ public class StartupWindowProvider implements StartupWindowInterface {
startupWindowToUse = window;
logger.log(Level.INFO, "Will use the custom startup window: " + startupWindowToUse.toString()); //NON-NLS
break;
}
}
} else {
// select first non-Autopsy start up window
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
while (it.hasNext()) {
StartupWindowInterface window = it.next();
if (!window.getClass().getCanonicalName().startsWith("org.sleuthkit.autopsy")) {
startupWindowToUse = window;
logger.log(Level.INFO, "Will use the custom startup window: " + startupWindowToUse.toString()); //NON-NLS
break;
}
}
}
if (startupWindowToUse == null) {
logger.log(Level.SEVERE, "Unexpected error, no custom startup window found, using the default"); //NON-NLS
logger.log(Level.SEVERE, "Unexpected error, no startup window chosen, using the default"); //NON-NLS
startupWindowToUse = new org.sleuthkit.autopsy.casemodule.StartupWindow();
}
}
}
}

View File

@ -38,7 +38,7 @@ public class RuntimeProperties {
*
* @param coreComponentsActive True or false.
*/
public static void setCoreComponentsActive(boolean coreComponentsActive) {
public synchronized static void setCoreComponentsActive(boolean coreComponentsActive) {
if (!coreComponentsActiveSet) {
RuntimeProperties.coreComponentsActive = coreComponentsActive;
coreComponentsActiveSet = true;
@ -56,7 +56,7 @@ public class RuntimeProperties {
*
* @return True or false.
*/
public static boolean coreComponentsAreActive() {
public synchronized static boolean coreComponentsAreActive() {
return coreComponentsActive;
}

View File

@ -0,0 +1,86 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.corecomponentinterfaces;
import java.nio.file.Path;
/**
* Interface implemented by DataSourceProcessors in order to be supported by
* automated ingest capability.
*
* @author elivis
*/
public interface AutomatedIngestDataSourceProcessor extends DataSourceProcessor {
/**
* Indicates whether the DataSourceProcessor is capable of processing the
* data source. Returns a confidence value. Method can throw an exception
* for a system level problem. The exception should not be thrown for an issue
* related to bad input data.
*
* @param dataSourcePath Path to the data source.
*
* @return Confidence value. Values between 0 and 100 are recommended. Zero
* or less means the data source is not supported by the
* DataSourceProcessor. Value of 100 indicates high certainty in
* being able to process the data source.
*
* @throws
* org.sleuthkit.autopsy.corecomponentinterfaces.AutomatedIngestDataSourceProcessor.AutomatedIngestDataSourceProcessorException
*/
int canProcess(Path dataSourcePath) throws AutomatedIngestDataSourceProcessorException;
/**
* Adds a data source to the case database using a background task in a
* separate thread by calling DataSourceProcessor.run() method. Returns as
* soon as the background task is started. The background task uses a
* callback object to signal task completion and return results. Method can
* throw an exception for a system level problem. The exception should not
* be thrown for an issue related to bad input data.
*
* @param deviceId An ASCII-printable identifier for the device
* associated with the data source that is intended
* to be unique across multiple cases (e.g., a UUID).
* @param dataSourcePath Path to the data source.
* @param progressMonitor Progress monitor that will be used by the
* background task to report progress.
* @param callBack Callback that will be used by the background task
* to return results.
*
* @throws
* org.sleuthkit.autopsy.corecomponentinterfaces.AutomatedIngestDataSourceProcessor.AutomatedIngestDataSourceProcessorException
*/
void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) throws AutomatedIngestDataSourceProcessorException;
/**
* A custom exception for the use of AutomatedIngestDataSourceProcessor.
*/
public class AutomatedIngestDataSourceProcessorException extends Exception {
private static final long serialVersionUID = 1L;
public AutomatedIngestDataSourceProcessorException(String message) {
super(message);
}
public AutomatedIngestDataSourceProcessorException(String message, Throwable cause) {
super(message, cause);
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.coreutils;
import org.sleuthkit.datamodel.SleuthkitJNI;
import java.io.IOException;
import java.nio.file.Path;
/**
* Utility methods for working with data sources.
*/
public class DataSourceUtils {
/**
* Calls TSK to determine whether a
* potential data source has a file system.
*
* @param dataSourcePath The path to the data source.
*
* @return True or false.
*
* @throws IOException if an error occurs while trying to determine if the
* data source has a file system.
*/
public static boolean imageHasFileSystem(Path dataSourcePath) throws IOException {
return SleuthkitJNI.isImageSupported(dataSourcePath.toString());
}
}

View File

@ -56,7 +56,7 @@ public class DriveUtils {
}
/**
* Determines whether or not a drive exists by eading the first byte and
* Determines whether or not a drive exists by reading the first byte and
* checking if it is a -1.
*
* @param path The path to test.

View File

@ -68,6 +68,12 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
private final HashMap<String, List<String>> existingMimeTypes = new HashMap<>();
private static final Logger LOGGER = Logger.getLogger(FileTypesByMimeType.class.getName());
private void removeListeners() {
deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
Case.removePropertyChangeListener(pcl);
}
/*
* The pcl is in the class because it has the easiest mechanisms to add
* and remove itself during its life cycles.
@ -75,9 +81,8 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())
// || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())
|| eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) {
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked out.
@ -92,6 +97,10 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
* Case is closed, do nothing.
*/
}
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
if (evt.getNewValue() == null) {
removeListeners();
}
}
};
@ -123,7 +132,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
allDistinctMimeTypesQuery.append(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()).append("))");
synchronized (existingMimeTypes) {
existingMimeTypes.clear();
}
if (skCase == null) {
@ -131,7 +139,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
}
try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery(allDistinctMimeTypesQuery.toString())) {
ResultSet resultSet = dbQuery.getResultSet();
synchronized (existingMimeTypes) {
while (resultSet.next()) {
final String mime_type = resultSet.getString("mime_type"); //NON-NLS
if (!mime_type.isEmpty()) {
@ -144,17 +151,19 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
}
}
}
}
} catch (TskCoreException | SQLException ex) {
LOGGER.log(Level.WARNING, "Unable to populate File Types by MIME Type tree view from DB: ", ex); //NON-NLS
LOGGER.log(Level.SEVERE, "Unable to populate File Types by MIME Type tree view from DB: ", ex); //NON-NLS
}
}
setChanged();
notifyObservers();
}
FileTypesByMimeType(SleuthkitCase skCase) {
IngestManager.getInstance().addIngestJobEventListener(pcl);
IngestManager.getInstance().addIngestModuleEventListener(pcl);
Case.addPropertyChangeListener(pcl);
this.skCase = skCase;
populateHashMap();
}
@ -178,6 +187,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
isEmptyMimeNode = true;
}
return isEmptyMimeNode;
}
/**

View File

@ -60,11 +60,12 @@ public final class IngestProgressSnapshotDialog extends JDialog {
super((Window) owner, TITLE, ModalityType.MODELESS);
if (shouldBeModal && owner instanceof JDialog) { // if called from a modal dialog, manipulate the parent be just under this in z order, and not modal.
final JDialog pseudoOwner = (JDialog) owner;
final ModalityType originalModality = pseudoOwner.getModalityType();
addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) { // Put it back to how it was before we manipulated it.
pseudoOwner.setVisible(false);
pseudoOwner.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
pseudoOwner.setModalityType(originalModality);
pseudoOwner.toFront();
pseudoOwner.setVisible(true);
}

View File

@ -66,7 +66,10 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane2" alignment="0" pref="789" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<Component id="jScrollPane2" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
@ -91,18 +94,20 @@
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="1" pref="1" max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="395" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="informationLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="informationSeparator" min="-2" pref="305" max="-2" attributes="0"/>
<Component id="informationLabel" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="309" max="-2" attributes="0"/>
</Group>
<Component id="ingestWarningLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="optionsLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="optionsSeparator" min="-2" pref="334" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
@ -130,17 +135,21 @@
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="optionsLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="optionsSeparator" min="-2" pref="324" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="70" max="-2" attributes="0"/>
<Component id="informationSeparator" min="-2" pref="305" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Component id="ingestWarningLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="23" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="25" max="-2" attributes="0"/>
<Component id="sendIngestMessagesCheckBox" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
@ -205,9 +214,9 @@
<Component id="addHashesToDatabaseButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="optionsLabel" min="-2" max="-2" attributes="0"/>
<Component id="optionsSeparator" min="-2" pref="6" max="-2" attributes="0"/>
<Component id="optionsSeparator" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="sendIngestMessagesCheckBox" min="-2" max="-2" attributes="0"/>

View File

@ -662,17 +662,18 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(1, 1, 1)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 395, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(informationLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(informationSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 305, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(ingestWarningLabel)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(informationLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(309, 309, 309))
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
.addComponent(optionsLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(optionsSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 334, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
@ -694,12 +695,15 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
.addGap(53, 53, 53)
.addComponent(hashDbNameLabel))))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(optionsLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(optionsSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 324, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addGap(70, 70, 70)
.addComponent(informationSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 305, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(23, 23, 23)
.addComponent(sendIngestMessagesCheckBox))))
.addGap(10, 10, 10)
.addComponent(ingestWarningLabel))
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(25, 25, 25)
.addComponent(sendIngestMessagesCheckBox)))
.addGap(50, 50, 50))))
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(hashDatabasesLabel)
@ -749,9 +753,9 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
.addComponent(indexButton)
.addComponent(addHashesToDatabaseButton))
.addGap(18, 18, 18)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(optionsLabel)
.addComponent(optionsSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 6, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(optionsSeparator, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(18, 18, 18)
.addComponent(sendIngestMessagesCheckBox)
.addGap(18, 18, 18)
@ -772,7 +776,9 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 789, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(jScrollPane2)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)

8
Experimental/build.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See harness/README in the NetBeans platform -->
<!-- for some information on what you could do (e.g. targets to override). -->
<!-- If you delete this file and reopen the project it will be recreated. -->
<project name="org.sleuthkit.autopsy.experimental" default="netbeans" basedir=".">
<description>Builds, tests, and runs the project org.sleuthkit.autopsy.experimental.</description>
<import file="nbproject/build-impl.xml"/>
</project>

7
Experimental/manifest.mf Normal file
View File

@ -0,0 +1,7 @@
Manifest-Version: 1.0
AutoUpdate-Show-In-Client: true
OpenIDE-Module: org.sleuthkit.autopsy.experimental
OpenIDE-Module-Layer: org/sleuthkit/autopsy/experimental/autoingest/layer.xml
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties
OpenIDE-Module-Specification-Version: 1.0

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
*** GENERATED FROM project.xml - DO NOT EDIT ***
*** EDIT ../build.xml INSTEAD ***
-->
<project name="org.sleuthkit.autopsy.experimental-impl" basedir="..">
<fail message="Please build using Ant 1.7.1 or higher.">
<condition>
<not>
<antversion atleast="1.7.1"/>
</not>
</condition>
</fail>
<property file="nbproject/private/suite-private.properties"/>
<property file="nbproject/suite.properties"/>
<fail unless="suite.dir">You must set 'suite.dir' to point to your containing module suite</fail>
<property file="${suite.dir}/nbproject/private/platform-private.properties"/>
<property file="${suite.dir}/nbproject/platform.properties"/>
<macrodef name="property" uri="http://www.netbeans.org/ns/nb-module-project/2">
<attribute name="name"/>
<attribute name="value"/>
<sequential>
<property name="@{name}" value="${@{value}}"/>
</sequential>
</macrodef>
<macrodef name="evalprops" uri="http://www.netbeans.org/ns/nb-module-project/2">
<attribute name="property"/>
<attribute name="value"/>
<sequential>
<property name="@{property}" value="@{value}"/>
</sequential>
</macrodef>
<property file="${user.properties.file}"/>
<nbmproject2:property name="harness.dir" value="nbplatform.${nbplatform.active}.harness.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
<nbmproject2:property name="nbplatform.active.dir" value="nbplatform.${nbplatform.active}.netbeans.dest.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
<nbmproject2:evalprops property="cluster.path.evaluated" value="${cluster.path}" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
<fail message="Path to 'platform' cluster missing in $${cluster.path} property or using corrupt Netbeans Platform (missing harness).">
<condition>
<not>
<contains string="${cluster.path.evaluated}" substring="platform"/>
</not>
</condition>
</fail>
<import file="${harness.dir}/build.xml"/>
</project>

View File

@ -0,0 +1,17 @@
file.reference.c3p0-0.9.5.jar=release/modules/ext/c3p0-0.9.5.jar
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar
file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar
file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar
file.reference.jackson-core-2.7.0.jar=release/modules/ext/jackson-core-2.7.0.jar
file.reference.LGoodDatePicker-4.3.1.jar=release/modules/ext/LGoodDatePicker-4.3.1.jar
file.reference.mchange-commons-java-0.2.9.jar=release/modules/ext/mchange-commons-java-0.2.9.jar
file.reference.solr-solrj-4.9.1.jar=release/modules/ext/solr-solrj-4.9.1.jar
file.reference.tika-core-1.5.jar=release/modules/ext/tika-core-1.5.jar
file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar
javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial
javadoc.reference.LGoodDatePicker-4.3.1.jar=release/modules/ext/LGoodDatePicker-4.3.1-javadoc.jar
javadoc.reference.solr-solrj-4.9.1.jar=release/modules/ext/solr-solrj-4.9.1-javadoc.jar
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
source.reference.LGoodDatePicker-4.3.1.jar=release/modules/ext/LGoodDatePicker-4.3.1-sources.jar
source.reference.solr-solrj-4.9.1.jar=release/modules/ext/solr-solrj-4.9.1-sources.jar

View File

@ -0,0 +1,184 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.apisupport.project</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
<code-name-base>org.sleuthkit.autopsy.experimental</code-name-base>
<suite-component/>
<module-dependencies>
<dependency>
<code-name-base>org.netbeans.modules.options.api</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
<specification-version>1.44.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.awt</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>7.65.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.dialogs</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>7.41.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.filesystems</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>9.8.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.loaders</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>7.63.2</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.modules</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>7.47.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.util</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>9.5.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.util.lookup</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>8.32.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.util.ui</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>9.4.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.windows</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>6.74.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.sleuthkit.autopsy.core</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>10</release-version>
<specification-version>10.5</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.sleuthkit.autopsy.corelibs</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>3</release-version>
<specification-version>1.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.sleuthkit.autopsy.keywordsearch</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>6</release-version>
<specification-version>6.3</specification-version>
</run-dependency>
</dependency>
</module-dependencies>
<public-packages>
<package>org.sleuthkit.autopsy.experimental.autoingest</package>
<package>org.sleuthkit.autopsy.experimental.configuration</package>
</public-packages>
<class-path-extension>
<runtime-relative-path>ext/zookeeper-3.4.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/zookeeper-3.4.6.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/curator-client-2.8.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/curator-client-2.8.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/mchange-commons-java-0.2.9.jar</runtime-relative-path>
<binary-origin>release/modules/ext/mchange-commons-java-0.2.9.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/LGoodDatePicker-4.3.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/LGoodDatePicker-4.3.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/tika-core-1.5.jar</runtime-relative-path>
<binary-origin>release/modules/ext/tika-core-1.5.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/curator-recipes-2.8.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/curator-recipes-2.8.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jackson-core-2.7.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jackson-core-2.7.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/c3p0-0.9.5.jar</runtime-relative-path>
<binary-origin>release/modules/ext/c3p0-0.9.5.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/curator-framework-2.8.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/curator-framework-2.8.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/solr-solrj-4.9.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/solr-solrj-4.9.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/httpclient-4.3.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/httpclient-4.3.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/httpmime-4.3.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/httpmime-4.3.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/httpcore-4.3.jar</runtime-relative-path>
<binary-origin>release/modules/ext/httpcore-4.3.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/noggit-0.5.jar</runtime-relative-path>
<binary-origin>release/modules/ext/noggit-0.5.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/postgresql-9.4-1201-jdbc41.jar</runtime-relative-path>
<binary-origin>release/modules/ext/postgresql-9.4-1201-jdbc41.jar</binary-origin>
</class-path-extension>
</data>
</configuration>
</project>

View File

@ -0,0 +1 @@
suite.dir=${basedir}/..

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,277 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import net.sf.sevenzipjbinding.ISequentialOutStream;
import net.sf.sevenzipjbinding.ISevenZipInArchive;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
/**
* Set of utilities that handles archive file extraction. Uses 7zip library.
*/
final class ArchiveUtil {
static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // NON-NLS
private ArchiveUtil() {
}
/**
* Enum of mime types which support archive extraction
*/
private enum SupportedArchiveExtractionFormats {
ZIP("application/zip"), //NON-NLS
SEVENZ("application/x-7z-compressed"), //NON-NLS
GZIP("application/gzip"), //NON-NLS
XGZIP("application/x-gzip"), //NON-NLS
XBZIP2("application/x-bzip2"), //NON-NLS
XTAR("application/x-tar"), //NON-NLS
XGTAR("application/x-gtar"),
XRAR("application/x-rar-compressed"); //NON-NLS
private final String mimeType;
SupportedArchiveExtractionFormats(final String mimeType) {
this.mimeType = mimeType;
}
@Override
public String toString() {
return this.mimeType;
}
}
/**
* Exception thrown when archive handling resulted in an error
*/
static class ArchiveExtractionException extends Exception {
private static final long serialVersionUID = 1L;
ArchiveExtractionException(String message) {
super(message);
}
ArchiveExtractionException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* This method returns array of supported file extensions.
*
* @return String array of supported file extensions.
*/
static String[] getSupportedArchiveTypes(){
return SUPPORTED_EXTENSIONS;
}
/**
* This method returns true if the MIME type is currently supported. Else it
* returns false.
*
* @param mimeType File mime type
*
* @return This method returns true if the file format is currently
* supported. Else it returns false.
*/
static boolean isExtractionSupportedByMimeType(String mimeType) {
for (SupportedArchiveExtractionFormats s : SupportedArchiveExtractionFormats.values()) {
if (s.toString().equals(mimeType)) {
return true;
}
}
return false;
}
/**
* This method returns true if the file extension is currently supported.
* Else it returns false. Attempt extension based detection in case Apache
* Tika based detection fails.
*
* @param extension File extension
*
* @return This method returns true if the file format is currently
* supported. Else it returns false.
*/
static boolean isExtractionSupportedByFileExtension(String extension) {
// attempt extension matching
for (String supportedExtension : SUPPORTED_EXTENSIONS) {
if (extension.equals(supportedExtension)) {
return true;
}
}
return false;
}
/**
* Returns a list of file names contained within an archive.
*
* @param archiveFilePath Full path to the archive file
*
* @return List of file names contained within archive
*
* @throws
* ArchiveExtractionException
*/
static List<String> getListOfFilesWithinArchive(String archiveFilePath) throws ArchiveExtractionException {
if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) {
try {
SevenZip.initSevenZipFromPlatformJAR();
} catch (SevenZipNativeInitializationException ex) {
throw new ArchiveExtractionException("AutoIngestDashboard_bnPause_paused", ex);
}
}
List<String> files = new ArrayList<>();
ISevenZipInArchive inArchive = null;
try {
RandomAccessFile randomAccessFile = new RandomAccessFile(new File(archiveFilePath), "r");
inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile));
final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
files.add(item.getPath());
}
} catch (Exception ex) {
throw new ArchiveExtractionException("Exception while reading archive contents", ex);
} finally {
if (inArchive != null) {
try {
inArchive.close();
} catch (SevenZipException ex) {
throw new ArchiveExtractionException("Exception while closing the archive", ex);
}
}
}
return files;
}
/**
* Extracts contents of an archive file into a directory.
*
* @param archiveFilePath Full path to archive.
* @param destinationFolder Path to directory where results will be
* extracted to.
*
* @throws
* ArchiveExtractionException
*/
static void unpackArchiveFile(String archiveFilePath, String destinationFolder) throws ArchiveExtractionException {
if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) {
try {
SevenZip.initSevenZipFromPlatformJAR();
} catch (SevenZipNativeInitializationException ex) {
throw new ArchiveExtractionException("Unable to initialize 7Zip libraries", ex);
}
}
ISevenZipInArchive inArchive = null;
try {
RandomAccessFile randomAccessFile = new RandomAccessFile(new File(archiveFilePath), "r");
inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile));
final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
for (ISimpleInArchiveItem entry : simpleInArchive.getArchiveItems()) {
String entryPathInArchive = entry.getPath();
Path fullPath = Paths.get(destinationFolder, entryPathInArchive);
File destFile = new File(fullPath.toString());
File destinationParent = destFile.getParentFile();
destinationParent.mkdirs();
if (!entry.isFolder()) {
UnpackStream unpackStream = null;
try {
Long size = entry.getSize();
unpackStream = new UnpackStream(destFile.toString(), size);
entry.extractSlow(unpackStream);
} catch (Exception ex) {
throw new ArchiveExtractionException("Exception while unpacking archive contents", ex);
} finally {
if (unpackStream != null) {
unpackStream.close();
}
}
}
}
} catch (Exception ex) {
throw new ArchiveExtractionException("Exception while unpacking archive contents", ex);
} finally {
try {
if (inArchive != null) {
inArchive.close();
}
} catch (SevenZipException ex) {
throw new ArchiveExtractionException("Exception while closing the archive", ex);
}
}
}
/**
* Stream used to unpack an archive to local file
*/
private static class UnpackStream implements ISequentialOutStream {
private OutputStream output;
private String destFilePath;
UnpackStream(String destFilePath, long size) throws ArchiveExtractionException {
this.destFilePath = destFilePath;
try {
output = new FileOutputStream(destFilePath);
} catch (IOException ex) {
throw new ArchiveExtractionException("Exception while unpacking archive contents", ex);
}
}
@Override
public int write(byte[] bytes) throws SevenZipException {
try {
output.write(bytes);
} catch (IOException ex) {
throw new SevenZipException("Error writing unpacked file to " + destFilePath, ex);
}
return bytes.length;
}
public void close() throws ArchiveExtractionException {
if (output != null) {
try {
output.flush();
output.close();
} catch (IOException ex) {
throw new ArchiveExtractionException("Exception while closing the archive", ex);
}
}
}
}
}

View File

@ -0,0 +1,108 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.logging.Level;
/**
* Utility for creating and checking for the existence of an automated ingest
* alert file. The purpose of the file is to put a marker in the case directory
* when an error or warning occurs in connection with an automated ingest job.
*/
final class AutoIngestAlertFile {
private static final String ERROR_FILE_NAME = "autoingest.alert";
/**
* Checks whether an automated ingest alert file exists in a case directory.
*
* @param caseDirectoryPath The case directory path.
*
* @return True or false.
*/
static boolean exists(Path caseDirectoryPath) {
return caseDirectoryPath.resolve(ERROR_FILE_NAME).toFile().exists();
}
/**
* Creates an automated ingest alert file in a case directory if such a file
* does not already exist.
*
* @param caseDirectoryPath The case directory path.
*
* @return True or false.
*/
static void create(Path caseDirectoryPath) throws AutoIngestAlertFileException {
try {
Files.createFile(caseDirectoryPath.resolve(ERROR_FILE_NAME));
} catch (FileAlreadyExistsException ignored) {
/*
* The file already exists, the exception is not exceptional.
*/
} catch (IOException ex) {
/*
* FileAlreadyExistsException implementation is optional, so check
* for that case.
*/
if (!exists(caseDirectoryPath)) {
throw new AutoIngestAlertFileException(String.format("Error creating automated ingest alert file in %s", caseDirectoryPath), ex);
}
}
}
/**
* Exception thrown when there is a problem creating an alert file.
*/
final static class AutoIngestAlertFileException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs an exception to throw when there is a problem creating an
* alert file.
*
* @param message The exception message.
*/
private AutoIngestAlertFileException(String message) {
super(message);
}
/**
* Constructs an exception to throw when there is a problem creating an
* alert file.
*
* @param message The exception message.
* @param cause The cause of the exception, if it was an exception.
*/
private AutoIngestAlertFileException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* Prevents instantiation of this utility class.
*/
private AutoIngestAlertFile() {
}
}

View File

@ -0,0 +1,197 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Comparator;
import java.util.Date;
import java.util.Objects;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* A representation of a case created by automated ingest.
*/
class AutoIngestCase implements Comparable<AutoIngestCase> {
private static final Logger logger = Logger.getLogger(AutoIngestCase.class.getName());
private final Path caseDirectoryPath;
private final String caseName;
private final Path metadataFilePath;
private final Date createDate;
private Date lastModfiedDate;
/**
* Constructs a representation of case created by automated ingest.
*
* @param caseDirectoryPath The case directory path.
*/
// RJCTODO: Throw instead of reporting error, let client decide what to do.
AutoIngestCase(Path caseDirectoryPath) {
this.caseDirectoryPath = caseDirectoryPath;
caseName = PathUtils.caseNameFromCaseDirectoryPath(caseDirectoryPath);
metadataFilePath = caseDirectoryPath.resolve(caseName + CaseMetadata.getFileExtension());
BasicFileAttributes fileAttrs = null;
try {
fileAttrs = Files.readAttributes(metadataFilePath, BasicFileAttributes.class);
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error reading file attributes of case metadata file in %s, will use current time for case createDate/lastModfiedDate", caseDirectoryPath), ex);
}
if (null != fileAttrs) {
createDate = new Date(fileAttrs.creationTime().toMillis());
lastModfiedDate = new Date(fileAttrs.lastModifiedTime().toMillis());
} else {
createDate = new Date();
lastModfiedDate = new Date();
}
}
/**
* Gets the case directory path.
*
* @return The case directory path.
*/
Path getCaseDirectoryPath() {
return this.caseDirectoryPath;
}
/**
* Gets the case name.
*
* @return The case name.
*/
String getCaseName() {
return this.caseName;
}
/**
* Gets the creation date for the case, defined as the create time of the
* case metadata file.
*
* @return The case creation date.
*/
Date getCreationDate() {
return this.createDate;
}
/**
* Gets the last accessed date for the case, defined as the last modified
* time of the case metadata file.
*
* @return The last accessed date.
*/
// RJCTODO: Throw instead of reporting error, let client decide what to do.
Date getLastAccessedDate() {
try {
BasicFileAttributes fileAttrs = Files.readAttributes(metadataFilePath, BasicFileAttributes.class);
lastModfiedDate = new Date(fileAttrs.lastModifiedTime().toMillis());
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error reading file attributes of case metadata file in %s, lastModfiedDate time not updated", caseDirectoryPath), ex);
}
return lastModfiedDate;
}
/**
* Gets the status of this case based on the auto ingest result file in the
* case directory.
*
* @return See CaseStatus enum definition.
*/
CaseStatus getStatus() {
if (AutoIngestAlertFile.exists(caseDirectoryPath)) {
return CaseStatus.ALERT;
} else {
return CaseStatus.OK;
}
}
/**
* Indicates whether or not some other object is "equal to" this
* AutoIngestCase object.
*
* @param other The other object.
*
* @return True or false.
*/
@Override
public boolean equals(Object other) {
if (!(other instanceof AutoIngestCase)) {
return false;
}
if (other == this) {
return true;
}
return this.caseDirectoryPath.toString().equals(((AutoIngestCase) other).caseDirectoryPath.toString());
}
/**
* Returns a hash code value for this AutoIngestCase object.
*
* @return The has code.
*/
@Override
public int hashCode() {
int hash = 7;
hash = 71 * hash + Objects.hashCode(this.caseDirectoryPath);
hash = 71 * hash + Objects.hashCode(this.createDate);
hash = 71 * hash + Objects.hashCode(this.caseName);
return hash;
}
/**
* Compares this AutopIngestCase object with abnother AutoIngestCase object
* for order.
*/
@Override
public int compareTo(AutoIngestCase other) {
return -this.lastModfiedDate.compareTo(other.getLastAccessedDate());
}
/**
* Comparator for a descending order sort on date created.
*/
static class LastAccessedDateDescendingComparator implements Comparator<AutoIngestCase> {
/**
* Compares two AutoIngestCase objects for order based on last accessed
* date (descending).
*
* @param object The first AutoIngestCase object
* @param otherObject The second AuotIngestCase object.
*
* @return A negative integer, zero, or a positive integer as the first
* argument is less than, equal to, or greater than the second.
*/
@Override
public int compare(AutoIngestCase object, AutoIngestCase otherObject) {
return -object.getLastAccessedDate().compareTo(otherObject.getLastAccessedDate());
}
}
enum CaseStatus {
OK,
ALERT
}
}

View File

@ -0,0 +1,66 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.Serializable;
import javax.annotation.concurrent.Immutable;
import org.sleuthkit.autopsy.events.AutopsyEvent;
/**
* Event published when a case is deleted by the automated ingest manager.
*/
@Immutable
final class AutoIngestCaseDeletedEvent extends AutopsyEvent implements Serializable {
private static final long serialVersionUID = 1L;
private final String caseName;
private final String nodeName;
/**
* Constructs an event that is published when a case is deleted by the
* automated ingest manager.
*
* @param caseName The case name.
* @param nodeName The host name of the node that deleted the case.
*/
AutoIngestCaseDeletedEvent(String caseName, String nodeName) {
super(AutoIngestManager.Event.CASE_DELETED.toString(), null, null);
this.caseName = caseName;
this.nodeName = nodeName;
}
/**
* RJCTODO
*
* @return
*/
String getCaseName() {
return caseName;
}
/**
* RJCTODO
*
* @return
*/
String getNodeName() {
return nodeName;
}
}

View File

@ -0,0 +1,99 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction;
import org.openide.util.actions.SystemAction;
import org.sleuthkit.autopsy.casemodule.CaseCloseAction;
import org.sleuthkit.autopsy.casemodule.CaseOpenAction;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.StartupWindowProvider;
import org.sleuthkit.autopsy.experimental.configuration.AutoIngestUserPreferences;
final class AutoIngestCaseOpenAction extends CallableSystemAction implements ActionListener {
private static final Logger logger = Logger.getLogger(AutoIngestCaseOpenAction.class.getName());
private static final long serialVersionUID = 1L;
public AutoIngestCaseOpenAction() {
}
@Override
public void actionPerformed(ActionEvent e) {
AutoIngestUserPreferences.SelectedMode mode = AutoIngestUserPreferences.getMode();
switch (mode) {
case REVIEW:
if (Case.isCaseOpen()) {
/*
* In review mode, close the currently open case, if any, and
* then display the review mode cases panel. This can be
* accomplished by invoking CaseCloseAction because it calls
* StartupWindowProvider.getInstance().open() after it closes
* the current case.
*/
SystemAction.get(CaseCloseAction.class).actionPerformed(e);
} else {
// no case is open, so show the startup window
StartupWindowProvider.getInstance().open();
}
break;
case AUTOMATED:
/*
* New case action is disabled in auto ingest mode.
*/
break;
case STANDALONE:
/**
* In standalone mode, invoke default Autopsy version of CaseOpenAction.
*/
Lookup.getDefault().lookup(CaseOpenAction.class).actionPerformed(e);
break;
default:
logger.log(Level.SEVERE, "Attempting to open case in unsupported mode {0}", mode.toString());
}
}
@Override
public void performAction() {
}
@Override
public String getName() {
return NbBundle.getMessage(AutoIngestCaseOpenAction.class, "CTL_OpenAction");
}
@Override
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
}

View File

@ -0,0 +1,65 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.Serializable;
import org.sleuthkit.autopsy.events.AutopsyEvent;
/**
* Event published when an automated ingest manager prioritizes all or part of a
* case.
*/
public final class AutoIngestCasePrioritizedEvent extends AutopsyEvent implements Serializable { // RJCTODO: Rename to AutoIngestPrioritizationEvent
private static final long serialVersionUID = 1L;
private final String caseName;
private final String nodeName;
/**
* Constructs an event published when an automated ingest manager
* prioritizes all or part of a case.
*
* @param caseName The name of the case.
* @param nodeName The host name of the node that prioritized the case.
*/
public AutoIngestCasePrioritizedEvent(String nodeName, String caseName) {
super(AutoIngestManager.Event.CASE_PRIORITIZED.toString(), null, null);
this.caseName = caseName;
this.nodeName = nodeName;
}
/**
* Gets the name of the prioritized case.
*
* @return The case name.
*/
public String getCaseName() {
return caseName;
}
/**
* Gets the host name of the node that prioritized the case.
*
* @return The host name of the node.
*/
public String getNodeName() {
return nodeName;
}
}

View File

@ -0,0 +1,475 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="1" attributes="0">
<Component id="lbPending" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="pendingScrollPane" min="-2" pref="920" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="bnPrioritizeCase" max="32767" attributes="0"/>
<Component id="bnPrioritizeJob" max="32767" attributes="0"/>
</Group>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="bnPause" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
<Component id="bnRefresh" linkSize="1" min="-2" pref="100" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
<Component id="bnOptions" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
<Component id="bnOpenLogDir" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
<Component id="bnExit" linkSize="1" min="-2" pref="94" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="runningScrollPane" min="-2" pref="920" max="-2" attributes="0"/>
<Component id="completedScrollPane" min="-2" pref="920" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="bnCancelJob" linkSize="1" pref="117" max="32767" attributes="0"/>
<Component id="bnShowProgress" linkSize="1" pref="116" max="32767" attributes="0"/>
<Component id="bnCancelModule" linkSize="1" alignment="0" pref="117" max="32767" attributes="0"/>
<Component id="bnDeleteCase" linkSize="1" alignment="0" pref="117" max="32767" attributes="0"/>
<Component id="bnShowCaseLog" max="32767" attributes="0"/>
<Component id="bnReprocessJob" alignment="0" max="32767" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="lbStatus" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="tbStatusMessage" min="-2" pref="861" max="-2" attributes="0"/>
</Group>
<Component id="lbCompleted" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbRunning" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="lbServicesStatus" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="tbServicesStatusMessage" min="-2" pref="861" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbStatus" alignment="3" min="-2" pref="23" max="-2" attributes="0"/>
<Component id="tbStatusMessage" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbServicesStatus" alignment="3" min="-2" pref="23" max="-2" attributes="0"/>
<Component id="tbServicesStatusMessage" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="lbPending" min="-2" pref="23" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="pendingScrollPane" min="-2" pref="215" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="82" max="-2" attributes="0"/>
<Component id="bnPrioritizeCase" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnPrioritizeJob" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="lbRunning" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="34" max="-2" attributes="0"/>
<Component id="bnShowProgress" linkSize="2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnCancelJob" linkSize="2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnCancelModule" linkSize="2" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="runningScrollPane" min="-2" pref="133" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="68" max="-2" attributes="0"/>
<Component id="bnReprocessJob" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnDeleteCase" linkSize="2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnShowCaseLog" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="lbCompleted" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="completedScrollPane" min="-2" pref="179" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="103" alignment="0" groupAlignment="3" attributes="0">
<Component id="bnExit" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnOpenLogDir" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="bnPause" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnRefresh" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnOptions" linkSize="2" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="pendingScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="pendingTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="pendingTableModel" type="code"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.pendingTable.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="autoResizeMode" type="int" value="4"/>
<Property name="rowHeight" type="int" value="20" postCode="pendingTable.setSelectionModel(new DefaultListSelectionModel() {&#xa; private static final long serialVersionUID = 1L;&#xa; @Override&#xa; public void setSelectionInterval(int index0, int index1) {&#xa; if (index0 == pendingTable.getSelectedRow()) {&#xa; pendingTable.clearSelection();&#xa; } else {&#xa; super.setSelectionInterval(index0, index1);&#xa; }&#xa; }&#xa;});"/>
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
<JTableSelectionModel selectionMode="0"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="runningScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="runningTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="runningTableModel" type="code"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.runningTable.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="autoResizeMode" type="int" value="4"/>
<Property name="rowHeight" type="int" value="20" postCode="runningTable.setSelectionModel(new DefaultListSelectionModel() {&#xa; private static final long serialVersionUID = 1L;&#xa; @Override&#xa; public void setSelectionInterval(int index0, int index1) {&#xa; if (index0 == runningTable.getSelectedRow()) {&#xa; runningTable.clearSelection();&#xa; } else {&#xa; super.setSelectionInterval(index0, index1);&#xa; }&#xa; }&#xa;});"/>
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
<JTableSelectionModel selectionMode="0"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="completedScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="completedTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="completedTableModel" type="code"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.completedTable.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="autoResizeMode" type="int" value="4"/>
<Property name="rowHeight" type="int" value="20" postCode="completedTable.setSelectionModel(new DefaultListSelectionModel() {&#xa; private static final long serialVersionUID = 1L;&#xa; @Override&#xa; public void setSelectionInterval(int index0, int index1) {&#xa; if (index0 == completedTable.getSelectedRow()) {&#xa; completedTable.clearSelection();&#xa; } else {&#xa; super.setSelectionInterval(index0, index1);&#xa; }&#xa; }&#xa;});"/>
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
<JTableSelectionModel selectionMode="0"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="bnCancelJob">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnCancelJob.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnCancelJob.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnCancelJobActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnDeleteCase">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnDeleteCase.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnDeleteCase.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnDeleteCaseActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lbPending">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="14" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.lbPending.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbRunning">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="14" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.lbRunning.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbCompleted">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="14" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.lbCompleted.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnRefresh">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnRefresh.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnRefresh.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnRefreshActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnCancelModule">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnCancelModule.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnCancelModule.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnCancelModuleActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnExit">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnExit.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnExit.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnExitActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnOptions">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnOptions.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnOptions.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOptionsActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnShowProgress">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnShowProgress.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnShowProgress.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnShowProgressActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnPause">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPause.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPause.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnPauseActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnPrioritizeCase">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPrioritizeCase.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPrioritizeCase.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnPrioritizeCaseActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnShowCaseLog">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnShowCaseLog.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnShowCaseLog.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnShowCaseLogActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="tbStatusMessage">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="1"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.tbStatusMessage.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbStatus">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="14" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.lbStatus.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnPrioritizeJob">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPrioritizeJob.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPrioritizeJob.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPrioritizeJob.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnPrioritizeJobActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lbServicesStatus">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="14" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.lbServicesStatus.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbServicesStatusMessage">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="1"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.tbServicesStatusMessage.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnOpenLogDir">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnOpenLogDir.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOpenLogDirActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnReprocessJob">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnReprocessJob.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnReprocessJobActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,537 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.Serializable;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.Comparator;
import java.util.Date;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.ingest.IngestJob;
/**
* An automated ingest job for a manifest. The manifest specifies a co-located
* data source and a case to which the data source is to be added.
*/
@ThreadSafe
public final class AutoIngestJob implements Comparable<AutoIngestJob>, Serializable {
private static final long serialVersionUID = 1L;
private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName();
private final Manifest manifest;
private final String nodeName;
@GuardedBy("this")
private String caseDirectoryPath;
@GuardedBy("this")
private Integer priority;
@GuardedBy("this")
private Stage stage;
@GuardedBy("this")
private Date stageStartDate;
@GuardedBy("this")
transient private DataSourceProcessor dataSourceProcessor;
@GuardedBy("this")
transient private IngestJob ingestJob;
@GuardedBy("this")
transient private boolean cancelled; // RJCTODO: Document
@GuardedBy("this")
transient private boolean completed; // RJCTODO: Document
@GuardedBy("this")
private Date completedDate;
@GuardedBy("this")
private boolean errorsOccurred;
/**
* Constructs an automated ingest job for a manifest. The manifest specifies
* a co-located data source and a case to which the data source is to be
* added.
*
* @param manifest The manifest
* @param caseDirectoryPath The path to the case directory for the job, may
* be null.
* @param priority The priority of the job. The higher the number,
* the higher the priority.
* @param nodeName If the job is in progress, the node doing the
* processing, otherwise the locla host.
* @param stage The processing stage for display purposes.
* @param completedDate The date when the job was completed. Use the
* epoch (January 1, 1970, 00:00:00 GMT) to
* indicate the the job is not completed, i.e., new
* Date(0L).
*/
// RJCTODO: The null case directory is error-prone and the nodeName is confusing.
AutoIngestJob(Manifest manifest, Path caseDirectoryPath, int priority, String nodeName, Stage stage, Date completedDate, boolean errorsOccurred) {
this.manifest = manifest;
if (null != caseDirectoryPath) {
this.caseDirectoryPath = caseDirectoryPath.toString();
} else {
this.caseDirectoryPath = "";
}
this.priority = priority;
this.nodeName = nodeName;
this.stage = stage;
this.stageStartDate = manifest.getDateFileCreated();
this.completedDate = completedDate;
this.errorsOccurred = errorsOccurred;
}
/**
* Gets the auto ingest jobmanifest.
*
* @return The manifest.
*/
Manifest getManifest() {
return this.manifest;
}
/**
* Queries whether or not a case directory path has been set for this auto
* ingest job.
*
* @return True or false
*/
// RJCTODO: Use this or lose this
synchronized boolean hasCaseDirectoryPath() {
return (false == this.caseDirectoryPath.isEmpty());
}
/**
* Sets the path to the case directory of the case associated with this job.
*
* @param caseDirectoryPath The path to the case directory.
*/
synchronized void setCaseDirectoryPath(Path caseDirectoryPath) {
this.caseDirectoryPath = caseDirectoryPath.toString();
}
/**
* Gets the path to the case directory of the case associated with this job,
* may be null.
*
* @return The case directory path or null if the case directory has not
* been created yet.
*/
synchronized Path getCaseDirectoryPath() {
if (!caseDirectoryPath.isEmpty()) {
return Paths.get(caseDirectoryPath);
} else {
return null;
}
}
/**
* Sets the priority of the job. A higher number indicates a higher
* priority.
*
* @param priority The priority.
*/
synchronized void setPriority(Integer priority) {
this.priority = priority;
}
/**
* Gets the priority of the job. A higher number indicates a higher
* priority.
*
* @return The priority.
*/
synchronized Integer getPriority() {
return this.priority;
}
/**
* RJCTODO
*
* @param newStage
*/
synchronized void setStage(Stage newStage) {
setStage(newStage, Date.from(Instant.now()));
}
/**
* RJCTODO
*
* @param state
* @param stateStartedDate
*/
synchronized void setStage(Stage newState, Date stateStartedDate) {
if (Stage.CANCELLING == this.stage && Stage.COMPLETED != newState) {
return;
}
this.stage = newState;
this.stageStartDate = stateStartedDate;
}
/**
* RJCTODO:
*
* @return
*/
synchronized Stage getStage() {
return this.stage;
}
/**
* RJCTODO
*
* @return
*/
synchronized Date getStageStartDate() {
return this.stageStartDate;
}
/**
* RJCTODO
*
* @return
*/
synchronized StageDetails getStageDetails() {
String description;
Date startDate;
if (Stage.CANCELLING != this.stage && null != this.ingestJob) {
IngestJob.ProgressSnapshot progress = this.ingestJob.getSnapshot();
IngestJob.DataSourceIngestModuleHandle ingestModuleHandle = progress.runningDataSourceIngestModule();
if (null != ingestModuleHandle) {
/**
* A first or second stage data source level ingest module is
* running. Reporting this takes precedence over reporting
* generic file analysis.
*/
startDate = ingestModuleHandle.startTime();
if (!ingestModuleHandle.isCancelled()) {
description = ingestModuleHandle.displayName();
} else {
description = String.format(Stage.CANCELLING_MODULE.getDisplayText(), ingestModuleHandle.displayName()); // RJCTODO: FIx this
}
} else {
/**
* If no data source level ingest module is running, then either
* it is still the first stage of analysis and file level ingest
* modules are running or another ingest job is still running.
* Note that there can be multiple ingest jobs running in
* parallel. For example, there is an ingest job created to
* ingest each extracted virtual machine.
*/
description = Stage.ANALYZING_FILES.getDisplayText();
startDate = progress.fileIngestStartTime();
}
} else {
description = this.stage.getDisplayText();
startDate = this.stageStartDate;
}
return new StageDetails(description, startDate);
}
synchronized void setDataSourceProcessor(DataSourceProcessor dataSourceProcessor) {
this.dataSourceProcessor = dataSourceProcessor;
}
/**
* RJCTODO
*/
// RJCTODO: Consider moving this class into AIM and making this private
synchronized void setIngestJob(IngestJob ingestJob) {
this.ingestJob = ingestJob;
}
/**
* RJCTODO
*/
// RJCTODO: Consider moving this class into AIM and making this private.
// Or move the AID into a separate package. Or do not worry about it.
synchronized IngestJob getIngestJob() {
return this.ingestJob;
}
/**
* RJCTODO
*/
synchronized void cancel() {
setStage(Stage.CANCELLING);
cancelled = true;
errorsOccurred = true;
if (null != dataSourceProcessor) {
dataSourceProcessor.cancel();
}
if (null != ingestJob) {
ingestJob.cancel(IngestJob.CancellationReason.USER_CANCELLED);
}
}
/**
* RJCTODO
*/
synchronized boolean isCancelled() {
return cancelled;
}
/**
* RJCTODO
*/
synchronized void setCompleted() {
setStage(Stage.COMPLETED);
completed = true;
}
/**
* RJCTODO
*
* @return
*/
synchronized boolean isCompleted() {
return completed;
}
/**
* Sets the date the job was completed, with or without cancellation or
* errors.
*
* @param completedDate The completion date.
*/
synchronized void setCompletedDate(Date completedDate) {
this.completedDate = completedDate;
}
/**
* Gets the date the job was completed, with or without cancellation or
* errors.
*
* @return True or false.
*/
synchronized Date getCompletedDate() {
return completedDate; // RJCTODO: Consider returning null if == 0 (epoch)
}
/**
* Sets whether or not erros occurred during the processing of the job.
*
* @param errorsOccurred True or false;
*/
synchronized void setErrorsOccurred(boolean errorsOccurred) {
this.errorsOccurred = errorsOccurred;
}
/**
* Queries whether or not erros occurred during the processing of the job.
*
* @return True or false.
*/
synchronized boolean hasErrors() {
return this.errorsOccurred;
}
/**
* RJCTODO Gets name of the node associated with the job, possibly a remote
* hose if the job is in progress.
*
* @return The node name.
*/
String getNodeName() {
return nodeName;
}
/**
* RJCTODO
*
* @param obj
*
* @return
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof AutoIngestJob)) {
return false;
}
if (obj == this) {
return true;
}
return this.getManifest().getFilePath().equals(((AutoIngestJob) obj).getManifest().getFilePath());
}
/**
* RJCTODO
*
* @return
*/
@Override
public int hashCode() {
// RJCTODO: Update this
int hash = 7;
// hash = 71 * hash + Objects.hashCode(this.dateCreated);
return hash;
}
/**
* RJCTODO Default sorting is by ready file creation date, descending
*
* @param o
*
* @return
*/
@Override
public int compareTo(AutoIngestJob o) {
return -this.getManifest().getDateFileCreated().compareTo(o.getManifest().getDateFileCreated());
}
/**
* Custom comparator that allows us to sort List<AutoIngestJob> on reverse
* chronological date modified (descending)
*/
static class ReverseDateCompletedComparator implements Comparator<AutoIngestJob> {
/**
* RJCTODO
*
* @param o1
* @param o2
*
* @return
*/
@Override
public int compare(AutoIngestJob o1, AutoIngestJob o2) {
return -o1.getStageStartDate().compareTo(o2.getStageStartDate());
}
}
/**
* Comparator that sorts auto ingest jobs by priority in descending order.
*/
public static class PriorityComparator implements Comparator<AutoIngestJob> {
/**
* RJCTODO
*
* @param job
* @param anotherJob
*
* @return
*/
@Override
public int compare(AutoIngestJob job, AutoIngestJob anotherJob) {
return -(job.getPriority().compareTo(anotherJob.getPriority()));
}
}
/**
* Custom comparator that allows us to sort List<AutoIngestJob> on case name
* alphabetically except for jobs for the current host, which are placed at
* the top of the list.
*/
static class AlphabeticalComparator implements Comparator<AutoIngestJob> {
/**
* RJCTODO
*
* @param o1
* @param o2
*
* @return
*/
@Override
public int compare(AutoIngestJob o1, AutoIngestJob o2) {
if (o1.getNodeName().equalsIgnoreCase(LOCAL_HOST_NAME)) {
return -1; // o1 is for current case, float to top
} else if (o2.getNodeName().equalsIgnoreCase(LOCAL_HOST_NAME)) {
return 1; // o2 is for current case, float to top
} else {
return o1.getManifest().getCaseName().compareToIgnoreCase(o2.getManifest().getCaseName());
}
}
}
/**
* RJCTODO
*/
// RJCTODO: Combine this enum with StageDetails to make a single class.
enum Stage {
PENDING("Pending"),
STARTING("Starting"),
UPDATING_SHARED_CONFIG("Updating shared configuration"),
CHECKING_SERVICES("Checking services"),
OPENING_CASE("Opening case"),
IDENTIFYING_DATA_SOURCE("Identifying data source type"),
ADDING_DATA_SOURCE("Adding data source"),
ANALYZING_DATA_SOURCE("Analyzing data source"),
ANALYZING_FILES("Analyzing files"),
EXPORTING_FILES("Exporting files"),
CANCELLING_MODULE("Cancelling module"),
CANCELLING("Cancelling"),
COMPLETED("Completed");
private final String displayText;
private Stage(String displayText) {
this.displayText = displayText;
}
String getDisplayText() {
return displayText;
}
}
/**
* RJCTODO
*/
@Immutable
static final class StageDetails {
private final String description;
private final Date startDate;
/**
* RJCTODO
*
* @param description
* @param startDate
*/
private StageDetails(String description, Date startDate) {
this.description = description;
this.startDate = startDate;
}
/**
* RJCTODO
*
* @return
*/
String getDescription() {
return this.description;
}
/**
* RJCTODO
*
* @return
*/
Date getStartDate() {
return this.startDate;
}
}
}

View File

@ -0,0 +1,55 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.Serializable;
/**
* Event published when an automated ingest manager completes processing an
* automated ingest job.
*/
public final class AutoIngestJobCompletedEvent extends AutoIngestJobEvent implements Serializable {
private static final long serialVersionUID = 1L;
private final boolean retry;
/**
* Constructs an event published when an automated ingest manager completes
* processing an automated ingest job.
*
* @param job The completed job.
* @param shouldRetry Whether or not the job actually completed or needs to
* be attempted again.
*/
public AutoIngestJobCompletedEvent(AutoIngestJob job, boolean shouldRetry) {
super(AutoIngestManager.Event.JOB_COMPLETED, job);
this.retry = shouldRetry;
}
/**
* Queries whether or not the job actually completed or needs to be
* attempted again.
*
* @return True or false.
*/
public boolean shouldRetry() {
return this.retry;
}
}

View File

@ -0,0 +1,51 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.Serializable;
import javax.annotation.concurrent.Immutable;
import org.sleuthkit.autopsy.events.AutopsyEvent;
/**
* RJCTODO
*/
@Immutable
abstract class AutoIngestJobEvent extends AutopsyEvent implements Serializable {
private static final long serialVersionUID = 1L;
private final AutoIngestJob job;
/**
* RJCTODO
*
*/
AutoIngestJobEvent(AutoIngestManager.Event eventSubType, AutoIngestJob job) {
super(eventSubType.toString(), null, null);
this.job = job;
}
/**
* RJCTODO
* @return
*/
AutoIngestJob getJob() {
return this.job;
}
}

View File

@ -0,0 +1,478 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService.Lock;
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService.CoordinationServiceException;
import java.util.concurrent.TimeUnit;
import java.util.List;
import javax.annotation.concurrent.Immutable;
import org.sleuthkit.autopsy.ingest.IngestModuleError;
import org.sleuthkit.autopsy.ingest.IngestManager.IngestManagerException;
/**
* A logger for the processing of an auto ingest job by an auto ingest node. An
* exclusive coordination service lock on the log file is used to serialize
* access to it by each auto ingest node so that log entries do not become
* garbled.
* <p>
* Normally, the log messages are written to the case auto ingest log in the
* case directory. If there is an error writing to the log, the message is
* preserved by writing it to the auto ingest system log, along with the cause
* of the error.
*/
@Immutable
final class AutoIngestJobLogger {
private static final String LOG_FILE_NAME = "auto_ingest_log.txt";
private static final int LOCK_TIME_OUT = 15;
private static final TimeUnit LOCK_TIME_OUT_UNIT = TimeUnit.MINUTES;
private static final String DATE_FORMAT_STRING = "yyyy/MM/dd HH:mm:ss";
private static final SimpleDateFormat logDateFormat = new SimpleDateFormat(DATE_FORMAT_STRING);
private final Path manifestPath;
private final String manifestFileName;
private final String dataSourceFileName;
private final Path caseDirectoryPath;
private final String hostName;
/**
* Message category added to log messages to make searching for various
* classes of messages easier, e.g., to make error messages stand out.
*/
private enum MessageCategory {
/**
* Qualifies a log message about normal automated ingest processing.
*/
INFO,
/**
* Qualifies a log message about an unexpected event or condtion during
* automated ingest processing.
*/
WARNING,
/**
* Qualifies a log message about an error event or condition during
* automated ingest processing.
*/
ERROR
}
/**
* Gets the path to the automated ingest log for a case.
*
* @param caseDirectoryPath The path to the case directory where the log
* resides.
*
* @return The path to the automated ingest case log for the case.
*/
static Path getLogPath(Path caseDirectoryPath) {
return Paths.get(caseDirectoryPath.toString(), LOG_FILE_NAME);
}
/**
* Constructs a logger for the processing of an auto ingest job by an auto
* ingest node. The log messages are written to the case auto ingest log, a
* user-friendly log of of the automated processing for a case that resides
* in the case directory.
*
* The auto iongest log for a case is not intended to be a comprehensive.
* Advanced users doing troubleshooting of an automated ingest cluster
* should also consult the Autopsy and system logs as needed.
*
* @param manifestPath The manifest for the auto ingest job.
* @param caseDirectoryPath The case directory.
*/
AutoIngestJobLogger(Path manifestPath, String dataSourceFileName, Path caseDirectoryPath) {
this.manifestPath = manifestPath;
manifestFileName = manifestPath.getFileName().toString();
this.dataSourceFileName = dataSourceFileName;
this.caseDirectoryPath = caseDirectoryPath;
hostName = NetworkUtils.getLocalHostName();
}
/**
* Logs the cancellation of an auto ingest job during processing.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logJobCancelled() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.WARNING, "Auto ingest job cancelled during processing");
}
/**
* Logs the presence of a manifest file without a matching data source.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logMissingDataSource() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Data source file not found");
}
/**
* Logs a failure to extract an archived data source.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logFailedToExtractDataSource() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Failed to extract data source from archive");
}
/**
* Logs a failure to parse a Cellebrite logical report data source.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logFailedToParseLogicalReportDataSource() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Failed to parse Cellebrite logical report data source");
}
/**
* Logs a failure to identify data source processor for the data source.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logFailedToIdentifyDataSource() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, String.format("Failed to identify data source"));
}
/**
* Logs cancellation of the addition of a data source to the case database.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logDataSourceProcessorCancelled() throws AutoIngestJobLoggerException, InterruptedException { // RJCTODO: Is this used now?
log(MessageCategory.WARNING, "Cancelled adding data source to case");
}
/**
* Logs selection of a data source processor
* @param dsp Name of the data source processor
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logDataSourceProcessorSelected(String dsp) throws AutoIngestJobLoggerException, InterruptedException{
log(MessageCategory.INFO, "Using data source processor: " + dsp);
}
/**
* Logs the failure of the selected data source processor.
* @param dsp Name of the data source processor
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logDataSourceProcessorError(String dsp) throws AutoIngestJobLoggerException, InterruptedException{
log(MessageCategory.ERROR, "Error processing with data source processor: " + dsp);
}
/**
* Logs the addition of a data source to the case database.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logDataSourceAdded() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.INFO, "Added data source to case");
}
/**
* Logs an failure adding a data source to the case database.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logFailedToAddDataSource() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Failed to add data source to case");
}
/**
* Logs failure of a data source to produce content.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logNoDataSourceContent() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Data source failed to produce content");
}
/**
* Logs failure to analyze a data source due to ingest job settings errors.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logIngestJobSettingsErrors() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Failed to analyze data source due to settings errors");
}
/**
* Logs failure to analyze a data source due to ingest module startup
* errors.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logIngestModuleStartupErrors() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Failed to analyze data source due to ingest module startup errors");
}
/**
* Logs failure to analyze a data source because the analysis could not be
* started due to an ingest manager exception.
*
* @param ex The ingest manager exception.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logAnalysisStartupError() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Failed to analyze data source due to ingest job startup error");
}
/**
* Logs the completion of analysis of a data source by the ingest modules.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logAnalysisCompleted() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.INFO, "Analysis of data source completed");
}
/**
* Logs the cancellation of analysis of a data source by an individual
* ingest module.
*
* @param cancelledModuleName The display name of the cancelled ingest
* module.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logIngestModuleCancelled(String cancelledModuleName) throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.WARNING, String.format("%s analysis of data source cancelled", cancelledModuleName));
}
/**
* Logs the cancellation of analysis of a data source by the ingest modules.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logAnalysisCancelled() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.WARNING, "Analysis of data source cancelled");
}
/**
* Logs that automated file export is not enabled.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logFileExportDisabled() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.WARNING, "Automated file export is not enabled");
}
/**
* Logs completion of file export.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logFileExportCompleted() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.INFO, "Automated file export completed");
}
/**
* Logs failure to complete file export.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logFileExportError() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Error exporting files");
}
/**
* Logs discovery of a crashed auto ingest job for which recovery will be
* attempted.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logCrashRecoveryWithRetry() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Detected crash while processing, reprocessing");
}
/**
* Logs discovery of a crashed auto ingest job for which recovery will not
* be attempted because the retry limit for the job has been reached.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
void logCrashRecoveryNoRetry() throws AutoIngestJobLoggerException, InterruptedException {
log(MessageCategory.ERROR, "Detected crash while processing, reached retry limit for processing");
}
/**
* Writes a message to the case auto ingest log.
* <p>
* An exclusive coordination service lock on the log file is used to
* serialize access to the log file by each auto ingest node so that log
* entries do not become garbled.
*
* @param category The message category.
* @param message The message.
*
* @throws AutoIngestJobLoggerException if there is an error writing the log
* message.
* @throws InterruptedException if interrupted while blocked waiting
* to acquire an exclusive lock on the
* log file.
*/
private void log(MessageCategory category, String message) throws AutoIngestJobLoggerException, InterruptedException {
try (Lock lock = CoordinationService.getInstance(CoordinationServiceNamespace.getRoot()).tryGetExclusiveLock(CoordinationService.CategoryNode.CASES, getLogPath(caseDirectoryPath).toString(), LOCK_TIME_OUT, LOCK_TIME_OUT_UNIT)) {
if (null != lock) {
File logFile = getLogPath(caseDirectoryPath).toFile();
try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(logFile, logFile.exists())), true)) {
writer.println(String.format("%s %s: %s: %s: %-8s: %s", logDateFormat.format((Date.from(Instant.now()).getTime())), hostName, manifestFileName, dataSourceFileName, category.toString(), message));
} catch (IOException ex) {
throw new AutoIngestJobLoggerException(String.format("Failed to write case auto ingest log message (\"%s\") for %s", message, manifestPath), ex);
}
} else {
throw new AutoIngestJobLoggerException(String.format("Failed to write case auto ingest log message (\"%s\") for %s due to time out acquiring log lock", message, manifestPath));
}
} catch (CoordinationServiceException ex) {
throw new AutoIngestJobLoggerException(String.format("Failed to write case auto ingest log message (\"%s\") for %s", message, manifestPath), ex);
}
}
/**
* Exception thrown when there is a problem writing a log message.
*/
final static class AutoIngestJobLoggerException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs an exception to throw when there is a problem writing a
* log message.
*
* @param message The exception message.
*/
private AutoIngestJobLoggerException(String message) {
super(message);
}
/**
* Constructs an exception to throw when there is a problem writing a
* log message.
*
* @param message The exception message.
* @param cause The cause of the exception, if it was an exception.
*/
private AutoIngestJobLoggerException(String message, Throwable cause) {
super(message, cause);
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.Serializable;
/**
* Event published when auto ingest manager (AIM) starts processing an auto
* ingest job.
*/
public final class AutoIngestJobStartedEvent extends AutoIngestJobEvent implements Serializable {
private static final long serialVersionUID = 1L;
/**
* RJCTODO
*/
public AutoIngestJobStartedEvent(AutoIngestJob job) {
super(AutoIngestManager.Event.JOB_STARTED, job);
}
}

View File

@ -0,0 +1,38 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.Serializable;
/**
* Event published periodically when an automated ingest manager (AIM) is processing
* an automated ingest job.
*/
public final class AutoIngestJobStatusEvent extends AutoIngestJobEvent implements Serializable {
private static final long serialVersionUID = 1L;
/**
* RJCTODO
*/
public AutoIngestJobStatusEvent(AutoIngestJob job) {
super(AutoIngestManager.Event.JOB_STATUS_UPDATED, job);
}
}

View File

@ -0,0 +1,93 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Timestamp;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/**
* A logger for the auto ingest system log, a separate log from both the case
* auto ingest log and the application log.
*/
final class AutoIngestSystemLogger {
private static final int LOG_SIZE = 0; // In bytes, zero is unlimited
private static final int LOG_FILE_COUNT = 10;
private static final Logger LOGGER = Logger.getLogger("AutoIngest"); //NON-NLS
private static final String NEWLINE = System.lineSeparator();
@GuardedBy("AutoIngestSystemLogger")
private static boolean configured;
/**
* Gets a logger for the auto ingest system log, separate from both the case
* auto ingest log and the application log.
*
* @return The logger.
*/
synchronized final static Logger getLogger() {
if (!configured) {
Path logFilePath = Paths.get(PlatformUtil.getUserDirectory().getAbsolutePath(), "var", "log", "auto_ingest.log");
try {
FileHandler fileHandler = new FileHandler(logFilePath.toString(), LOG_SIZE, LOG_FILE_COUNT);
fileHandler.setEncoding(PlatformUtil.getLogFileEncoding());
fileHandler.setFormatter(new Formatter() {
@Override
public String format(LogRecord record) {
Throwable thrown = record.getThrown();
String stackTrace = ""; //NON-NLS
while (thrown != null) {
stackTrace += thrown.toString() + NEWLINE;
for (StackTraceElement traceElem : record.getThrown().getStackTrace()) {
stackTrace += "\t" + traceElem.toString() + NEWLINE; //NON-NLS
}
thrown = thrown.getCause();
}
return (new Timestamp(record.getMillis())).toString() + " " //NON-NLS
+ record.getSourceClassName() + " " //NON-NLS
+ record.getSourceMethodName() + NEWLINE
+ record.getLevel() + ": " //NON-NLS
+ this.formatMessage(record) + NEWLINE
+ stackTrace;
}
});
LOGGER.addHandler(fileHandler);
LOGGER.setUseParentHandlers(false);
} catch (SecurityException | IOException ex) {
throw new RuntimeException(String.format("Error initializing file handler for %s", logFilePath), ex); //NON-NLS
}
configured = true;
}
return LOGGER;
}
/**
* Prevents instantiation of this utility class.
*/
private AutoIngestSystemLogger() {
}
}

View File

@ -0,0 +1,122 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashMap;
import javax.annotation.concurrent.Immutable;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.openide.util.lookup.ServiceProvider;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
/**
* RJCTODO
*/
@Immutable
@ServiceProvider(service = ManifestFileParser.class)
public final class AutopsyManifestFileParser implements ManifestFileParser {
private static final String MANIFEST_FILE_NAME_SIGNATURE = "_Manifest.xml";
private static final String ROOT_ELEM_TAG_NAME = "Manifest";
private static final String CASE_NAME_XPATH = "/Manifest/Collection/Name/text()";
private static final String DEVICE_ID_XPATH = "/Manifest/Collection/Image/ID/text()";
private static final String DATA_SOURCE_NAME_XPATH = "/Manifest/Collection/Image/Name/text()";
/**
* RJCTODO
*
* @param filePath
*
* @return
*/
@Override
public boolean fileIsManifest(Path filePath) {
boolean fileIsManifest = false;
try {
Path fileName = filePath.getFileName();
if (fileName.toString().endsWith(MANIFEST_FILE_NAME_SIGNATURE)) {
Document doc = this.createManifestDOM(filePath);
Element docElement = doc.getDocumentElement();
fileIsManifest = docElement.getTagName().equals(ROOT_ELEM_TAG_NAME);
}
} catch (Exception unused) {
fileIsManifest = false;
}
return fileIsManifest;
}
/**
* RJCTODO
*
* @param filePath
*
* @return
*
* @throws org.sleuthkit.autopsy.experimental.autoingest.ManifestFileParser.ManifestFileParserException
*/
@Override
public Manifest parse(Path filePath) throws ManifestFileParserException {
if (!fileIsManifest(filePath)) {
throw new ManifestFileParserException(String.format("%s not recognized as a manifest", filePath));
}
try {
Document doc = this.createManifestDOM(filePath);
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xpath.compile(CASE_NAME_XPATH);
String caseName = (String) expr.evaluate(doc, XPathConstants.STRING);
expr = xpath.compile(DEVICE_ID_XPATH);
String deviceId = (String) expr.evaluate(doc, XPathConstants.STRING);
expr = xpath.compile(DATA_SOURCE_NAME_XPATH);
String dataSourceName = (String) expr.evaluate(doc, XPathConstants.STRING);
Path dataSourcePath = filePath.getParent().resolve(dataSourceName);
return new Manifest(filePath, caseName, deviceId, dataSourcePath, new HashMap<>());
} catch (Exception ex) {
throw new ManifestFileParserException(String.format("Error parsing manifest %s", filePath), ex);
}
}
/**
* RJCTODO
*
* @param manifestFilePath
*
* @return
*
* @throws ParserConfigurationException
* @throws SAXException
* @throws IOException
*/
private Document createManifestDOM(Path manifestFilePath) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
return docBuilder.parse(manifestFilePath.toFile());
}
}

View File

@ -0,0 +1,294 @@
CTL_OpenAction=Open Case...
AutoIngestDashboard.bnRefresh.text=&Refresh
AutoIngestDashboard.lbCompleted.text=Completed Jobs
AutoIngestDashboard.lbRunning.text=Running Jobs
AutoIngestDashboard.lbPending.text=Pending Jobs
AutoIngestDashboard.bnCancelModule.text=Cancel &Module
AutoIngestDashboard.bnExit.text=&Exit
AutoIngestDashboard.bnOptions.text=&Options
AutoIngestDashboard.JobsTableModel.ColumnHeader.Case=Case
AutoIngestDashboard.JobsTableModel.ColumnHeader.ImageFolder=Data Source
AutoIngestDashboard.JobsTableModel.ColumnHeader.HostName=Host Name
AutoIngestDashboard.JobsTableModel.ColumnHeader.CreatedTime=Job Created
AutoIngestDashboard.JobsTableModel.ColumnHeader.StartedTime=Stage Started
AutoIngestDashboard.JobsTableModel.ColumnHeader.CompletedTime=Job Completed
AutoIngestDashboard.JobsTableModel.ColumnHeader.Stage=Stage
AutoIngestDashboard.JobsTableModel.ColumnHeader.Status=Status
AutoIngestDashboard.JobsTableModel.ColumnHeader.ManifestFilePath= Manifest File Path
AutoIngestDashboard.bnShowProgress.text=Ingest Progress
AutoIngestDashboard.bnResume.text=Resume
AutoIngestDashboard.bnPause.text=Pause
AutoIngestDashboard.bnPause.confirmHeader=Are you sure you want to pause?
AutoIngestDashboard.bnPause.warningText=Pause will occur after the current job completes processing. This could take a long time. Continue?
AutoIngestDashboard.bnPause.toolTipText=Suspend processing of Pending Jobs
AutoIngestDashboard.bnPause.toolTipTextResume=Resume processing of Pending Jobs
AutoIngestDashboard.bnPause.pausing=Pausing after current job completes...
AutoIngestDashboard.bnRefresh.toolTipText=Refresh displayed tables
AutoIngestDashboard.bnShowProgress.toolTipText=Show the progress of the currently running Job. This functionality is only available for jobs running on current AIM node.
AutoIngestDashboard.bnCancelModule.toolTipText=Cancel processing of the current module within the Job and move on to the next module within the Job. This functionality is only available for jobs running on current AIM node.
AutoIngestDashboard.bnExit.toolTipText=Exit Application
AutoIngestDashboard.Cancelling=Cancelling...
AutoIngestDashboard.bnOptions.toolTipText=Display options panel. All processing must be paused to open the options panel.
AutoIngestDashboard.pendingTable.toolTipText=The Pending table displays the order upcoming Jobs will be processed with the top of the list first
AutoIngestDashboard.runningTable.toolTipText=The Running table displays the currently running Job and information about it
AutoIngestDashboard.completedTable.toolTipText=The Completed table shows all Jobs that have been processed already
AutoIngestDashboard.JobsTableModel.ColumnHeader.StageTime=Time in Stage
AutoIngestDashboard.JobsTableModel.ColumnHeader.CaseFolder=Case Folder
AutoIngestDashboard.JobsTableModel.ColumnHeader.LocalJob= Local Job?
AutoIngestDashboard.DeletionFailed=Deletion failed for job
AutoIngestDashboard.ShowLogFailed.Title=Unable to display case log
AutoIngestDashboard.ShowLogFailed.Message=Case log file does not exist
AutoIngestDashboard.bnPrioritizeCase.toolTipText=Move all images associated with a case to top of Pending queue.
AutoIngestDashboard.bnPrioritizeCase.text=Prioritize Case
AutoIngestDashboard.ExitConsequences=This will cancel any currently running job on this host. Exiting while a job is running potentially leaves the case in an inconsistent or corrupted state.
AutoIngestDashboard.ExitingStatus=Exiting...
AutoIngestDashboard.OK=OK
AutoIngestDashboard.Cancel=Cancel
AutoIngestDashboard.AutoIngestStartupFailed.Message=Failed to start automated ingest.\nPlease see auto ingest system log for details.
AutoIngestDashboard.AutoIngestStartupFailed.Title=Automated Ingest Error
AutoIngestDashboard.AutoIngestStartupError=Failed to start automated ingest. Verify Multi-user Settings.
AutoIngestDashboard.AutoIngestStartupWarning.Title=Automated Ingest Warning
AutoIngestDashboard.AutoIngestStartupWarning.Message=Failed to establish remote communications with other automated ingest nodes.\nAuto ingest dashboard will only be able to display local ingest job events.\nPlease verify Multi-User settings (Options->Multi-User). See application log for details.
AutoIngestDashboard.UpdatingSharedConfig=Updating shared configuration
AutoIngestDashboard.SharedConfigurationDisabled=Shared configuration disabled
AutoIngestDashboard.EnableConfigurationSettings=Enable shared configuration from the options panel before uploading
AutoIngestDashboard.ErrorUploadingConfiguration=Error uploading configuration
AutoIngestDashboard.UploadSuccessTitle=Success
AutoIngestDashboard.UploadSuccess=Shared configuration successfully uploaded
AutoIngestDashboard.UploadFailedTitle=Failed
AutoIngestDashboard.ConfigLocked=The shared configuration directory is locked because upload from another node is in progress. \nIf this is an error, you can unlock the directory and then retry the upload.
AutoIngestDashboard.ConfigLockedTitle=Configuration directory locked
AutoIngestDashboard.tbServicesStatusMessage.Message=Case databases {0}, keyword search {1}, coordination {2}, messaging {3}
AutoIngestDashboard.tbServicesStatusMessage.Message.Up=up
AutoIngestDashboard.tbServicesStatusMessage.Message.Down=down
AutoIngestDashboard.tbServicesStatusMessage.Message.Unknown=unknown
AutoIngestDashboard.PauseDueToSystemError=Paused due to system error, please consult the auto ingest system log
ConfirmationDialog.DoNotDelete=Do not delete
ConfirmationDialog.Delete=Permanently delete
ConfirmationDialog.DeleteAreYouSure=The entire case will be removed. Are you sure you want to delete case
ConfirmationDialog.ConfirmDeletion=Do you really want to cancel copy job
ConfirmationDialog.ConfirmDeletionHeader=Confirm Deletion
ConfirmationDialog.QuestionMark=?
ConfirmationDialog.DoNotCancelModule=Do Not Cancel Module
ConfirmationDialog.DoNotCancelJob=Do Not Cancel Job
ConfirmationDialog.DoNotCancel=Do not cancel
ConfirmationDialog.Cancel=Proceed with cancellation
ConfirmationDialog.CancelJob=Cancel Job
ConfirmationDialog.CancelModule=Cancel Module
ConfirmationDialog.CancelModuleAreYouSure=The analysis of the ingest module processing the current job will be canceled. Are you sure?
ConfirmationDialog.CancelJobAreYouSure=The currently running job will be canceled. Are you sure?
ConfirmationDialog.ConfirmCancellationHeader=Confirm Cancellation
ConfirmationDialog.Exit=Exit
ConfirmationDialog.DoNotExit=Do Not Exit
ConfirmationDialog.ConfirmExit=All incomplete copy jobs will be cancelled. Are you sure?
ConfirmationDialog.ConfirmExitHeader=Confirm Exit
OpenIDE-Module-Name=Experimental
ReviewModeCasePanel.bnRefresh.text=&Refresh
ReviewModeCasePanel.bnOpen.text=&Open
ReviewModeCasePanel.rbGroupLabel.text=Show Last 10:
ReviewModeCasePanel.rbDays.text=Days
ReviewModeCasePanel.rbWeeks.text=Weeks
ReviewModeCasePanel.rbMonths.text=Months
ReviewModeCasePanel.rbAllCases.text=Everything
ReviewModeCasePanel.cannotOpenCase=Cannot Open Case
ReviewModeCasePanel.casePathNotFound=Case path not found
ReviewModeCasePanel.caseIsLocked=Single-user case is locked.
DisplayLogDialog.cannotOpenLog=Unable to open the selected case log file
DisplayLogDialog.cannotFindLog=Unable to find the selected case log file
DisplayLogDialog.unableToShowLogFile=Unable to show log file
DisplayLogDialog.okay=Okay
ReviewModeCasePanel.bnShowLog.text=&Show Log
CopyFilesPanel.lbFrom.text=From Source
CopyFilesPanel.lbTo.text=Destination Case
CopyFilesPanel.bnCopy.text=&Copy
CopyFilesPanel.bnCancel.text=C&ancel
CopyFilesPanel.lbStatus.text=Status
CopyFilesPanel.tbCaseName.text=
CopyFilesPanel.tbSourceName.text=
CopyFilesPanel.bnCaseName.text=B&rowse
CopyFilesPanel.bnSourceName.text=&Browse
CopyFilesPanel.spRetryCount.toolTipText=Skip this copy job and move on to the next after failing this number of times.
CopyFilesPanel.rbExistingCase.text=
CopyFilesPanel.rbNewCase.text=
CopyFilesPanel.lbFailureText.text=failures
CopyFilesPanel.lpSkipAfterText.text=Skip after
CopyFilesPanel.lbExistingCase.text=Use Existing Case
CopyFilesPanel.lbNewCase.text=Create New Case
CopyFilesPanel.lbTitle.text=Schedule New Copy Job
CopyFilesPanel.confirmCancellation=Do you really want to cancel the copy operation?
CopyFilesPanel.confirmCopyHeader=Confirm Copy to Existing Case
CopyFilesPanel.confirmCopyAdd=exists. Do you really want to copy more files to this Case?
CopyFilesPanel.confirmCopyYes=Copy
CopyFilesPanel.confirmCopyNo=Do not copy
CopyFilesPanel.lbScheduledJobs.text=Scheduled Copy Jobs
CopyFilesPanel.lbRunningJob.text=Running Copy Job
CopyFilesPanel.lbCompletedJobs.text=Completed Copy Jobs
CopyFilesPanel.Source=Source
CopyFilesPanel.Destination=Destination
CopyFilesPanel.Initiated=Initiated
CopyFilesPanel.Started=Started
CopyFilesPanel.RunTime=Run Time
CopyFilesPanel.Status=State
CopyFilesPanel.tableCompletedJobs.toolTipText=Shows completed copy jobs and status
CopyFilesPanel.toolTipText=Shows the currently running copy job.
CopyFilesPanel.lbWait.text=Wait
CopyFilesPanel.lbWaitMinutesText.text=minutes before retrying
CopyFilesPanel.bnUp.text=
CopyFilesPanel.bnDown.text=
CopyFilesPanel.bnShowLog.text=&Show Log
CopyFilesPanel.tablePendingJobs.toolTipText=Scheduled copy jobs in the order they will complete, starting from the top.
CopyFilesPanel.lbPendingJobs.text=Scheduled Copy Jobs
CopyFilesPanel.chooseOrCreate=Please choose or create a Case
CopyFilesPanel.invalidCasePath=Invalid case path
CopyFilesPanel.empty=
CopyFilesPanel.failedToDeleteSome=Failed to delete some files in cancelled job
CopyFilesPanel.noRunningJob=No running copy job to cancel
CopyFilesPanel.destinationEmpty=Destination cannot be within Source
CopyFilesPanel.copyAborted=Copy aborted. No copying occurred.
CopyFilesPanel.copying=Copying...
CopyFilesPanel.copySuccess=Files successfully copied.
CopyFilesPanel.errorCopying=Error copying.
CopyFilesPanel.permissionDenied=Permission denied. Check permissions on source and destination directories.
CopyFilesPanel.retriesLeft=retries left. Waiting to retry.
CopyFilesPanel.noRetriesLeft=Error copying. All retries used. Skipping.
CopyFilesPanel.cancelled=Cancelled running copy job
CopyFilesPanel.deleteConfirmed=Copy job cancelled
CopyFilesPanel.deleteNotConfirmed=Copy job not cancelled
CopyFilesPanel.deleteUnable=Unable to cancel copy job
CopyFilesPanel.unableToOpenLogfile=Unable to open logfile
CopyFilesPanel.invalidSourcePath=Invalid source path
CopyFilesPanel.lbScheduledJobs.text=Scheduled Copy Jobs (Placeholder)
CopyFilesPanel.ConfirmCancellation=Do you really want to cancel the copy operation?
CopyFilesPanel.ConfirmCopyHeader=Confirm Copy to Existing Case
CopyFilesPanel.ConfirmCopyAdd=exists. Do you really want to copy more files to this Case?
CopyFilesPanel.ConfirmCopyYes=Copy
CopyFilesPanel.ConfirmCopyNo=Do not copy
ConfirmationDialog.ConfirmUnlockHeader=Confirm Case Unlock
ReviewModeCasePanel.bnShowLog.text=&Show Log
AutoIngestDashboard.bnPrioritizeCase.toolTipText=Move all images associated with a case to top of Pending queue.
AutoIngestDashboard.bnPrioritizeCase.text=Prioriti&ze Case
AutoIngestDashboard.bnShowCaseLog.toolTipText=Display case log file for selected case
AutoIngestDashboard.bnShowCaseLog.text=Show Case &Log
ReviewModeCasePanel.bnShowLog.toolTipText=Display case log file for selected case
CopyFilesPanel.bnCancelPendingJob.text=Ca&ncel
CopyFilesPanel.tbDestinationCase.text=
CopyFilesPanel.cbThrottleNetwork.text=&Throttle Network
CopyFilesPanel.cbThrottleNetwork.toolTipText=<html>Select this box if a low-bandwidth network connection is involved in this copy job.<br>\nSelecting this box will artificially limit the transfer speed by inserting strategic delays.<br>\nThis helps copy files across low-bandwidth networks where the transfer would<br>\notherwise fail. Only select this if you are having problems copying across the network.</html>
CopyFilesPanel.bnShowCurrentLog.text=Show &Log
CopyFilesPanel.bnShowCurrentLog.text=Show &Log
AutoIngestDashboard.bnCancelJob.toolTipText=Cancel processing of the current Job and move on to the next Job. This functionality is only available for jobs running on current AIM node.
AutoIngestDashboard.bnCancelJob.text=&Cancel Job
AutoIngestDashboard.bnDeleteCase.toolTipText=Delete the selected Case in its entirety
AutoIngestDashboard.bnDeleteCase.text=&Delete Case
CopyFilesPanel.lbCaseName.text=Case Name
CaseStatusIconCellRenderer.tooltiptext.ok=Images processed successfully
CaseStatusIconCellRenderer.tooltiptext.warning=An error occurred or processing was canceled for at least one image - please check the log
OptionsCategory_Name_Case_Import=Case Import
OptionsCategory_Keywords_Case_Import=Case Import Settings
CaseImportPanel.validationErrMsg.MUdisabled=Multi user settings must be enabled and saved
CaseImportPanel.validationErrMsg.AIMdisabled=Automated ingest cluster must be joined in Automated ingest mode
CaseImportPanel.ChooseCase=Choose a case to import
CaseImportPanel.ChooseSource=Choose source images
CaseImportPanel.DatabaseConnected=Database connected
CaseImportPanel.DatabaseNotConnected=Verify Multi-user database settings.
CaseImportPanel.ImportingCases=Importing case(s)...
CaseImportPanel.Cancelling=Cancelling
CaseImportPanel.CannotOpenLog=Unable to open log file
CaseImportPanel.BadCaseSourceFolder=Bad case source folder name
CaseImportPanel.BadCaseDestinationFolder=Bad case destination folder name
CaseImportPanel.BadImageSourceFolder=Bad image source folder name
CaseImportPanel.BadImageDestinationFolder=Bad image destination folder name
CaseImportPanel.Error=Error
CaseImportPanel.Complete=Complete
CaseImportPanel.Blank=
CaseImportPanel.DeleteWarning=Make sure no important files are in the case source directory
AutoIngestDashboard.lbStatus.text=Status:
SingleUserCaseImporter.NonUniqueOutputFolder=Output folder not unique. Skipping
SingleUserCaseImporter.WillImport=Will import:
SingleUserCaseImporter.None=None
SingleUserCaseImporter.WillNotImport=Will not import:
SingleUserCaseImporter.ContinueWithImport=Continue with import?
SingleUserCaseImporter.Cancelled=Cancelled
SingleUserCaseImporter.ImportedAsMultiUser=\nThis case was imported as a multi-user collaborative case on
SingleUserCaseImporter.StartingBatch=Starting batch processing of
SingleUserCaseImporter.to=to
SingleUserCaseImporter.ErrorFindingAutFiles=Error trying to find .aut files to import
SingleUserCaseImporter.StartedProcessing=Started processing
SingleUserCaseImporter.FinishedProcessing=Finished processing
SingleUserCaseImporter.FailedToComplete=Failed to complete processing of
SingleUserCaseImporter.CompletedBatch=Completed batch processing of
SingleUserCaseImporter.AbortingBatch=Aborting batch processing of
SingleUserCaseImporter.SourceImageMissing=. Source image missing for
ReviewModeCasePanel.CaseHeaderText=Case
ReviewModeCasePanel.CreatedTimeHeaderText=Created Time
ReviewModeCasePanel.StatusIconHeaderText=Status
ReviewModeCasePanel.OutputFolderHeaderText=Output Folder
ReviewModeCasePanel.LastAccessedTimeHeaderText=Last Accessed Time
CopyFilesPanel.bnOptions.text=&Options
AutoIngestDashboard.lbServicesStatus.text=Services Status:
AutoIngestDashboard.tbServicesStatusMessage.text=
AutoIngestDashboard.tbStatusMessage.text=
FileExporterSettingsPanel.ChooseRootDirectory=Choose a root directory for file output
FileExporterSettingsPanel.ChooseReportDirectory=Choose a report directory
FileExporterSettingsPanel.RuleName=Rule Name
FileExporterSettingsPanel.RootDirectory=Root Directory
FileExporterSettingsPanel.ReportDirectory=Report Directory
FileExporterSettingsPanel.AttributeValue=Value to compare against
FileExporterSettingsPanel.RuleListTooltip=Shows a list of rules.
FileExporterSettingsPanel.RuleOutputTooltip=This is the root output folder for saved output. You must have read and write access for this folder.
FileExporterSettingsPanel.MimetypeTooltip=Select the MIME Type to compare against.
FileExporterSettingsPanel.MimetypeText=MIME Type
FileExporterSettingsPanel.MimetypeCheckboxTooltip=Check this to filter based on MIME Type.
FileExporterSettingsPanel.FileSizeValueToolTip=Select the value to compare against.
FileExporterSettingsPanel.FileSizeUnitToolTip=Select the units of the file size value.
FileExporterSettingsPanel.FileSize=File Size
FileExporterSettingsPanel.FileSizeComparisonTooltip=Select the file size comparison operator.
FileExporterSettingsPanel.MimeTypeComparisonTooltip=Select the MIME Type comparison operator.
FileExporterSettingsPanel.CurrentlySelectedRuleNameTooltip=Shows the currently selected rule's name.
FileExporterSettingsPanel.SaveText=Save Rule
FileExporterSettingsPanel.SaveTooltip=Click to save the rule. There must be a rule name and at least one conditional (checkbox) enabled.
FileExporterSettingsPanel.BrowseText=Browse
FileExporterSettingsPanel.BrowseRootOutputFolder=Browse to set the root output folder.
FileExporterSettingsPanel.NewText=New Rule
FileExporterSettingsPanel.NewRuleTooltip=Clears the rule editor, allowing the user to enter a new rule.
FileExporterSettingsPanel.BrowseReportTooltip=Browse to set the report output folder.
FileExporterSettingsPanel.ReportOutputFolderTooltip=This is the report output folder. You must have read and write access for this folder.
FileExporterSettingsPanel.DeleteText=Delete Rule
FileExporterSettingsPanel.DeleteTooltip=Deletes the selected rule.
FileExporterSettingsPanel.UnsavedChangesLost=Unsaved rule changes will be lost. Continue?
FileExporterSettingsPanel.ChangesWillBeLost=Unsaved changes will be lost.
FileExporterSettingsPanel.DoYouWantToSave=Do you want to save?
FileExporterSettingsPanel.BadRootFolder=Bad root folder chosen.
FileExporterSettingsPanel.BadFolderForInterestingFileExport=Bad folder for file export
FileExporterSettingsPanel.BadReportFolder=Bad report folder chosen.
FileExporterSettingsPanel.ReallyDeleteRule=Really delete rule
FileExporterSettingsPanel.ReallyDeleteCondition=Really delete condition
FileExporterSettingsPanel.QuestionMark=?
FileExporterSettingsPanel.ConfirmRuleDeletion=Confirm rule deletion
FileExporterSettingsPanel.ConfirmClauseDeletion=Confirm clause deletion
FileExporterSettingsPanel.Title=File Export Rule Generator
FileExporterSettingsPanel.UnableToFindDirectory=Unable to find directory.
FileExporterSettingsPanel.PermissionsInvalid=Invalid folder permissions. Can not read or write.
FileExporterSettingsPanel.RuleNotSaved=Malformed rule. Rule not saved.
FileExporterSettingsPanel.MalformedRule=Malformed rule.
FileExporterSettingsPanel.FileSize_1=Select to include file size in the rule
FileExporterSettingsPanel.MimetypeCheckboxTooltip_1=Select to include MIME type in the rule
FileExporterSettingsPanel.MimeTypeComparisonTooltip_1=Select the conditional operator
FileExporterSettingsPanel.FileSizeComparisonTooltip_1=Select the conditional operator
FileExporterSettingsPanel.FileSizeValueToolTip_1=Select the desired file size
FileExporterSettingsPanel.FileSizeUnitToolTip_1=Select the desired file size units
FileExporterSettingsPanel.MimetypeTooltip_1=Select the desired MIME type
FileExporterSettingsPanel.CurrentlySelectedRuleNameTooltip_1=The name of the rule
FileExporterSettingsPanel.ReportOutputFolderTooltip_1=The Reports folder where a JSON report is written out for each exported file.
FileExporterSettingsPanel.RuleOutputTooltip_1=The Files folder where each matching file is written out
FileExporterSettingsPanel.BrowseRootOutputFolder_1=Browse for the Files Folder
FileExporterSettingsPanel.BrowseReportTooltip_1=Browse for the Reports Folder
FileExporterSettingsPanel.NewRuleTooltip_1=Clear the rule editor to begin a new rule
FileExporterSettingsPanel.DeleteTooltip_1=Delete the selected rule
FileExporterSettingsPanel.SaveTooltip_1=Save the current rule
AutoIngestDashboard.bnOpenLogDir.text=Open System Logs Directory
AutoIngestDashboard.bnPrioritizeJob.text=Prioritize Job
AutoIngestDashboard.bnPrioritizeJob.toolTipText=Move this folder to the top of the Pending queue.
AutoIngestDashboard.bnReprocessJob.text=Reprocess Job
AutoIngestDashboard.bnPrioritizeFolder.label=<AutoIngestDashboard.bnPrioritizeJob.text>
AutoIngestDashboard.bnPrioritizeJob.actionCommand=<AutoIngestDashboard.bnPrioritizeJob.text>

View File

@ -0,0 +1,476 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.8" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[830, 240]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="41" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lbImageSource" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="lbCaseSource" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="tbCaseSource" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnBrowseCaseSource" min="-2" pref="99" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="tbImageSource" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="tbInputNotification" min="-2" pref="527" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnBrowseImageSource" min="-2" pref="99" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="37" max="-2" attributes="0"/>
<Component id="lbCaption" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="35" max="-2" attributes="0"/>
<Component id="tbOops" min="-2" pref="465" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="1" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="tbOutputNotification" min="-2" pref="495" max="-2" attributes="0"/>
<Component id="tbBottomNotification" alignment="1" min="-2" pref="391" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lbCaseDestination" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="lbImageDestination" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="lbDbConnection" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="lbProgressBar" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="tbCaseDestination" alignment="0" max="32767" attributes="0"/>
<Component id="tbImageDestination" alignment="0" max="32767" attributes="0"/>
<Component id="pbShowProgress" alignment="1" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="bnStart" min="-2" pref="99" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnCancel" min="-2" pref="99" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="bnShowLog" min="-2" pref="99" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="cbCopyImages" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="picDbStatus" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="cbDeleteCase" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="tbDeleteWarning" min="-2" pref="478" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="11" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<EmptySpace min="-2" pref="105" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbCaption" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="tbOops" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="bnBrowseCaseSource" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="tbCaseSource" alignment="2" min="-2" pref="30" max="-2" attributes="0"/>
<Component id="lbCaseSource" alignment="2" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="bnBrowseImageSource" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="tbImageSource" alignment="2" min="-2" pref="30" max="-2" attributes="0"/>
<Component id="lbImageSource" alignment="2" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cbCopyImages" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="tbInputNotification" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cbDeleteCase" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="tbDeleteWarning" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="35" max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="tbCaseDestination" alignment="2" min="-2" pref="30" max="-2" attributes="0"/>
<Component id="lbCaseDestination" alignment="2" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="tbImageDestination" alignment="2" min="-2" pref="30" max="-2" attributes="0"/>
<Component id="lbImageDestination" alignment="2" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="tbOutputNotification" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="9" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbDbConnection" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="picDbStatus" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="pbShowProgress" alignment="2" min="-2" pref="26" max="-2" attributes="0"/>
<Component id="lbProgressBar" alignment="2" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="bnShowLog" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="bnStart" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnCancel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="tbBottomNotification" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="172" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="lbDbConnection">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Database"/>
<Property name="toolTipText" type="java.lang.String" value="Set database credentials via &apos;Options&apos;"/>
<Property name="verticalAlignment" type="int" value="3"/>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="picDbStatus">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
<ComponentRef name="lbDbConnection"/>
</Property>
<Property name="text" type="java.lang.String" value="Database Status"/>
<Property name="verticalAlignment" type="int" value="3"/>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbCaseDestination">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Case Destination"/>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbCaseSource">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
<ComponentRef name="lbCaseSource"/>
</Property>
<Property name="text" type="java.lang.String" value="Case Source"/>
<Property name="verticalAlignment" type="int" value="3"/>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbCaption">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="1"/>
</Property>
<Property name="text" type="java.lang.String" value="Import single-user cases to multi-user cases"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbImageDestination">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Image Destination"/>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbImageSource">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Image Source"/>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnStart">
<Properties>
<Property name="text" type="java.lang.String" value="Start"/>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnStartActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnCancel">
<Properties>
<Property name="text" type="java.lang.String" value="Cancel"/>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnCancelActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnShowLog">
<Properties>
<Property name="text" type="java.lang.String" value="Show Log"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnShowLogActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnBrowseCaseSource">
<Properties>
<Property name="text" type="java.lang.String" value="Browse"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnBrowseCaseSourceActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnBrowseImageSource">
<Properties>
<Property name="text" type="java.lang.String" value="Browse"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnBrowseImageSourceActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JProgressBar" name="pbShowProgress">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="1"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="focusable" type="boolean" value="false"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 16]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[146, 16]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_SerializeTo" type="java.lang.String" value="CaseConversionPanel_pbShowProgress"/>
</AuxValues>
</Component>
<Component class="javax.swing.JTextField" name="tbCaseSource">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value="Press &quot;Browse&quot; to select the case source folder."/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
<LineBorder roundedCorners="true">
<Color PropertyName="color" blue="99" green="99" red="99" type="rgb"/>
</LineBorder>
</Border>
</Property>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbCaseDestination">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value="The case destination folder. Press &quot;Options&quot; and edit &quot;Shared Results Folder&quot; to change this. Any imported cases will be stored in this folder."/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
<LineBorder roundedCorners="true">
<Color PropertyName="color" blue="99" green="99" red="99" type="rgb"/>
</LineBorder>
</Border>
</Property>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbImageDestination">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value="This is the Image folder. Press &quot;Options&quot; and edit &quot;Shared Images Folder&quot; to change this. Any input images will be copied to this folder during import."/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
<LineBorder roundedCorners="true">
<Color PropertyName="color" blue="99" green="99" red="99" type="rgb"/>
</LineBorder>
</Border>
</Property>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbBottomNotification">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="horizontalAlignment" type="int" value="4"/>
<Property name="text" type="java.lang.String" value="tbNotification"/>
<Property name="toolTipText" type="java.lang.String" value="Shows notifications"/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbImageSource">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value="Press &quot;Browse&quot; to select the image source folder."/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
<LineBorder roundedCorners="true">
<Color PropertyName="color" blue="99" green="99" red="99" type="rgb"/>
</LineBorder>
</Border>
</Property>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="cbCopyImages">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Copy images"/>
</Properties>
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="cbCopyImagesStateChanged"/>
<EventHandler event="propertyChange" listener="java.beans.PropertyChangeListener" parameters="java.beans.PropertyChangeEvent" handler="cbCopyImagesPropertyChange"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="cbDeleteCase">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Delete original case"/>
</Properties>
<Events>
<EventHandler event="propertyChange" listener="java.beans.PropertyChangeListener" parameters="java.beans.PropertyChangeEvent" handler="cbDeleteCasePropertyChange"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lbProgressBar">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Progress"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbInputNotification">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="horizontalAlignment" type="int" value="4"/>
<Property name="text" type="java.lang.String" value="Input box"/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbOutputNotification">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="horizontalAlignment" type="int" value="4"/>
<Property name="text" type="java.lang.String" value="Output box"/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbDeleteWarning">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" value="delete warning"/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbOops">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="tbOops" property="font" relativeSize="false" size="12"/>
</FontInfo>
</Property>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,787 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.EventQueue;
import java.awt.Font;
import java.io.File;
import java.nio.file.Paths;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JTextField;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.core.RuntimeProperties;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.core.UserPreferencesException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.CaseDbConnectionInfo;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import java.util.logging.Level;
import org.sleuthkit.autopsy.experimental.configuration.AutoIngestUserPreferences;
import static org.sleuthkit.autopsy.experimental.configuration.AutoIngestUserPreferences.SelectedMode.AUTOMATED;
/**
* This panel shows up in a tab pane next to the copy files panel for the
* automated ingest copy node.
*
*/
public class CaseImportPanel extends javax.swing.JPanel implements ImportDoneCallback {
private final CaseImportPanelController controller;
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(CaseImportPanel.class.getName());
private Thread ongoingImport; // used to interrupt thread if we need to
private final JFileChooser caseSourceFolderChooser = new JFileChooser();
private final JFileChooser imageSourceFolderChooser = new JFileChooser();
private CaseDbConnectionInfo db;
private final ImageIcon goodDatabaseCredentials;
private final ImageIcon badDatabaseCredentials;
private boolean canTalkToDb = false;
private boolean copyImagesState = true;
private boolean deleteImagesState = false;
private static final String MULTI_USER_SETTINGS_MUST_BE_ENABLED = NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.validationErrMsg.MUdisabled");
private static final String AIM_MUST_BE_ENABLED = NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.validationErrMsg.AIMdisabled");
// Used to specify which notification area should be upated
private enum NotificationLabel {
INPUT,
OUTPUT,
BOTTOM,
PROGRESS
}
/**
* Creates new panel CaseImportPanel
*/
public CaseImportPanel(CaseImportPanelController theController) {
controller = theController;
initComponents();
badDatabaseCredentials = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/warning16.png", false)); //NON-NLS
goodDatabaseCredentials = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/tick.png", false)); //NON-NLS
}
/**
* Validate current panel settings.
*/
boolean valid() {
// Nothing to validate for case import panel as far as Netbeans Tools/Options controller is concerned
return true;
}
/**
* Store current panel settings.
*/
void store() {
// Nothing to store for case import panel as far as Netbeans Tools/Options controller is concerned
}
/**
* Load data.
*/
final void load() {
// Multi user mode must be enabled. This is required to make sure database credentials are set.
// Also, "join auto ingest cluster" must be selected and we need to be in automated ingest mode.
// This is required to make sure "shared images" and "shared results" folders are set
if (!UserPreferences.getIsMultiUserModeEnabled()) {
tbOops.setText(MULTI_USER_SETTINGS_MUST_BE_ENABLED);
return;
} else if (RuntimeProperties.coreComponentsAreActive()) {
tbOops.setText(AIM_MUST_BE_ENABLED);
return;
} else {
tbOops.setText("");
}
// Note: we used to store input folders in persistent storage but it is not done any more for some reason...
caseSourceFolderChooser.setCurrentDirectory(caseSourceFolderChooser.getFileSystemView().getParentDirectory(new File("C:\\"))); //NON-NLS
caseSourceFolderChooser.setAcceptAllFileFilterUsed(false);
caseSourceFolderChooser.setDialogTitle(NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.ChooseCase"));
caseSourceFolderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
imageSourceFolderChooser.setCurrentDirectory(imageSourceFolderChooser.getFileSystemView().getParentDirectory(new File("C:\\"))); //NON-NLS
imageSourceFolderChooser.setAcceptAllFileFilterUsed(false);
imageSourceFolderChooser.setDialogTitle(NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.ChooseSource"));
imageSourceFolderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
cbCopyImages.setSelected(true);
cbDeleteCase.setSelected(false);
picDbStatus.setText(""); //NON-NLS
tbDeleteWarning.setText(""); //NON-NLS
tbInputNotification.setText(""); //NON-NLS
tbOutputNotification.setText(""); //NON-NLS
tbBottomNotification.setText(""); //NON-NLS
pbShowProgress.setStringPainted(true);
pbShowProgress.setForeground(new Color(51, 153, 255));
pbShowProgress.setString(""); //NON-NLS
showDbStatus();
handleAutoModeInputs();
}
void handleAutoModeInputs() {
String caseDestinationResult = "";
String output = AutoIngestUserPreferences.getAutoModeResultsFolder();
if (output.isEmpty() || !(new File(output).exists())) {
setNotificationText(NotificationLabel.OUTPUT, NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.BadCaseDestinationFolder"), false);
} else {
tbCaseDestination.setText(output);
caseDestinationResult = "";
}
String imageDestinationResult = "";
String imageFolder = AutoIngestUserPreferences.getAutoModeImageFolder();
if (imageFolder.isEmpty() || !(new File(imageFolder).exists())) {
setNotificationText(NotificationLabel.OUTPUT, NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.BadImageDestinationFolder"), false);
} else {
tbImageDestination.setText(imageFolder);
imageDestinationResult = "";
}
String result = caseDestinationResult;
if (result.isEmpty()) {
result = imageDestinationResult;
}
setNotificationText(NotificationLabel.OUTPUT, result, false);
}
/**
* Set status pictures to show if the database credentials are good or bad
*/
private void showDbStatus() {
try {
db = UserPreferences.getDatabaseConnectionInfo();
} catch (UserPreferencesException ex) {
logger.log(Level.SEVERE, "Error accessing case database connection info", ex); //NON-NLS
setDbConnectionStatus(false, badDatabaseCredentials, NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.DatabaseNotConnected"));
return;
}
try {
SleuthkitCase.tryConnect(db);
setDbConnectionStatus(true, goodDatabaseCredentials, NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.DatabaseConnected"));
} catch (TskCoreException ex) {
setDbConnectionStatus(false, badDatabaseCredentials, NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.DatabaseNotConnected"));
logger.log(Level.SEVERE, "Unable to communicate with PostgreSQL: {0}", ex.getMessage());
}
}
private void setDbConnectionStatus(boolean canConnect, ImageIcon credentials, String text) {
canTalkToDb = canConnect;
picDbStatus.setIcon(credentials);
picDbStatus.setText(text);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
lbDbConnection = new javax.swing.JLabel();
picDbStatus = new javax.swing.JLabel();
lbCaseDestination = new javax.swing.JLabel();
lbCaseSource = new javax.swing.JLabel();
lbCaption = new javax.swing.JLabel();
lbImageDestination = new javax.swing.JLabel();
lbImageSource = new javax.swing.JLabel();
bnStart = new javax.swing.JButton();
bnCancel = new javax.swing.JButton();
bnShowLog = new javax.swing.JButton();
bnBrowseCaseSource = new javax.swing.JButton();
bnBrowseImageSource = new javax.swing.JButton();
pbShowProgress = new javax.swing.JProgressBar();
tbCaseSource = new javax.swing.JTextField();
tbCaseDestination = new javax.swing.JTextField();
tbImageDestination = new javax.swing.JTextField();
tbBottomNotification = new javax.swing.JTextField();
tbImageSource = new javax.swing.JTextField();
cbCopyImages = new javax.swing.JCheckBox();
cbDeleteCase = new javax.swing.JCheckBox();
lbProgressBar = new javax.swing.JLabel();
tbInputNotification = new javax.swing.JTextField();
tbOutputNotification = new javax.swing.JTextField();
tbDeleteWarning = new javax.swing.JTextField();
tbOops = new javax.swing.JTextField();
setMinimumSize(new java.awt.Dimension(830, 240));
lbDbConnection.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
lbDbConnection.setText("Database");
lbDbConnection.setToolTipText("Set database credentials via 'Options'");
lbDbConnection.setVerticalAlignment(javax.swing.SwingConstants.BOTTOM);
lbDbConnection.setFocusable(false);
picDbStatus.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
picDbStatus.setLabelFor(lbDbConnection);
picDbStatus.setText("Database Status");
picDbStatus.setVerticalAlignment(javax.swing.SwingConstants.BOTTOM);
picDbStatus.setFocusable(false);
lbCaseDestination.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
lbCaseDestination.setText("Case Destination");
lbCaseDestination.setFocusable(false);
lbCaseSource.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
lbCaseSource.setLabelFor(lbCaseSource);
lbCaseSource.setText("Case Source");
lbCaseSource.setVerticalAlignment(javax.swing.SwingConstants.BOTTOM);
lbCaseSource.setFocusable(false);
lbCaption.setFont(new java.awt.Font("Tahoma", 1, 12)); // NOI18N
lbCaption.setText("Import single-user cases to multi-user cases");
lbImageDestination.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
lbImageDestination.setText("Image Destination");
lbImageDestination.setFocusable(false);
lbImageSource.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
lbImageSource.setText("Image Source");
lbImageSource.setFocusable(false);
bnStart.setText("Start");
bnStart.setEnabled(false);
bnStart.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnStartActionPerformed(evt);
}
});
bnCancel.setText("Cancel");
bnCancel.setEnabled(false);
bnCancel.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnCancelActionPerformed(evt);
}
});
bnShowLog.setText("Show Log");
bnShowLog.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnShowLogActionPerformed(evt);
}
});
bnBrowseCaseSource.setText("Browse");
bnBrowseCaseSource.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnBrowseCaseSourceActionPerformed(evt);
}
});
bnBrowseImageSource.setText("Browse");
bnBrowseImageSource.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnBrowseImageSourceActionPerformed(evt);
}
});
pbShowProgress.setFont(new java.awt.Font("Tahoma", 1, 12)); // NOI18N
pbShowProgress.setToolTipText("");
pbShowProgress.setFocusable(false);
pbShowProgress.setMaximumSize(new java.awt.Dimension(32767, 16));
pbShowProgress.setPreferredSize(new java.awt.Dimension(146, 16));
tbCaseSource.setEditable(false);
tbCaseSource.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
tbCaseSource.setToolTipText("Press \"Browse\" to select the case source folder.");
tbCaseSource.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(153, 153, 153), 1, true));
tbCaseSource.setFocusable(false);
tbCaseDestination.setEditable(false);
tbCaseDestination.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
tbCaseDestination.setToolTipText("The case destination folder. Press \"Options\" and edit \"Shared Results Folder\" to change this. Any imported cases will be stored in this folder.");
tbCaseDestination.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(153, 153, 153), 1, true));
tbCaseDestination.setFocusable(false);
tbImageDestination.setEditable(false);
tbImageDestination.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
tbImageDestination.setToolTipText("This is the Image folder. Press \"Options\" and edit \"Shared Images Folder\" to change this. Any input images will be copied to this folder during import.");
tbImageDestination.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(153, 153, 153), 1, true));
tbImageDestination.setFocusable(false);
tbBottomNotification.setEditable(false);
tbBottomNotification.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
tbBottomNotification.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
tbBottomNotification.setText("tbNotification");
tbBottomNotification.setToolTipText("Shows notifications");
tbBottomNotification.setBorder(null);
tbBottomNotification.setFocusable(false);
tbImageSource.setEditable(false);
tbImageSource.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
tbImageSource.setToolTipText("Press \"Browse\" to select the image source folder.");
tbImageSource.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(153, 153, 153), 1, true));
tbImageSource.setFocusable(false);
cbCopyImages.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
cbCopyImages.setText("Copy images");
cbCopyImages.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
cbCopyImagesStateChanged(evt);
}
});
cbCopyImages.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
public void propertyChange(java.beans.PropertyChangeEvent evt) {
cbCopyImagesPropertyChange(evt);
}
});
cbDeleteCase.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
cbDeleteCase.setText("Delete original case");
cbDeleteCase.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
public void propertyChange(java.beans.PropertyChangeEvent evt) {
cbDeleteCasePropertyChange(evt);
}
});
lbProgressBar.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
lbProgressBar.setText("Progress");
tbInputNotification.setEditable(false);
tbInputNotification.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
tbInputNotification.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
tbInputNotification.setText("Input box");
tbInputNotification.setBorder(null);
tbOutputNotification.setEditable(false);
tbOutputNotification.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
tbOutputNotification.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
tbOutputNotification.setText("Output box");
tbOutputNotification.setBorder(null);
tbDeleteWarning.setEditable(false);
tbDeleteWarning.setText("delete warning");
tbDeleteWarning.setBorder(null);
tbOops.setEditable(false);
tbOops.setFont(tbOops.getFont().deriveFont(tbOops.getFont().getStyle() | java.awt.Font.BOLD, 12));
tbOops.setForeground(new java.awt.Color(255, 0, 0));
tbOops.setBorder(null);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(41, 41, 41)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lbImageSource, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(lbCaseSource, javax.swing.GroupLayout.Alignment.TRAILING))
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(tbCaseSource)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(bnBrowseCaseSource, javax.swing.GroupLayout.PREFERRED_SIZE, 99, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(tbImageSource)
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addComponent(tbInputNotification, javax.swing.GroupLayout.PREFERRED_SIZE, 527, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(bnBrowseImageSource, javax.swing.GroupLayout.PREFERRED_SIZE, 99, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addGroup(layout.createSequentialGroup()
.addGap(37, 37, 37)
.addComponent(lbCaption)
.addGap(35, 35, 35)
.addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, 465, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(layout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(tbOutputNotification, javax.swing.GroupLayout.PREFERRED_SIZE, 495, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(tbBottomNotification, javax.swing.GroupLayout.PREFERRED_SIZE, 391, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lbCaseDestination, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(lbImageDestination, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(lbDbConnection, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(lbProgressBar, javax.swing.GroupLayout.Alignment.TRAILING))
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(tbCaseDestination)
.addComponent(tbImageDestination)
.addComponent(pbShowProgress, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(bnStart, javax.swing.GroupLayout.PREFERRED_SIZE, 99, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(bnCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 99, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(bnShowLog, javax.swing.GroupLayout.PREFERRED_SIZE, 99, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(cbCopyImages)
.addComponent(picDbStatus)
.addGroup(layout.createSequentialGroup()
.addComponent(cbDeleteCase)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(tbDeleteWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 478, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGap(0, 11, Short.MAX_VALUE)))))
.addGap(105, 105, 105)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbCaption)
.addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(bnBrowseCaseSource)
.addComponent(tbCaseSource, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbCaseSource))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(bnBrowseImageSource)
.addComponent(tbImageSource, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbImageSource))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cbCopyImages)
.addComponent(tbInputNotification, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cbDeleteCase)
.addComponent(tbDeleteWarning, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(35, 35, 35)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(tbCaseDestination, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbCaseDestination))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(tbImageDestination, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbImageDestination))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tbOutputNotification, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(9, 9, 9)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbDbConnection)
.addComponent(picDbStatus))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(pbShowProgress, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbProgressBar))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(bnShowLog)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(bnStart)
.addComponent(bnCancel)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tbBottomNotification, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(172, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
/**
* Handles pressing the "Start" button
*
* @param evt
*/
private void bnStartActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnStartActionPerformed
showDbStatus();
if (canTalkToDb) {
setNotificationText(NotificationLabel.PROGRESS, NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.ImportingCases"), true);
SingleUserCaseImporter caseImporter = new SingleUserCaseImporter(
tbImageSource.getText(),
tbCaseSource.getText(),
tbImageDestination.getText(),
tbCaseDestination.getText(),
cbCopyImages.isSelected(),
cbDeleteCase.isSelected(),
this);
pbShowProgress.setIndeterminate(true);
ongoingImport = new Thread(caseImporter);
setButtonsForJobRunning(true);
ongoingImport.start();
} else {
bnStart.setEnabled(false);
}
}//GEN-LAST:event_bnStartActionPerformed
/**
* Allows bulk-setting the button enabled states.
*
* @param setting true if we are currently processing an import job, false
* otherwise
*/
void setButtonsForJobRunning(boolean setting) {
bnBrowseCaseSource.setEnabled(!setting);
bnBrowseImageSource.setEnabled(!setting);
cbCopyImages.setEnabled(!setting);
cbDeleteCase.setEnabled(!setting);
bnStart.setEnabled(!setting);
bnCancel.setEnabled(setting);
}
/**
* Handles pressing the Cancel button
*
* @param evt
*/
private void bnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnCancelActionPerformed
if (ongoingImport != null) {
setNotificationText(NotificationLabel.PROGRESS, NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.Cancelling"), false);
ongoingImport.interrupt();
}
}//GEN-LAST:event_bnCancelActionPerformed
/**
* Handles pressing the Browse for case source folder button
*
* @param evt
*/
private void bnBrowseCaseSourceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnBrowseCaseSourceActionPerformed
int returnVal = caseSourceFolderChooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
validateSourceFields();
}
}//GEN-LAST:event_bnBrowseCaseSourceActionPerformed
/**
* Show user information about status of source fields, hierarchically.
*/
private void validateSourceFields() {
String caseSourceResult = "";
File selectedFolder = caseSourceFolderChooser.getSelectedFile();
if (selectedFolder == null || !selectedFolder.exists()) {
caseSourceResult = NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.BadCaseSourceFolder");
tbCaseSource.setText("");
} else {
caseSourceResult = NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.Blank");
caseSourceFolderChooser.setCurrentDirectory(selectedFolder);
tbCaseSource.setText(selectedFolder.toString());
}
String caseImagesResult = "";
if (cbCopyImages.isSelected()) {
selectedFolder = imageSourceFolderChooser.getSelectedFile();
if (selectedFolder == null || !selectedFolder.exists()) {
caseImagesResult = NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.BadImageSourceFolder");
tbImageSource.setText(""); //NON-NLS
} else {
if (tbInputNotification.getText().isEmpty()) {
caseImagesResult = NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.Blank");
}
imageSourceFolderChooser.setCurrentDirectory(selectedFolder);
tbImageSource.setText(selectedFolder.toString());
}
}
String result = caseSourceResult;
if (result.isEmpty()) {
result = caseImagesResult;
}
setNotificationText(NotificationLabel.INPUT, result, false);
enableStartButton();
}
/**
* Handles pressing the Show Log button
*
* @param evt
*/
private void bnShowLogActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnShowLogActionPerformed
try {
File logFile = Paths.get(tbCaseDestination.getText(), SingleUserCaseImporter.CASE_IMPORT_LOG_FILE).toFile();
setNotificationText(NotificationLabel.BOTTOM, "", false); //NON-NLS
Desktop.getDesktop().edit(logFile);
} catch (Exception ex) {
setNotificationText(NotificationLabel.BOTTOM, NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.CannotOpenLog"), false);
}
}//GEN-LAST:event_bnShowLogActionPerformed
/**
* Handles pressing the Browse for image source folder button
*
* @param evt
*/
private void bnBrowseImageSourceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnBrowseImageSourceActionPerformed
int returnVal = imageSourceFolderChooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
validateSourceFields();
}
}//GEN-LAST:event_bnBrowseImageSourceActionPerformed
private void cbCopyImagesPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_cbCopyImagesPropertyChange
}//GEN-LAST:event_cbCopyImagesPropertyChange
private void cbDeleteCasePropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_cbDeleteCasePropertyChange
if (deleteImagesState != cbDeleteCase.isSelected()) {
deleteImagesState = cbDeleteCase.isSelected();
if (cbDeleteCase.isSelected()) {
tbDeleteWarning.setForeground(Color.RED);
tbDeleteWarning.setText(NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.DeleteWarning"));
} else {
// USE BUNDLE
tbDeleteWarning.setText("");
}
}
}//GEN-LAST:event_cbDeleteCasePropertyChange
private void cbCopyImagesStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_cbCopyImagesStateChanged
// Enable or disable the image folder entries
// this gets notified of mouseovers and such, so check that it actually
// changed before changing the state of UI components
if (copyImagesState != cbCopyImages.isSelected()) {
copyImagesState = cbCopyImages.isSelected();
if (copyImagesState) {
tbImageSource.setEnabled(true);
tbImageDestination.setEnabled(true);
bnBrowseImageSource.setEnabled(true);
} else {
tbImageSource.setEnabled(false);
tbImageDestination.setEnabled(false);
bnBrowseImageSource.setEnabled(false);
}
validateSourceFields();
}
}//GEN-LAST:event_cbCopyImagesStateChanged
/**
* Enables the start button if all input is in order, disables it otherwise
*/
private void enableStartButton() {
if (UserPreferences.getIsMultiUserModeEnabled()
&& AutoIngestUserPreferences.getJoinAutoModeCluster()
&& (! RuntimeProperties.coreComponentsAreActive())
&& !tbCaseSource.getText().isEmpty()
&& !tbCaseDestination.getText().isEmpty()
&& canTalkToDb == true
&& (!cbCopyImages.isSelected() || (!tbImageSource.getText().isEmpty() && !tbImageDestination.getText().isEmpty()))) {
bnStart.setEnabled(true);
} else {
bnStart.setEnabled(false);
}
}
/**
* Allows setting the notification text outside the EDT.
*
* @param position the label we intend to set
* @param text The text to set
* @param okay True if there was no issue, false otherwise. Sets text
* color.
*/
private void setNotificationText(final NotificationLabel position, final String text, final boolean okay) {
EventQueue.invokeLater(() -> {
if (position != NotificationLabel.PROGRESS) {
JTextField textField;
if (position == NotificationLabel.INPUT) {
textField = tbInputNotification;
} else if (position == NotificationLabel.OUTPUT) {
textField = tbOutputNotification;
} else {
textField = tbBottomNotification;
}
textField.setText(text);
if (okay) {
Font font = textField.getFont();
textField.setFont(font.deriveFont(Font.BOLD));
textField.setForeground(Color.BLACK);
} else {
Font font = textField.getFont();
textField.setFont(font.deriveFont(Font.PLAIN));
textField.setForeground(Color.RED);
}
} else {
pbShowProgress.setString(text);
if (okay) {
pbShowProgress.setForeground(new Color(51, 153, 255));
} else {
pbShowProgress.setForeground(Color.RED);
}
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton bnBrowseCaseSource;
private javax.swing.JButton bnBrowseImageSource;
private javax.swing.JButton bnCancel;
private javax.swing.JButton bnShowLog;
private javax.swing.JButton bnStart;
private javax.swing.JCheckBox cbCopyImages;
private javax.swing.JCheckBox cbDeleteCase;
private javax.swing.JLabel lbCaption;
private javax.swing.JLabel lbCaseDestination;
private javax.swing.JLabel lbCaseSource;
private javax.swing.JLabel lbDbConnection;
private javax.swing.JLabel lbImageDestination;
private javax.swing.JLabel lbImageSource;
private javax.swing.JLabel lbProgressBar;
private javax.swing.JProgressBar pbShowProgress;
private javax.swing.JLabel picDbStatus;
private javax.swing.JTextField tbBottomNotification;
private javax.swing.JTextField tbCaseDestination;
private javax.swing.JTextField tbCaseSource;
private javax.swing.JTextField tbDeleteWarning;
private javax.swing.JTextField tbImageDestination;
private javax.swing.JTextField tbImageSource;
private javax.swing.JTextField tbInputNotification;
private javax.swing.JTextField tbOops;
private javax.swing.JTextField tbOutputNotification;
// End of variables declaration//GEN-END:variables
/**
* This method is called by the import thread as it is finishing.
*
* @param result true if the entire import was successful, false
* otherwise
* @param resultString the text string to show the user
*/
@Override
public void importDoneCallback(boolean result, String resultString) {
if (resultString == null || resultString.isEmpty()) {
pbShowProgress.setIndeterminate(false);
pbShowProgress.setValue(100);
if (result) {
setNotificationText(NotificationLabel.PROGRESS, NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.Complete"), true);
} else {
setNotificationText(NotificationLabel.PROGRESS, NbBundle.getMessage(CaseImportPanel.class, "CaseImportPanel.Error"), result);
}
} else {
pbShowProgress.setIndeterminate(false);
if (result == true) {
pbShowProgress.setValue(0);
} else {
pbShowProgress.setValue(100);
}
setNotificationText(NotificationLabel.PROGRESS, resultString, result);
}
setButtonsForJobRunning(false);
ongoingImport = null;
showDbStatus();
}
}

View File

@ -0,0 +1,134 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.JComponent;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
// Commenting this out to remove from options display. Case import is currently broken
// (needs update to the database queries) and will be moving to the tools menu
/*@OptionsPanelController.TopLevelRegistration(categoryName = "#OptionsCategory_Name_Case_Import",
iconBase = "org/sleuthkit/autopsy/experimental/images/import32.png",
position = 4,
keywords = "#OptionsCategory_Keywords_Case_Import",
keywordsCategory = "Case Import")*/
public final class CaseImportPanelController extends OptionsPanelController {
private CaseImportPanel panel;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private boolean changed;
private static final Logger logger = Logger.getLogger(CaseImportPanelController.class.getName());
@Override
public void update() {
getPanel().load();
changed = false;
}
@Override
public void applyChanges() {
getPanel().store();
changed = false;
}
@Override
public void cancel() {
}
@Override
public boolean isValid() {
return getPanel().valid();
}
@Override
public boolean isChanged() {
return changed;
}
@Override
public HelpCtx getHelpCtx() {
return null;
}
@Override
public JComponent getComponent(Lookup masterLookup) {
return getPanel();
}
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
if (pcs.getPropertyChangeListeners().length == 0) {
pcs.addPropertyChangeListener(l);
}
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
/**
* Note the NetBeans Framework does not appear to call this at all. We
* are using NetBeans 7.3.1 Build 201306052037. Perhaps in a future
* version of the Framework this will be resolved, but for now, simply
* don't unregister anything and add one time only in the
* addPropertyChangeListener() method above.
*/
}
private CaseImportPanel getPanel() {
if (panel == null) {
panel = new CaseImportPanel(this);
}
return panel;
}
void changed() {
if (!changed) {
changed = true;
try {
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
} catch (Exception e) {
logger.log(Level.SEVERE, "GeneralOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
}
try {
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
} catch (Exception e) {
logger.log(Level.SEVERE, "GeneralOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Component;
import javax.swing.ImageIcon;
import javax.swing.JTable;
import static javax.swing.SwingConstants.CENTER;
import org.openide.util.ImageUtilities;
/**
* A JTable cell renderer that represents an auto ingest alert file exists flag
* as a center-aligned icon, and grays out the cell if the table is disabled.
*/
class CaseStatusIconCellRenderer extends GrayableCellRenderer {
private static final long serialVersionUID = 1L;
static final ImageIcon checkedIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/tick.png", false));
static final ImageIcon warningIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/warning16.png", false));
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setHorizontalAlignment(CENTER);
if ((value instanceof Boolean)) {
if (true == (Boolean) value) {
setIcon(warningIcon);
setToolTipText(org.openide.util.NbBundle.getMessage(CaseStatusIconCellRenderer.class, "CaseStatusIconCellRenderer.tooltiptext.warning"));
} else {
setIcon(checkedIcon);
setToolTipText(org.openide.util.NbBundle.getMessage(CaseStatusIconCellRenderer.class, "CaseStatusIconCellRenderer.tooltiptext.ok"));
}
}
grayCellIfTableNotEnabled(table, isSelected);
return this;
}
}

View File

@ -0,0 +1,34 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import static javax.swing.SwingConstants.CENTER;
/**
* A JTable cell renderer that center-aligns cell content and grays out the cell
* if the table is disabled.
*/
class CenteredGrayableCellRenderer extends GrayableCellRenderer {
private static final long serialVersionUID = 1L;
public CenteredGrayableCellRenderer() {
setHorizontalAlignment(CENTER);
}
}

View File

@ -0,0 +1,33 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
/**
* Namespace elements for auto ingest coordination service nodes.
*/
final class CoordinationServiceNamespace {
private static final String ROOT = "autopsy";
static String getRoot() {
return ROOT;
}
private CoordinationServiceNamespace() {
}
}

View File

@ -0,0 +1,74 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Component;
import java.time.Duration;
import javax.swing.JTable;
import static javax.swing.SwingConstants.CENTER;
/**
* A JTable cell renderer that renders a duration represented as a long as a
* string with days, hours, minutes, and seconds components. It center-aligns
* cell content and grays out the cell if the table is disabled.
*/
class DurationCellRenderer extends GrayableCellRenderer {
private static final long serialVersionUID = 1L;
DurationCellRenderer() {
setHorizontalAlignment(CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value instanceof Long) {
{
Duration d = Duration.ofMillis((long) value);
if (d.isNegative()) {
d = Duration.ofMillis(-(long) value);
}
String result;
long days = d.toDays();
long hours = d.minusDays(days).toHours();
long minutes = d.minusDays(days).minusHours(hours).toMinutes();
long seconds = d.minusDays(days).minusHours(hours).minusMinutes(minutes).getSeconds();
if (minutes > 0) {
if (hours > 0) {
if (days > 0) {
result = days + " d " + hours + " h " + minutes + " m " + seconds + " s";
} else {
result = hours + " h " + minutes + " m " + seconds + " s";
}
} else {
result = minutes + " m " + seconds + " s";
}
} else {
result = seconds + " s";
}
setText(result);
}
}
grayCellIfTableNotEnabled(table, isSelected);
return this;
}
}

View File

@ -0,0 +1,282 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.TreeMap;
import org.openide.util.io.NbObjectInputStream;
import org.openide.util.io.NbObjectOutputStream;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/**
* Settings for the export of files based on user-defined export rules.
*/
final class FileExportSettings implements Serializable {
private static final long serialVersionUID = 1L;
private static final String DEFAULT_MASTER_CATALOG_NAME = "interim";
private static final String DEFAULT_EXPORT_COMPLETED_FILE_NAME = "EXTRACTION_FINISHED";
private static final String DEFAULT_RULES_EVALUATED_NAME = "SORTER_FINISHED";
private static final String SETTINGS_DIRECTORY = org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration.FILE_EXPORTER_FOLDER;
private static final String SETTINGS_FILE_NAME = org.sleuthkit.autopsy.experimental.configuration.SharedConfiguration.FILE_EXPORTER_SETTINGS_FILE;
private TreeMap<String, FileExportRuleSet> ruleSets;
private String filesRootDirectory;
private String reportsRootDirectory;
private String masterCatalogName;
private String exportCompletedFlagFileName;
private String rulesEvaluatedFlagFileName;
private boolean enabled;
/**
* Saves the file export settings to secondary storage. Existing settings
* are overwritten.
*
* @param settings The settings to save.
*
* @throws
* org.sleuthkit.autopsy.autoingest.FileExportSettings.PersistenceException
*/
static synchronized void store(FileExportSettings settings) throws PersistenceException {
Path folderPath = Paths.get(PlatformUtil.getUserConfigDirectory(), SETTINGS_DIRECTORY);
Path filePath = Paths.get(folderPath.toString(), SETTINGS_FILE_NAME);
try {
Files.createDirectories(folderPath);
try (NbObjectOutputStream out = new NbObjectOutputStream(new FileOutputStream(filePath.toString()))) {
out.writeObject(settings);
}
} catch (IOException ex) {
throw new PersistenceException(String.format("Failed to write settings to %s", filePath), ex);
}
}
/**
* Reads the file export settings from secondary storage.
*
* @return The settings.
*
* @throws
* org.sleuthkit.autopsy.autoingest.FileExportSettings.PersistenceException
*/
static synchronized FileExportSettings load() throws PersistenceException {
Path filePath = Paths.get(PlatformUtil.getUserConfigDirectory(), SETTINGS_DIRECTORY, SETTINGS_FILE_NAME);
try {
// if the File Exporter settings file doesn't exist, return default settings
if (!filePath.toFile().exists()) {
return new FileExportSettings();
}
try (NbObjectInputStream in = new NbObjectInputStream(new FileInputStream(filePath.toString()))) {
FileExportSettings settings = (FileExportSettings) in.readObject();
return settings;
}
} catch (IOException | ClassNotFoundException ex) {
throw new PersistenceException(String.format("Failed to read settings from %s", filePath), ex);
}
}
/**
* Constructs an instance of the settings for the export of files based on
* user-defined export rules.
*/
FileExportSettings() {
enabled = false;
ruleSets = new TreeMap<>();
filesRootDirectory = null;
reportsRootDirectory = null;
masterCatalogName = DEFAULT_MASTER_CATALOG_NAME;
exportCompletedFlagFileName = DEFAULT_EXPORT_COMPLETED_FILE_NAME;
rulesEvaluatedFlagFileName = DEFAULT_RULES_EVALUATED_NAME;
}
/**
* Sets file export enabled state.
*
* @param state The state to set.
*/
void setFileExportEnabledState(boolean state) {
this.enabled = state;
}
/**
* Gets file export enabled state.
*
* @return The enabled state.
*/
boolean getFileExportEnabledState() {
return this.enabled;
}
/**
* Sets the file export rules.
*
* @param ruleSets A map of rules with name keys, sorted by name.
*
*/
void setRuleSets(TreeMap<String, FileExportRuleSet> ruleSets) {
this.ruleSets = ruleSets;
}
/**
* Gets the file export rules.
*
* @return A map of rules with name keys, sorted by name.
*/
TreeMap<String, FileExportRuleSet> getRuleSets() {
return this.ruleSets;
}
/**
* Sets the root file export directory.
*
* @param filesRootDirectory The path of the root directory for files
* export.
*/
void setFilesRootDirectory(Path filesRootDirectory) {
this.filesRootDirectory = filesRootDirectory.toString();
}
/**
* Gets the root file output directory.
*
* @return The path of the root directory for files export, may be null.
*/
Path getFilesRootDirectory() {
if (null != filesRootDirectory) {
return Paths.get(this.filesRootDirectory);
} else {
return null;
}
}
/**
* Sets the root reports (file catalogs) directory.
*
* @param reportsRootDirectory The path of the root directory for reports
* (file catalogs).
*/
void setReportsRootDirectory(Path reportsRootDirectory) {
this.reportsRootDirectory = reportsRootDirectory.toString();
}
/**
* Gets the root file output directory.
*
* @return The path of the root directory for reports (file catalogs), may
* be null.
*/
Path getReportsRootDirectory() {
if (null != this.reportsRootDirectory) {
return Paths.get(this.reportsRootDirectory);
} else {
return null;
}
}
/**
* Sets the name of the master catalog of exported files.
*
* @param name The catalog name.
*/
void setMasterCatalogName(String name) {
this.masterCatalogName = name;
}
/**
* Gets the name of the master catalog of exported files.
*
* @return The catalog name.
*/
String getMasterCatalogName() {
return this.masterCatalogName;
}
/**
* Sets the name of the file written to indicate file export is completed.
*
* @param fileName The file name.
*/
void setExportCompletedFlagFileName(String fileName) {
this.exportCompletedFlagFileName = fileName;
}
/**
* Gets the name of the file written to indicate file export is completed.
*
* @return The file name.
*/
String getExportCompletedFlagFileName() {
return this.exportCompletedFlagFileName;
}
/**
* Sets the name of the file written to indicate file export rule evaluation
* is completed.
*
* @param fileName The file name.
*/
void setRulesEvaluatedFlagFileName(String fileName) {
this.rulesEvaluatedFlagFileName = fileName;
}
/**
* Gets the name of the file written to indicate file export rule evaluation
* is completed.
*
* @return The file name.
*/
String getRulesEvaluatedFlagFileName() {
return rulesEvaluatedFlagFileName;
}
/**
* Exception thrown if there is a problem storing or loading the settings.
*/
public final static class PersistenceException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs an exception.
*
* @param message The exception message.
*/
private PersistenceException(String message) {
super(message);
}
/**
* Constructs an exception.
*
* @param message The exception message.
* @param cause The exception cause.
*/
private PersistenceException(String message, Throwable cause) {
super(message, cause);
}
}
}

View File

@ -0,0 +1,645 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Supplier;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.FileSystem;
import org.sleuthkit.datamodel.IngestJobInfo;
import org.sleuthkit.datamodel.IngestModuleInfo;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.experimental.autoingest.FileExportRuleSet.Rule;
/**
* Exports the files that satisfy user-defined file export rules from a set of
* data sources associated with a device.
*/
final class FileExporter {
private static final int FAT_NTFS_FLAGS = TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_FAT12.getValue() | TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_FAT16.getValue() | TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_FAT32.getValue() | TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_NTFS.getValue();
// File Exporter requires File Type Identification and Hash Lookup modules to run beforehand.
private static final List<String> REQUIRED_MODULE_CANONICAL_NAME = Arrays.asList("org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory", "org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory");
private String deviceId;
private FileExportSettings settings;
private Path filesDirPath;
private Path reportsDirPath;
private JsonFactory jsonGeneratorFactory;
private JsonGenerator masterCatalog;
private Map<String, JsonGenerator> ruleNamesToCatalogs;
private List<Path> flagFilePaths;
FileExporter() throws FileExportException {
try {
settings = FileExportSettings.load();
} catch (FileExportSettings.PersistenceException ex) {
throw new FileExportException("Error initializing File Exporter", ex);
}
}
boolean isEnabled() {
return settings.getFileExportEnabledState();
}
/**
* Exports the files that satisfy user-defined file export rules from a set
* of data sources associated with a device.
*
* @param deviceId The device id.
* @param dataSources The data sources.
* @param cancelCheck A function used to check if the file AutoInjectJob process
* should be terminated.
* @throws FileExportException if there is an error in the export process.
*/
void process(String deviceId, List<Content> dataSources, Supplier<Boolean> cancelCheck) throws FileExportException {
this.deviceId = deviceId;
try {
if (!isEnabled() || cancelCheck.get()) {
return;
}
// File Exporter requires several ingest modules to run beforehand.
// Verify that these modules were enabled for the current data source(s)
if (!verifyPrerequisites(dataSources)) {
// throw exception to pause auto ingest
throw new FileExportException("File Exporter prerequisite ingest modules were not turned on");
}
setUp();
for (Content dataSource : dataSources) {
Map<Long, List<String>> fileIdsToRuleNames = evaluateRules(dataSource, cancelCheck);
if (cancelCheck.get()) {
break;
}
exportFiles(fileIdsToRuleNames, cancelCheck);
}
closeCatalogs();
writeFlagFiles();
} catch (FileExportSettings.PersistenceException | FileExportRuleSet.ExportRulesException | TskCoreException | IOException ex) {
throw new FileExportException("Error occurred during file export", ex);
}
}
/**
* Verifies that all AFE prerequisite ingest modules were enabled for all
* input data sources
*
* @param dataSources List of data sources to check
*
* @return True if all AFE prerequisite ingest modules were enabled,
* false otherwise
*
* @throws org.sleuthkit.autopsy.autoingest.FileExporter.FileExportException
*/
private boolean verifyPrerequisites(List<Content> dataSources) throws FileExportException {
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
List<IngestJobInfo> ingestJobs = new ArrayList<>();
try {
// all ingest jobs that were processed as part of this case
ingestJobs = skCase.getIngestJobs();
} catch (TskCoreException ex) {
throw new FileExportException("Failed to obtain ingest jobs", ex);
}
// for each data source
for (Content dataSource : dataSources) {
// verify that all required modules were enabled
for (String requiredModuleCanonicalName : REQUIRED_MODULE_CANONICAL_NAME) {
boolean requiredModuleWasEnabled = false;
/* NOTE: there could have been multiple ingest jobs performed for
each data source. We have to loop over all ingest jobs for the data
source to check whether the required ingest module was enabled
in either one of them. */
for (IngestJobInfo ingestJob : ingestJobs) {
if (ingestJob.getStatus() != IngestJobInfo.IngestJobStatusType.COMPLETED) {
continue;
}
if (dataSource.getId() != ingestJob.getObjectId()) {
// ingest job was for a differnt data source
continue;
}
// loop over job's ingest module list to check whether it contains the required module
if (isRequiredModuleEnabled(requiredModuleCanonicalName, ingestJob)) {
requiredModuleWasEnabled = true;
break;
}
}
if (!requiredModuleWasEnabled) {
// required module did not run
return false;
}
}
}
// if we are here then all requirements were met
return true;
}
/**
* Loop over ingest job's ingest module list to check whether it contains
* the required module
*
* @param requiredModuleCanonicalName Canonical name of the ingest module to
* look for
* @param ingestJob Ingest job object
*
* @return True if ingest job contained required ingest module, false
* otherwise
*/
private boolean isRequiredModuleEnabled(String requiredModuleCanonicalName, IngestJobInfo ingestJob) {
for (IngestModuleInfo ingestModuleInfo : ingestJob.getIngestModuleInfo()) {
String canonicalName = ingestModuleInfo.getUniqueName().split("-")[0];
if (canonicalName.equals(requiredModuleCanonicalName)) {
return true;
}
}
return false;
}
/**
* Sets up the export process by loading user settings, creating output
* directories, creating the JSON generator for the master files catalog,
* and determining the flag file paths.
*
* @throws FileExportSettings.PersistenceException If there is a problem
* loading the settings.
* @throws IOException If there is a problem
* creating the export
* directories or the JSON
* generator.
*/
private void setUp() throws FileExportSettings.PersistenceException, IOException {
/*
* Create the file export and report root directory paths.
*/
filesDirPath = settings.getFilesRootDirectory();
filesDirPath = filesDirPath.resolve(deviceId);
reportsDirPath = settings.getReportsRootDirectory();
reportsDirPath = reportsDirPath.resolve(deviceId);
/*
* Delete the results of a previous run, if any. Results deletion cleans
* up for a crashed auto ingest job.
*/
FileUtil.deleteDir(reportsDirPath.toFile());
FileUtil.deleteDir(filesDirPath.toFile());
/*
* Create the file export and report root directories.
*/
Files.createDirectories(filesDirPath);
Files.createDirectories(reportsDirPath);
/*
* Create the JSON generator for the master catalog of exported files
* and a map to hold the JSON generators for the rule catalogs.
*/
jsonGeneratorFactory = new JsonFactory();
jsonGeneratorFactory.setRootValueSeparator("\r\n");
String catalogName = settings.getMasterCatalogName();
String catalogDir = catalogName.substring(0, 1).toUpperCase() + catalogName.substring(1);
Path catalogPath = Paths.get(reportsDirPath.toString(), catalogDir, catalogName + ".json");
Files.createDirectories(catalogPath.getParent());
File catalogFile = catalogPath.toFile();
masterCatalog = jsonGeneratorFactory.createGenerator(catalogFile, JsonEncoding.UTF8);
ruleNamesToCatalogs = new HashMap<>();
/*
* Set up the paths for the flag files that are written to signal export
* is complete for thsi device.
*/
flagFilePaths = new ArrayList<>();
flagFilePaths.add(Paths.get(filesDirPath.toString(), settings.getExportCompletedFlagFileName()));
flagFilePaths.add(Paths.get(reportsDirPath.toString(), catalogDir, settings.getExportCompletedFlagFileName()));
flagFilePaths.add(Paths.get(reportsDirPath.toString(), settings.getRulesEvaluatedFlagFileName()));
}
/**
* Evaluates each file export rule to produce a map that associates the file
* id of each file to be exported with a list of the names of the rules
* satisfied by the file.
*
* @return The map of file ids to rule name lists.
*/
/**
* Evaluates each file export rule for a data source to produce a map that
* associates the file id of each file to be exported with a list of the
* names of the rules satisfied by the file.
*
* @param dataSource The data source.
* @param cancelCheck A function used to check if the file AutoInjectJob process
* should be terminated.
*
* @return The map of file ids to rule name lists.
*
* @throws FileExportRuleSet.ExportRulesException If there is a problem
* evaluating a rule.
*/
private Map<Long, List<String>> evaluateRules(Content dataSource, Supplier<Boolean> cancelCheck) throws FileExportRuleSet.ExportRulesException {
TreeMap<String, FileExportRuleSet> ruleSets = settings.getRuleSets();
Map<Long, List<String>> fileIdsToRuleNames = new HashMap<>();
for (FileExportRuleSet ruleSet : ruleSets.values()) {
for (Rule rule : ruleSet.getRules().values()) {
if (cancelCheck.get()) {
return fileIdsToRuleNames;
}
List<Long> fileIds = rule.evaluate(dataSource.getId());
for (Long fileId : fileIds) {
List<String> ruleList;
if (!fileIdsToRuleNames.containsKey(fileId)) {
ruleList = new ArrayList<>();
fileIdsToRuleNames.put(fileId, ruleList);
} else {
ruleList = fileIdsToRuleNames.get(fileId);
}
ruleList.add(rule.getName());
}
}
}
return fileIdsToRuleNames;
}
/**
* Writes each file to be exported to secondary storage and makes entries
* for the file in the master catalog and the catalogs of the export rules
* the file satisfied.
*
* @param fileIdsToRuleNames The map of file ids to rule name lists.
* @param cancelCheck A function used to check if the file write process
* should be terminated.
*
* @throws TskCoreException If there is a problem querying file metadata or
* getting file content.
* @throws IOException If there is a problem writing a file to
* secondary storage.
*/
private void exportFiles(Map<Long, List<String>> fileIdsToRuleNames, Supplier<Boolean> cancelCheck) throws TskCoreException, IOException {
for (Map.Entry<Long, List<String>> entry : fileIdsToRuleNames.entrySet()) {
if (cancelCheck.get()) {
return;
}
exportFile(entry.getKey(), entry.getValue(), cancelCheck);
}
}
/**
* Writes a file to secondary storage and makes entries for the file in the
* master catalog and the catalogs of the export rules the file satisfied.
*
* @param fileId The id of the file to export.
* @param ruleNames The names of the export rules the file satisfied.
* @param progress The progress reporter for this module.
* @param cancelCheck A function used to check if the file write process
* should be terminated.
*
* @throws TskCoreException If there is a problem querying file metadata or
* getting file content.
* @throws IOException If there is a problem writing the file to
* storage.
*/
private void exportFile(Long fileId, List<String> ruleNames, Supplier<Boolean> cancelCheck) throws TskCoreException, IOException {
AbstractFile file = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(fileId);
if (!shouldExportFile(file)) {
return;
}
Map<BlackboardArtifact, List<BlackboardAttribute>> artifactsToAttributes = new HashMap<>();
List<BlackboardArtifact> artifacts = file.getAllArtifacts();
for (BlackboardArtifact artifact : artifacts) {
artifactsToAttributes.put(artifact, artifact.getAttributes());
}
Path filePath = exportFileToSecondaryStorage(file, cancelCheck);
if (filePath == null) {
return;
}
addFileToCatalog(file, artifactsToAttributes, filePath, masterCatalog);
for (String ruleName : ruleNames) {
JsonGenerator ruleCatalog = this.ruleNamesToCatalogs.get(ruleName);
if (null == ruleCatalog) {
Path catalogPath = Paths.get(reportsDirPath.toString(), ruleName, "catalog.json");
Files.createDirectories(catalogPath.getParent());
File catalogFile = catalogPath.toFile();
ruleCatalog = jsonGeneratorFactory.createGenerator(catalogFile, JsonEncoding.UTF8);
ruleNamesToCatalogs.put(ruleName, ruleCatalog);
}
addFileToCatalog(file, artifactsToAttributes, filePath, ruleCatalog);
}
}
/**
* Determines whether or not a file should be exported, even though it
* satisfies a file export rule. Unallocated space files, pseudo-files, and
* special NTFS or FAT file system files are not eligible for export.
*
* @param candidateFile The file.
*
* @return True or false.
*
* @throws TskCoreException If there is a problem querying file metadata.
*/
private static boolean shouldExportFile(AbstractFile candidateFile) throws TskCoreException {
if (candidateFile instanceof org.sleuthkit.datamodel.File) {
/*
* Is the file in an NTFS or FAT file system?
*/
org.sleuthkit.datamodel.File file = (org.sleuthkit.datamodel.File) candidateFile;
TskData.TSK_FS_TYPE_ENUM fileSystemType = TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_UNSUPP;
FileSystem fileSystem = file.getFileSystem();
if (null != fileSystem) {
fileSystemType = fileSystem.getFsType();
}
if ((fileSystemType.getValue() & FAT_NTFS_FLAGS) == 0) {
return true;
}
/*
* Is the NTFS or FAT file in the root directory?
*/
AbstractFile parent = file.getParentDirectory();
boolean isInRootDir = parent.isRoot();
/*
* Check its meta-address and check its name for the '$' character
* and a ':' character (not a default attribute).
*/
if (isInRootDir && file.getMetaAddr() < 32) {
String name = file.getName();
if (name.length() > 0 && name.charAt(0) == '$' && name.contains(":")) {
return false;
}
}
}
return true;
}
/**
* Writes a file to a "hash tree" in secondary storage. For example, a file
* with MD5 hash d131dd02c5e6eec46e4bc422aef54eb4 and MIME type text/html
* would be written to the following location:
*
* outputDir/text-html/D1/31/DD/02/D131DD02C5E6EEC4
*
* @param file The file to export.
* @param cancelCheck A function used to check if the file write process
* should be terminated.
*
* @return The path to the exported file.
*
* @throws TskCoreException If there is a problem reading from the case
* database.
* @throws IOException If the file cannot be written.
*/
private Path exportFileToSecondaryStorage(AbstractFile file, Supplier<Boolean> cancelCheck) throws TskCoreException, IOException {
/*
* Get the MIME type of the file to be used as a path component.
*/
String mimeType = file.getMIMEType();
/*
* Get the MD5 hash of the file to be used for path components.
*/
String md5 = file.getMd5Hash().toUpperCase();
/*
* Construct the export path and export the file.
*/
Path exportFilePath = Paths.get(this.filesDirPath.toString(),
mimeType.replace('/', '-'),
md5.substring(0, 2),
md5.substring(2, 4),
md5.substring(4, 6),
md5.substring(6, 8),
md5);
File exportFile = exportFilePath.toFile();
if (!exportFile.exists()) {
Files.createDirectories(exportFilePath.getParent());
ContentUtils.writeToFile(file, exportFile, cancelCheck);
if (cancelCheck.get()) {
Files.delete(exportFilePath);
return null;
}
}
return exportFilePath;
}
/**
* Adds an exported file to a catalog.
*
* @param file The file.
* @param artifactsToAttributes The artifacts associated with the file.
* @param filePath The path to the exported file.
* @param catalog The catalog.
*
* @throws IOException If there is an error while writing to the
* catalog.
* @throws TskCoreExceptiion If there is a problem querying the case
* database.
*/
private void addFileToCatalog(AbstractFile file, Map<BlackboardArtifact, List<BlackboardAttribute>> artifactsToAttributes, Path filePath, JsonGenerator catalog) throws IOException, TskCoreException {
catalog.writeStartObject();
String md5 = file.getMd5Hash().toUpperCase();
catalog.writeFieldName(md5);
catalog.writeStartObject();
catalog.writeStringField("Filename", filePath.toString());
catalog.writeStringField("Type", file.getMIMEType());
catalog.writeStringField("MD5", md5);
catalog.writeFieldName("File data");
catalog.writeStartObject();
catalog.writeFieldName("Modified");
catalog.writeStartArray();
catalog.writeString(file.getMtimeAsDate());
catalog.writeEndArray();
catalog.writeFieldName("Changed");
catalog.writeStartArray();
catalog.writeString(file.getCtimeAsDate());
catalog.writeEndArray();
catalog.writeFieldName("Accessed");
catalog.writeStartArray();
catalog.writeString(file.getAtimeAsDate());
catalog.writeEndArray();
catalog.writeFieldName("Created");
catalog.writeStartArray();
catalog.writeString(file.getCrtimeAsDate());
catalog.writeEndArray();
catalog.writeFieldName("Extension");
catalog.writeStartArray();
catalog.writeString(file.getNameExtension());
catalog.writeEndArray();
catalog.writeFieldName("Filename");
catalog.writeStartArray();
catalog.writeString(file.getName());
catalog.writeEndArray();
catalog.writeFieldName("Size");
catalog.writeStartArray();
catalog.writeString(Long.toString(file.getSize()));
catalog.writeEndArray();
catalog.writeFieldName("Source Path");
catalog.writeStartArray();
catalog.writeString(file.getParentPath() + "/" + file.getName());
catalog.writeEndArray();
catalog.writeFieldName("Flags (Dir)");
catalog.writeStartArray();
catalog.writeString(file.getDirFlagAsString());
catalog.writeEndArray();
catalog.writeFieldName("Flags (Meta)");
catalog.writeStartArray();
catalog.writeString(file.getMetaFlagsAsString());
catalog.writeEndArray();
catalog.writeFieldName("Mode");
catalog.writeStartArray();
catalog.writeString(file.getModesAsString());
catalog.writeEndArray();
catalog.writeFieldName("User ID");
catalog.writeStartArray();
catalog.writeString(Integer.toString(file.getUid()));
catalog.writeEndArray();
catalog.writeFieldName("Group ID");
catalog.writeStartArray();
catalog.writeString(Integer.toString(file.getGid()));
catalog.writeEndArray();
catalog.writeFieldName("Meta Addr");
catalog.writeStartArray();
catalog.writeString(Long.toString(file.getMetaAddr()));
catalog.writeEndArray();
catalog.writeFieldName("Attr Addr");
catalog.writeStartArray();
catalog.writeString(Long.toString(file.getAttrType().getValue()) + "-" + file.getAttributeId());
catalog.writeEndArray();
catalog.writeFieldName("Dir Type");
catalog.writeStartArray();
catalog.writeString(file.getDirType().getLabel());
catalog.writeEndArray();
catalog.writeFieldName("Meta Type");
catalog.writeStartArray();
catalog.writeString(file.getMetaType().toString());
catalog.writeEndArray();
catalog.writeFieldName("Known");
catalog.writeStartArray();
catalog.writeString(file.getKnown().getName());
catalog.writeEndArray();
catalog.writeEndObject();
for (Map.Entry<BlackboardArtifact, List<BlackboardAttribute>> entry : artifactsToAttributes.entrySet()) {
for (BlackboardAttribute attr : entry.getValue()) {
catalog.writeFieldName(entry.getKey().getArtifactTypeName());
catalog.writeStartObject();
catalog.writeFieldName(attr.getAttributeType().getTypeName());
catalog.writeStartArray();
catalog.writeString(attr.getDisplayString());
catalog.writeEndArray();
catalog.writeEndObject();
}
}
catalog.writeEndObject();
catalog.writeEndObject();
}
/**
* Closes all catalogs opened during export.
*
* @throws IOException If there is a problem closing a catalog.
*/
private void closeCatalogs() throws IOException {
masterCatalog.close();
for (JsonGenerator catalog : this.ruleNamesToCatalogs.values()) {
catalog.close();
}
}
/**
* Writes the flag files that signal export is completed.
*
* @throws IOException If there is a problem writing a flag file.
*/
private void writeFlagFiles() throws IOException {
for (Path flagFile : flagFilePaths) {
Files.createFile(flagFile);
}
}
/**
* Exception thrown to clients if there is a problem exporting files.
*/
final static class FileExportException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs an exception.
*
* @param message The exception message.
*/
private FileExportException(String message) {
super(message);
}
/**
* Constructs an exception.
*
* @param message The exception message.
* @param cause The exception cause.
*/
private FileExportException(String message, Throwable cause) {
super(message, cause);
}
}
}

View File

@ -0,0 +1,727 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.7" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
<Property name="name" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.Title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="cbEnableFileExport" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="39" max="-2" attributes="0"/>
<Component id="lbExplanation" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
<Component id="bnNewRule" min="-2" pref="101" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="bnDeleteRule" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="bnClose" min="-2" pref="101" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="mainPanel" pref="1051" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cbEnableFileExport" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbExplanation" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="7" max="-2" attributes="0"/>
<Component id="mainPanel" pref="455" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="bnDeleteRule" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnNewRule" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnClose" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="mainPanel">
<Properties>
<Property name="autoscrolls" type="boolean" value="true"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[657, 425]"/>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lbFiles" min="-2" max="-2" attributes="0"/>
<Component id="lbReports" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="tbRootDirectory" max="32767" attributes="0"/>
<Component id="tbReportDirectory" max="32767" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="bnBrowseReportDirectory" alignment="1" min="-2" pref="93" max="-2" attributes="0"/>
<Component id="bnBrowseRootDirectory" alignment="1" min="-2" pref="93" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" attributes="0">
<Component id="ruleListScrollPane" min="-2" pref="278" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pnEditRule" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="1" pref="1" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="tbRootDirectory" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbFiles" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnBrowseRootDirectory" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="bnBrowseReportDirectory" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="tbReportDirectory" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbReports" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="ruleListScrollPane" max="32767" attributes="0"/>
<Component id="pnEditRule" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JTextField" name="tbRootDirectory">
<Properties>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.RuleOutputTooltip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[2000, 2000]"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="pnEditRule">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
<Property name="autoscrolls" type="boolean" value="true"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value=""/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="198" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="lbSaveRuleHelper" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnSaveRule" min="-2" pref="101" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="comboBoxAttributeComparison" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="dateTimePicker" min="-2" pref="306" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="tbAttributeValue" pref="158" max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="comboBoxArtifactName" min="-2" max="-2" attributes="0"/>
<Component id="lbArtifact" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="comboBoxAttributeName" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="comboBoxValueType" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="lbAttribute" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="bnAddAttribute" min="-2" pref="117" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnDeleteAttribute" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="cbMimeType" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="cbFileSize" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="cbAttributeType" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="89" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="comboBoxMimeTypeComparison" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="comboBoxMimeValue" min="-2" pref="240" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="comboBoxFileSizeComparison" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="spFileSizeValue" min="-2" pref="105" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="comboBoxFileSizeUnits" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="lbRuleName" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="tbRuleName" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="tbRuleName" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbRuleName" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cbMimeType" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="comboBoxMimeTypeComparison" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="comboBoxMimeValue" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="78" max="-2" attributes="0"/>
<Component id="lbAttribute" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="comboBoxAttributeName" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="comboBoxArtifactName" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="comboBoxValueType" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="tbAttributeValue" min="-2" max="-2" attributes="0"/>
<Component id="dateTimePicker" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="comboBoxAttributeComparison" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="bnAddAttribute" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnDeleteAttribute" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="bnSaveRule" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbSaveRuleHelper" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cbFileSize" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="spFileSizeValue" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="comboBoxFileSizeUnits" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="comboBoxFileSizeComparison" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cbAttributeType" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbArtifact" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="187" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JComboBox" name="comboBoxMimeValue">
<Properties>
<Property name="editable" type="boolean" value="true"/>
<Property name="maximumRowCount" type="int" value="30"/>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.MimetypeTooltip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="comboBoxMimeValueActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JCheckBox" name="cbMimeType">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.MimetypeText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.MimetypeCheckboxTooltip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="checkButtonItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JSpinner" name="spFileSizeValue">
<Properties>
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
<SpinnerModel initial="1024" minimum="0" numberType="java.lang.Integer" stepSize="1" type="number"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.FileSizeValueToolTip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JComboBox" name="comboBoxFileSizeUnits">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.FileSizeUnitToolTip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JCheckBox" name="cbFileSize">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.FileSize" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.FileSize_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="checkButtonItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="comboBoxFileSizeComparison">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.FileSizeComparisonTooltip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32, 20]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JComboBox" name="comboBoxMimeTypeComparison">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.MimeTypeComparisonTooltip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32, 20]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JTextField" name="tbRuleName">
<Properties>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.CurrentlySelectedRuleNameTooltip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[10, 1000]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[733, 20]"/>
</Property>
</Properties>
<Events>
<EventHandler event="keyTyped" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="tbRuleNameKeyTyped"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnSaveRule">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/experimental/images/save-icon.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.SaveText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.SaveTooltip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnSaveRuleActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="comboBoxArtifactName">
<Properties>
<Property name="editable" type="boolean" value="true"/>
<Property name="maximumRowCount" type="int" value="30"/>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value="The Artifact to match"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="comboBoxArtifactNameActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JComboBox" name="comboBoxAttributeName">
<Properties>
<Property name="editable" type="boolean" value="true"/>
<Property name="maximumRowCount" type="int" value="30"/>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value="The attribute of the artifact to match"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="comboBoxAttributeNameActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JComboBox" name="comboBoxAttributeComparison">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value="Select the conditional operator"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32, 23]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JTextField" name="tbAttributeValue">
<Properties>
<Property name="toolTipText" type="java.lang.String" value="Type a value here"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[6, 23]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[6, 23]"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnAddAttribute">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/experimental/images/left-arrow-16-icon.png"/>
</Property>
<Property name="text" type="java.lang.String" value="Add Attribute"/>
<Property name="toolTipText" type="java.lang.String" value="Click to add an attribute to the current rule"/>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnAddAttributeActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="comboBoxValueType">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value="Select the data type of attribute. This will affect how values are compared."/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="comboBoxValueTypeActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JCheckBox" name="cbAttributeType">
<Properties>
<Property name="text" type="java.lang.String" value="Attributes"/>
<Property name="toolTipText" type="java.lang.String" value="Select to include artifact/attribute pairs in the rule"/>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cbAttributeTypeItemStateChanged"/>
</Events>
</Component>
<Component class="com.github.lgooddatepicker.datetimepicker.DateTimePicker" name="dateTimePicker">
<Properties>
<Property name="toolTipText" type="java.lang.String" value="Choose a date and time"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new DateTimePicker(null, timeSettings);"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="lbArtifact">
<Properties>
<Property name="text" type="java.lang.String" value="Artifact"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbAttribute">
<Properties>
<Property name="text" type="java.lang.String" value="Attribute"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnDeleteAttribute">
<Properties>
<Property name="text" type="java.lang.String" value="Delete Attribute"/>
<Property name="toolTipText" type="java.lang.String" value="Click to remove the selected attribute"/>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnDeleteAttributeActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="lsAttributeList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultListModel&lt;String&gt;()" type="code"/>
</Property>
<Property name="selectionMode" type="int" value="0"/>
<Property name="toolTipText" type="java.lang.String" value="The attributes for the selected rule"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="lbRuleName">
<Properties>
<Property name="text" type="java.lang.String" value="Rule Name"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbSaveRuleHelper">
<Properties>
<Property name="horizontalAlignment" type="int" value="4"/>
<Property name="text" type="java.lang.String" value="To save, a rule must have a name and at least one condition."/>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="bnBrowseReportDirectory">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.BrowseText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.BrowseReportTooltip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnBrowseReportDirectoryActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="tbReportDirectory">
<Properties>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.ReportOutputFolderTooltip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[2000, 2000]"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="ruleListScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTree" name="trRuleList">
<Properties>
<Property name="model" type="javax.swing.tree.TreeModel" editor="org.netbeans.modules.form.editors2.TreeModelEditor">
<TreeModel code=""/>
</Property>
<Property name="toolTipText" type="java.lang.String" value="This tree shows the rules to collect files for automatic file export"/>
<Property name="name" type="java.lang.String" value="trRuleList" noResource="true"/>
<Property name="showsRootHandles" type="boolean" value="true"/>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleParent" type="javax.accessibility.Accessible" editor="org.netbeans.modules.form.ComponentChooserEditor">
<ComponentRef name="ruleListScrollPane"/>
</Property>
</AccessibilityProperties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="lbFiles">
<Properties>
<Property name="text" type="java.lang.String" value="Files Folder"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbReports">
<Properties>
<Property name="text" type="java.lang.String" value="Reports Folder"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnBrowseRootDirectory">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.BrowseText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.BrowseRootOutputFolder_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnBrowseRootDirectoryActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JCheckBox" name="cbEnableFileExport">
<Properties>
<Property name="text" type="java.lang.String" value="Enable File Export"/>
<Property name="toolTipText" type="java.lang.String" value="Select to enable File Export"/>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cbEnableFileExportItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnNewRule">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/experimental/images/plus-icon.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.NewText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.NewRuleTooltip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnNewRuleActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnDeleteRule">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/experimental/images/minus-icon.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.DeleteText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="FileExporterSettingsPanel.DeleteTooltip_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnDeleteRuleActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnClose">
<Properties>
<Property name="text" type="java.lang.String" value="Close"/>
<Property name="toolTipText" type="java.lang.String" value="Close the settings panel"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnCloseActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lbExplanation">
<Properties>
<Property name="text" type="java.lang.String" value="File Export occurs after ingest has completed, automatically exporting files matching the rules specified below. Individual components of the rule are ANDed together."/>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,70 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Color;
import java.awt.Component;
import javax.swing.JTable;
import static javax.swing.SwingConstants.LEFT;
import javax.swing.table.DefaultTableCellRenderer;
/**
* A JTable cell renderer that left-aligns cell content and grays out the cell
* if the table is disabled.
*/
class GrayableCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
GrayableCellRenderer() {
setHorizontalAlignment(LEFT);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (null != value) {
setText(value.toString());
}
grayCellIfTableNotEnabled(table, isSelected);
return this;
}
void grayCellIfTableNotEnabled(JTable table, boolean isSelected) {
if (table.isEnabled()) {
/*
* The table is enabled, make the foreground and background the
* normal selected or unselected color.
*/
if (isSelected) {
setBackground(table.getSelectionBackground());
setForeground(table.getSelectionForeground());
} else {
setBackground(table.getBackground());
setForeground(table.getForeground());
}
} else {
/*
* The table is disabled, make the foreground and background gray.
*/
setBackground(Color.lightGray);
setForeground(Color.darkGray);
}
}
}

View File

@ -0,0 +1,24 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
public interface ImportDoneCallback {
void importDoneCallback(boolean result, String resultString);
}

View File

@ -0,0 +1,49 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Component;
import java.text.SimpleDateFormat;
import javax.swing.JTable;
import static javax.swing.SwingConstants.CENTER;
/**
* A JTable cell renderer that renders a date represented as a long as a
* center-aligned, long-format date string. It also grays out the cell if the
* table is disabled.
*/
class LongDateCellRenderer extends GrayableCellRenderer {
private static final long serialVersionUID = 1L;
private static final String FORMAT_STRING = "yyyy/MM/dd HH:mm:ss"; //NON-NLS
private static final SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT_STRING);
public LongDateCellRenderer() {
setHorizontalAlignment(CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value != null) {
setText(dateFormat.format(value));
}
grayCellIfTableNotEnabled(table, isSelected);
return this;
}
}

View File

@ -0,0 +1,130 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.concurrent.Immutable;
/**
* RJCTODO
*/
@Immutable
public final class Manifest implements Serializable {
private static final long serialVersionUID = 1L;
private final String filePath;
private final Date dateFileCreated;
private final String caseName;
private final String deviceId;
private final String dataSourcePath;
private final Map<String, String> manifestProperties;
/**
* RJCTODO
*
* @param manifestFilePath
* @param caseName
* @param deviceId
* @param dataSourcePath
* @param manifestProperties
*
* @throws IOException
*/
public Manifest(Path manifestFilePath, String caseName, String deviceId, Path dataSourcePath, Map<String, String> manifestProperties) throws IOException {
this.filePath = manifestFilePath.toString();
BasicFileAttributes attrs = Files.readAttributes(manifestFilePath, BasicFileAttributes.class);
this.dateFileCreated = new Date(attrs.creationTime().toMillis());
this.caseName = caseName;
this.deviceId = deviceId;
this.dataSourcePath = dataSourcePath.toString();
this.manifestProperties = new HashMap<>(manifestProperties);
}
/**
* RJCTODO
*
* @return
*/
public Path getFilePath() {
return Paths.get(this.filePath);
}
/**
* RJCTODO
*
* @return
* @throws IOException
*/
public Date getDateFileCreated() {
return this.dateFileCreated;
}
/**
* RJCTODO
*
* @return
*/
public String getCaseName() {
return caseName;
}
/**
* RJCTODO
*
* @return
*/
public String getDeviceId() {
return deviceId;
}
/**
* RJCTODO
*
* @return
*/
public Path getDataSourcePath() {
return Paths.get(dataSourcePath);
}
/**
* RJCTODO
* @return
*/
public String getDataSourceFileName() {
return Paths.get(dataSourcePath).getFileName().toString();
}
/**
* RJCTODO
*
* @return
*/
public Map<String, String> getManifestProperties() {
return new HashMap<>(manifestProperties);
}
}

View File

@ -0,0 +1,58 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.nio.file.Path;
/**
* RJCTODO:
*/
public interface ManifestFileParser {
boolean fileIsManifest(Path filePath);
Manifest parse(Path filePath) throws ManifestFileParserException;
/**
* Exception thrown if a manifest file cannot be parsed. RJCTODO
*/
public final static class ManifestFileParserException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs an exception to throw if a manifest file cannot be parsed.
*
* @param message The exception message.
*/
public ManifestFileParserException(String message) {
super(message);
}
/**
* Constructs an exception to throw if a manifest file cannot be parsed.
*
* @param message The exception message.
* @param cause The exception cause, if it was a Throwable.
*/
public ManifestFileParserException(String message, Throwable cause) {
super(message, cause);
}
}
}

View File

@ -0,0 +1,237 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.nio.ByteBuffer;
import java.util.Date;
/**
* A coordination service node data transfer object for an auto ingest job
* manifest. The data include: processing status, priority, the number of times
* the auto ingest job for the manifest has crashed during processing, and the
* date the auto ingest job for the manifest was completed.
*/
final class ManifestNodeData {
private static final int DEFAULT_PRIORITY = 0;
private final boolean coordSvcNodeDataWasSet;
private ProcessingStatus status;
private int priority;
private int numberOfCrashes;
private long completedDate;
private boolean errorsOccurred;
/**
* Constructs a coordination service node data data transfer object for an
* auto ingest manifest from the raw bytes obtained from the coordination
* service.
*
* @param nodeData The raw bytes received from the coordination service.
*/
ManifestNodeData(byte[] nodeData) {
ByteBuffer buffer = ByteBuffer.wrap(nodeData);
this.coordSvcNodeDataWasSet = buffer.hasRemaining();
if (this.coordSvcNodeDataWasSet) {
int rawStatus = buffer.getInt();
if (ProcessingStatus.PENDING.ordinal() == rawStatus) {
this.status = ProcessingStatus.PENDING;
} else if (ProcessingStatus.PROCESSING.ordinal() == rawStatus) {
this.status = ProcessingStatus.PROCESSING;
} else if (ProcessingStatus.COMPLETED.ordinal() == rawStatus) {
this.status = ProcessingStatus.COMPLETED;
}else if (ProcessingStatus.DELETED.ordinal() == rawStatus) {
this.status = ProcessingStatus.DELETED;
}
this.priority = buffer.getInt();
this.numberOfCrashes = buffer.getInt();
this.completedDate = buffer.getLong();
int errorFlag = buffer.getInt();
this.errorsOccurred = (1 == errorFlag);
} else {
this.status = ProcessingStatus.PENDING;
this.priority = DEFAULT_PRIORITY;
this.numberOfCrashes = 0;
this.completedDate = 0L;
this.errorsOccurred = false;
}
}
/**
* Constructs a coordination service node data data transfer object for an
* auto ingest manifest from values provided by the auto ingest system.
*
* @param status The processing status of the manifest.
* @param priority The priority of the manifest.
* @param numberOfCrashes The number of times auto ingest jobs for the
* manifest have crashed during processing.
* @param completedDate The date the auto ingest job for the manifest was
* completed.
*/
ManifestNodeData(ProcessingStatus status, int priority, int numberOfCrashes, Date completedDate, boolean errorOccurred) {
this.coordSvcNodeDataWasSet = false;
this.status = status;
this.priority = priority;
this.numberOfCrashes = numberOfCrashes;
this.completedDate = completedDate.getTime();
this.errorsOccurred = errorOccurred;
}
/**
* Indicates whether or not the coordination service node data was set,
* i.e., this object was constructed from raw bytes from the ccordination
* service node for the manifest.
*
* @return True or false.
*/
// RJCTODO: This is confusing, consider changing the API so that the use case is to
// check the length of the node data from the coordination service before
// constructing an instance of this object. That would be much more clear!
boolean coordSvcNodeDataWasSet() {
return this.coordSvcNodeDataWasSet;
}
/**
* Gets the processing status of the manifest
*
* @return The processing status of the manifest.
*/
ProcessingStatus getStatus() {
return this.status;
}
/**
* Sets the processing status of the manifest
*
* @param status The processing status of the manifest.
*/
void setStatus(ProcessingStatus status) {
this.status = status;
}
/**
* Gets the priority of the manifest.
*
* @return The priority of the manifest.
*/
int getPriority() {
return this.priority;
}
/**
* Sets the priority of the manifest. A higher number indicates a higheer
* priority.
*
* @param priority The priority of the manifest.
*/
void setPriority(int priority) {
this.priority = priority;
}
/**
* Gets the number of times auto ingest jobs for the manifest have crashed
* during processing.
*
* @return The number of times auto ingest jobs for the manifest have
* crashed during processing.
*/
int getNumberOfCrashes() {
return this.numberOfCrashes;
}
/**
* Sets the number of times auto ingest jobs for the manifest have crashed
* during processing.
*
* @param numberOfCrashes The number of times auto ingest jobs for the
* manifest have crashed during processing.
*/
void setNumberOfCrashes(int numberOfCrashes) {
this.numberOfCrashes = numberOfCrashes;
}
/**
* Gets the date the auto ingest job for the manifest was completed.
*
* @return The date the auto ingest job for the manifest was completed. The
* epoch (January 1, 1970, 00:00:00 GMT) indicates the date is not
* set, i.e., Date.getTime() returns 0L.
*/
Date getCompletedDate() {
return new Date(this.completedDate);
}
/**
* Sets the date the auto ingest job for the manifest was completed.
*
* @param completedDate The date the auto ingest job for the manifest was
* completed. Use the epoch (January 1, 1970, 00:00:00
* GMT) to indicate the date is not set, i.e., new
* Date(0L).
*/
void setCompletedDate(Date completedDate) {
this.completedDate = completedDate.getTime();
}
/**
* Queries whether or not any errors occurred during the processing of the
* auto ingest job for the manifest.
*
* @return True or false.
*/
boolean getErrorsOccurred() {
return this.errorsOccurred;
}
/**
* Sets whether or not any errors occurred during the processing of the auto
* ingest job for the manifest.
*
* @param errorsOccurred True or false.
*/
void setErrorsOccurred(boolean errorsOccurred) {
this.errorsOccurred = errorsOccurred;
}
/**
* Gets the node data as raw bytes that can be sent to the coordination
* service.
*
* @return The manifest node data as a byte array.
*/
byte[] toArray() {
ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES * 4 + Long.BYTES);
buffer.putInt(this.status.ordinal());
buffer.putInt(this.priority);
buffer.putInt(this.numberOfCrashes);
buffer.putLong(this.completedDate);
buffer.putInt(this.errorsOccurred ? 1 : 0);
return buffer.array();
}
/**
* Processing status for the auto ingest job for the manifest.
*/
enum ProcessingStatus {
PENDING,
PROCESSING,
COMPLETED,
DELETED
}
}

View File

@ -0,0 +1,178 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.io.File;
import java.io.FilenameFilter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.casemodule.GeneralFilter;
final class PathUtils {
private static final List<String> CASE_METADATA_FILE_EXTS = Arrays.asList(new String[]{CaseMetadata.getFileExtension()});
private static final GeneralFilter caseMetadataFileFilter = new GeneralFilter(CASE_METADATA_FILE_EXTS, "Autopsy Case File");
/**
* Searches a given folder for the most recently modified case folder for a
* case.
*
* @param folderToSearch The folder to be searched.
* @param caseName The name of the case for which a case folder is to
* be found.
*
* @return The path of the case folder, or null if it is not found.
*/
static Path findCaseDirectory(Path folderToSearch, String caseName) {
File searchFolder = new File(folderToSearch.toString());
if (!searchFolder.isDirectory()) {
return null;
}
Path caseFolderPath = null;
String[] candidateFolders = searchFolder.list(new CaseFolderFilter(caseName));
long mostRecentModified = 0;
for (String candidateFolder : candidateFolders) {
File file = new File(candidateFolder);
if (file.lastModified() >= mostRecentModified) {
mostRecentModified = file.lastModified();
caseFolderPath = Paths.get(folderToSearch.toString(), file.getPath());
}
}
return caseFolderPath;
}
/**
* Gets a listing of case folders in a given folder.
*
* @param folderToSearch The path of the folder to search.
*
* @return A list of the output case folder paths.
*/
static List<Path> findCaseFolders(Path folderToSearch) { // RJCTODO: Rename
File searchFolder = new File(folderToSearch.toString());
if (!searchFolder.isDirectory()) {
return Collections.emptyList();
}
String[] caseFolders = searchFolder.list(new CaseFolderFilter(null));
List<Path> caseFolderPaths = new ArrayList<>();
for (String path : caseFolders) {
caseFolderPaths.add(Paths.get(folderToSearch.toString(), path));
}
return caseFolderPaths;
}
/**
* Determines whether or not there is a case metadata file in a given
* folder.
*
* @param folderPath Path to the folder to search.
*
* @return True or false.
*/
static boolean hasCaseMetadataFile(Path folderPath) {
/**
* TODO: If need be, this can be rewritten without the FilenameFilter so
* that it does not necessarily visit every file in the folder.
*/
File folder = folderPath.toFile();
if (!folder.isDirectory()) {
return false;
}
String[] caseDataFiles = folder.list((File folder1, String fileName) -> {
File file = new File(folder1, fileName);
if (file.isFile()) {
return caseMetadataFileFilter.accept(file);
}
return false;
});
return caseDataFiles.length != 0;
}
/**
* Extracts the case name from a case folder path.
*
* @param caseFolderPath A case folder path.
*
* @return A case name, with the time stamp suffix removed.
*/
static String caseNameFromCaseDirectoryPath(Path caseFolderPath) {
String caseName = caseFolderPath.getFileName().toString();
if (caseName.length() > TimeStampUtils.getTimeStampLength()) {
return caseName.substring(0, caseName.length() - TimeStampUtils.getTimeStampLength());
} else {
return caseName;
}
}
/**
* Creates a case folder path. Does not create the folder described by the
* path.
*
* @param caseFoldersPath The root case folders path.
* @param caseName The name of the case.
*
* @return A case folder path with a time stamp suffix.
*/
static Path createCaseFolderPath(Path caseFoldersPath, String caseName) { // RJCTODO: Rename
String folderName = caseName + "_" + TimeStampUtils.createTimeStamp();
return Paths.get(caseFoldersPath.toString(), folderName);
}
private static class CaseFolderFilter implements FilenameFilter {
private final String caseName;
CaseFolderFilter(String caseName) {
this.caseName = caseName;
}
@Override
public boolean accept(File folder, String fileName) {
File file = new File(folder, fileName);
if (file.isDirectory() && fileName.length() > TimeStampUtils.getTimeStampLength()) {
Path filePath = Paths.get(file.getPath());
if (TimeStampUtils.endsWithTimeStamp(fileName)) {
if (null != caseName) {
String fileNamePrefix = fileName.substring(0, fileName.length() - TimeStampUtils.getTimeStampLength());
if (fileNamePrefix.equals(caseName)) {
return hasCaseMetadataFile(filePath);
}
} else {
return hasCaseMetadataFile(filePath);
}
}
}
return false;
}
}
/**
* Supress creation of instances of this class.
*/
private PathUtils() {
}
}

View File

@ -0,0 +1,240 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.swing.SwingUtilities;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.casemodule.AddImageAction;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CaseActionException;
import org.sleuthkit.autopsy.casemodule.CaseNewAction;
import org.sleuthkit.autopsy.experimental.configuration.AutoIngestUserPreferences;
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.experimental.coordinationservice.CoordinationService.CoordinationServiceException;
/**
* Handles opening, locking, and unlocking cases in review mode. Instances of
* this class are tightly coupled to the Autopsy "current case" concept and the
* Autopsy UI, and cases must be opened by code executing in the event
* dispatch thread (EDT). Because of the tight coupling to the UI, exception
* messages are deliberately user-friendly.
*/
final class ReviewModeCaseManager implements PropertyChangeListener {
/*
* Provides uniform exceptions with user-friendly error messages.
*/
final class ReviewModeCaseManagerException extends Exception {
private static final long serialVersionUID = 1L;
private ReviewModeCaseManagerException(String message) {
super(message);
}
private ReviewModeCaseManagerException(String message, Throwable cause) {
super(message, cause);
}
}
private static final Logger logger = Logger.getLogger(ReviewModeCaseManager.class.getName());
private static ReviewModeCaseManager instance;
private CoordinationService.Lock currentCaseLock;
/**
* Gets the review mode case manager.
*
* @return The review mode case manager singleton.
*/
synchronized static ReviewModeCaseManager getInstance() {
if (instance == null) {
/*
* Two stage construction is used here to avoid allowing "this"
* reference to escape from the constructor via registering as an
* PropertyChangeListener. This is to ensure that a partially
* constructed manager is not published to other threads.
*/
instance = new ReviewModeCaseManager();
Case.addPropertyChangeListener(instance);
}
return instance;
}
/**
* Constructs a review mode case manager to handles opening, locking, and
* unlocking cases in review mode. Instances of this class are tightly
* coupled to the Autopsy "current case" concept and the Autopsy UI,
* and cases must be opened by code executing in the event dispatch thread
* (EDT). Because of the tight coupling to the UI, exception messages are
* deliberately user-friendly.
*
*/
private ReviewModeCaseManager() {
/*
* Disable the new case action because review mode is only for looking
* at cases created by automated ingest.
*/
CallableSystemAction.get(CaseNewAction.class).setEnabled(false);
/*
* Permanently delete the "Open Recent Cases" item in the "File" menu.
* This is quite drastic, as it also affects Autopsy standalone mode on
* this machine, but review mode is only for looking at cases created by
* automated ingest.
*/
FileObject root = FileUtil.getConfigRoot();
FileObject openRecentCasesMenu = root.getFileObject("Menu/Case/OpenRecentCase");
if (openRecentCasesMenu != null) {
try {
openRecentCasesMenu.delete();
} catch (IOException ex) {
ReviewModeCaseManager.logger.log(Level.WARNING, "Unable to remove Open Recent Cases file menu item", ex);
}
}
}
/*
* Gets a list of the cases in the top level case folder used by automated
* ingest.
*/
List<AutoIngestCase> getCases() {
List<AutoIngestCase> cases = new ArrayList<>();
List<Path> caseFolders = PathUtils.findCaseFolders(Paths.get(AutoIngestUserPreferences.getAutoModeResultsFolder()));
for (Path caseFolderPath : caseFolders) {
cases.add(new AutoIngestCase(caseFolderPath));
}
return cases;
}
/**
* Attempts to open a case as the current case. Assumes it is called by code
* executing in the event dispatch thread (EDT).
*
* @param caseMetadataFilePath Path to the case metadata file.
*
* @throws ReviewModeCaseManagerException
*/
/*
* TODO (RC): With a little work, the lock acquisition/release could be done
* by a thread in a single thread executor, removing the "do it in the EDT"
* requirement
*/
synchronized void openCaseInEDT(Path caseMetadataFilePath) throws ReviewModeCaseManagerException {
Path caseFolderPath = caseMetadataFilePath.getParent();
try {
/*
* Acquire a lock on the case folder. If the lock cannot be
* acquired, the case cannot be opened.
*/
currentCaseLock = CoordinationService.getInstance(CoordinationServiceNamespace.getRoot()).tryGetSharedLock(CoordinationService.CategoryNode.CASES, caseFolderPath.toString());
if (null == currentCaseLock) {
throw new ReviewModeCaseManagerException("Could not get shared access to multi-user case folder");
}
/*
* Open the case.
*/
Case.open(caseMetadataFilePath.toString());
/**
* Disable the add data source action in review mode. This has to be
* done here because Case.open() calls Case.doCaseChange() and the
* latter method enables the action. Since Case.doCaseChange()
* enables the menus on EDT by calling SwingUtilities.invokeLater(),
* we have to do the same thing here to maintain the order of
* execution.
*/
SwingUtilities.invokeLater(() -> {
CallableSystemAction.get(AddImageAction.class).setEnabled(false);
});
} catch (CoordinationServiceException | ReviewModeCaseManagerException | CaseActionException ex) {
/*
* Release the coordination service lock on the case folder.
*/
try {
if (currentCaseLock != null) {
currentCaseLock.release();
currentCaseLock = null;
}
} catch (CoordinationService.CoordinationServiceException exx) {
logger.log(Level.SEVERE, String.format("Error deleting legacy LOCKED state file for case at %s", caseFolderPath), exx);
}
if (ex instanceof CoordinationServiceException) {
throw new ReviewModeCaseManagerException("Could not get access to the case folder from the coordination service, contact administrator", ex);
} else if (ex instanceof IOException) {
throw new ReviewModeCaseManagerException("Could not write to the case folder, contact adminstrator", ex);
} else if (ex instanceof CaseActionException) {
/*
* CaseActionExceptions have user friendly error messages.
*/
throw new ReviewModeCaseManagerException(String.format("Could not open the case (%s), contract administrator", ex.getMessage()), ex);
} else if (ex instanceof ReviewModeCaseManagerException) {
throw (ReviewModeCaseManagerException) ex;
}
}
}
/**
* @inheritDoc
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())
&& null != evt.getOldValue()
&& null == evt.getNewValue()) {
/*
* When a case is closed, release the coordination service lock on
* the case folder. This must be done in the EDT because it was
* acquired in the EDT via openCase().
*/
if (null != currentCaseLock) {
try {
SwingUtilities.invokeAndWait(() -> {
try {
currentCaseLock.release();
currentCaseLock = null;
} catch (CoordinationService.CoordinationServiceException ex) {
logger.log(Level.SEVERE, String.format("Failed to release the coordination service lock with path %s", currentCaseLock.getNodePath()), ex);
currentCaseLock = null;
}
});
} catch (InterruptedException | InvocationTargetException ex) {
logger.log(Level.SEVERE, String.format("Failed to release the coordination service lock with path %s", currentCaseLock.getNodePath()), ex);
currentCaseLock = null;
}
}
}
}
}

View File

@ -0,0 +1,237 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="rbGroupHistoryLength">
</Component>
</NonVisualComponents>
<Properties>
<Property name="name" type="java.lang.String" value="Completed Cases" noResource="true"/>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="13" max="-2" attributes="0"/>
<Component id="bnOpen" min="-2" pref="80" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="bnRefresh" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="bnShowLog" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="panelFilter" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="scrollPaneTable" pref="1007" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="43" max="-2" attributes="0"/>
<Component id="scrollPaneTable" min="-2" pref="450" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="32767" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="panelFilter" min="-2" pref="130" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="bnOpen" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnRefresh" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnShowLog" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="bnOpen">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ReviewModeCasePanel.bnOpen.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOpenActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JScrollPane" name="scrollPaneTable">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="casesTable">
<Properties>
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="caseTableModel" type="code"/>
</Property>
<Property name="autoResizeMode" type="int" value="4"/>
<Property name="rowHeight" type="int" value="20"/>
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
<JTableSelectionModel selectionMode="0"/>
</Property>
</Properties>
<Events>
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="casesTableMouseClicked"/>
</Events>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="bnRefresh">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ReviewModeCasePanel.bnRefresh.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnRefreshActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JPanel" name="panelFilter">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="rbGroupLabel" min="-2" max="-2" attributes="0"/>
<Component id="rbAllCases" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="rbMonths" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="rbWeeks" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="rbDays" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="34" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="rbGroupLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="rbDays" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="rbWeeks" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="rbMonths" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="rbAllCases" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JRadioButton" name="rbAllCases">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="rbGroupHistoryLength"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ReviewModeCasePanel.rbAllCases.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="rbAllCasesItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="rbMonths">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="rbGroupHistoryLength"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ReviewModeCasePanel.rbMonths.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="rbMonthsItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="rbWeeks">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="rbGroupHistoryLength"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ReviewModeCasePanel.rbWeeks.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="rbWeeksItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="rbDays">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="rbGroupHistoryLength"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ReviewModeCasePanel.rbDays.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="rbDaysItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="rbGroupLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ReviewModeCasePanel.rbGroupLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="bnShowLog">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ReviewModeCasePanel.bnShowLog.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="ReviewModeCasePanel.bnShowLog.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnShowLogActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,626 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Desktop;
import java.nio.file.Paths;
import java.util.List;
import javax.swing.JPanel;
import java.awt.EventQueue;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.file.Path;
import java.util.Date;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.JOptionPane;
import javax.swing.JDialog;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import org.sleuthkit.autopsy.casemodule.StartupWindowProvider;
import java.awt.Cursor;
import java.io.IOException;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.experimental.autoingest.ReviewModeCaseManager.ReviewModeCaseManagerException;
/**
* A panel that allows a user to open cases created by automated ingest.
*/
public final class ReviewModeCasePanel extends JPanel {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(ReviewModeCasePanel.class.getName());
private static final AutoIngestCase.LastAccessedDateDescendingComparator reverseDateModifiedComparator = new AutoIngestCase.LastAccessedDateDescendingComparator();
private static final int CASE_COL_MIN_WIDTH = 30;
private static final int CASE_COL_MAX_WIDTH = 2000;
private static final int CASE_COL_PREFERRED_WIDTH = 300;
private static final int TIME_COL_MIN_WIDTH = 40;
private static final int TIME_COL_MAX_WIDTH = 250;
private static final int TIME_COL_PREFERRED_WIDTH = 160;
private static final int STATUS_COL_MIN_WIDTH = 55;
private static final int STATUS_COL_MAX_WIDTH = 250;
private static final int STATUS_COL_PREFERRED_WIDTH = 60;
private static final int MILLISECONDS_TO_WAIT_BEFORE_STARTING = 500; // RJCTODO: Shorten name
private static final int MILLISECONDS_TO_WAIT_BETWEEN_UPDATES = 30000; // RJCTODO: Shorten name
private ScheduledThreadPoolExecutor casesTableRefreshExecutor;
/*
* The JTable table model for the cases table presented by this view is
* defined by the following string, enum, and array.
*
* TODO (RC): Consider unifying this stuff in an enum as in
* AutoIngestDashboard to make it less error prone.
*/
private static final String CASE_HEADER = org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.CaseHeaderText");
private static final String CREATEDTIME_HEADER = org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.CreatedTimeHeaderText");
private static final String COMPLETEDTIME_HEADER = org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.LastAccessedTimeHeaderText");
private static final String STATUS_ICON_HEADER = org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.StatusIconHeaderText");
private static final String OUTPUT_FOLDER_HEADER = org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.OutputFolderHeaderText");
enum COLUMN_HEADERS {
CASE,
CREATEDTIME,
COMPLETEDTIME,
STATUS_ICON,
OUTPUTFOLDER // RJCTODO: Change name
}
private final String[] columnNames = {CASE_HEADER, CREATEDTIME_HEADER, COMPLETEDTIME_HEADER, STATUS_ICON_HEADER, OUTPUT_FOLDER_HEADER};
private DefaultTableModel caseTableModel;
private Path currentlySelectedCase = null;
/**
* Constructs a panel that allows a user to open cases created by automated
* ingest.
*/
public ReviewModeCasePanel(JDialog parent) {
caseTableModel = new DefaultTableModel(columnNames, 0) {
private static final long serialVersionUID = 1L;
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
initComponents();
/*
* Configure the columns of the cases table.
*/
TableColumn theColumn;
theColumn = casesTable.getColumn(CASE_HEADER);
theColumn.setCellRenderer(new GrayableCellRenderer());
theColumn.setMinWidth(CASE_COL_MIN_WIDTH);
theColumn.setMaxWidth(CASE_COL_MAX_WIDTH);
theColumn.setPreferredWidth(CASE_COL_PREFERRED_WIDTH);
theColumn.setWidth(CASE_COL_PREFERRED_WIDTH);
theColumn = casesTable.getColumn(CREATEDTIME_HEADER);
theColumn.setCellRenderer(new LongDateCellRenderer());
theColumn.setMinWidth(TIME_COL_MIN_WIDTH);
theColumn.setMaxWidth(TIME_COL_MAX_WIDTH);
theColumn.setPreferredWidth(TIME_COL_PREFERRED_WIDTH);
theColumn.setWidth(TIME_COL_PREFERRED_WIDTH);
theColumn = casesTable.getColumn(COMPLETEDTIME_HEADER);
theColumn.setCellRenderer(new LongDateCellRenderer());
theColumn.setMinWidth(TIME_COL_MIN_WIDTH);
theColumn.setMaxWidth(TIME_COL_MAX_WIDTH);
theColumn.setPreferredWidth(TIME_COL_PREFERRED_WIDTH);
theColumn.setWidth(TIME_COL_PREFERRED_WIDTH);
theColumn = casesTable.getColumn(STATUS_ICON_HEADER);
theColumn.setCellRenderer(new CaseStatusIconCellRenderer());
theColumn.setMinWidth(STATUS_COL_MIN_WIDTH);
theColumn.setMaxWidth(STATUS_COL_MAX_WIDTH);
theColumn.setPreferredWidth(STATUS_COL_PREFERRED_WIDTH);
theColumn.setWidth(STATUS_COL_PREFERRED_WIDTH);
casesTable.removeColumn(casesTable.getColumn(OUTPUT_FOLDER_HEADER));
/*
* Listen for row selection changes and set button state for the current
* selection.
*/
casesTable.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> {
//Ignore extra messages.
if (e.getValueIsAdjusting()) {
return;
}
setButtons();
});
/*
* Add a window state listener that starts and stops refreshing of the
* cases table.
*/
if (parent != null) {
parent.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
stopCasesTableRefreshes();
}
@Override
public void windowActivated(WindowEvent e) {
startCasesTableRefreshes();
}
@Override
public void windowClosed(WindowEvent e) {
stopCasesTableRefreshes();
}
});
}
}
/**
* Start doing periodic refreshes of the cases table.
*/
private void startCasesTableRefreshes() {
if (null == casesTableRefreshExecutor) {
casesTableRefreshExecutor = new ScheduledThreadPoolExecutor(1);
this.casesTableRefreshExecutor.scheduleAtFixedRate(() -> {
refreshCasesTable();
}, MILLISECONDS_TO_WAIT_BEFORE_STARTING, MILLISECONDS_TO_WAIT_BETWEEN_UPDATES, TimeUnit.MILLISECONDS);
}
}
/**
* Stop doing periodic refreshes of the cases table.
*/
private void stopCasesTableRefreshes() {
if (null != casesTableRefreshExecutor) {
casesTableRefreshExecutor.shutdown();
}
this.casesTableRefreshExecutor = null;
}
/*
* Updates the view presented by the panel.
*/
public void updateView() {
Thread thread = new Thread(() -> {
refreshCasesTable();
});
thread.start();
}
/**
* Gets the list of cases known to the review mode cases manager and
* refreshes the cases table.
*/
private void refreshCasesTable() {
try {
currentlySelectedCase = getSelectedCase();
List<AutoIngestCase> theModel = ReviewModeCaseManager.getInstance().getCases();
EventQueue.invokeLater(new CaseTableRefreshTask(theModel));
} catch (Exception ex) {
logger.log(Level.SEVERE, "Unexpected exception in refreshCasesTable", ex); //NON-NLS
}
}
/**
* Gets the current selection in the cases table.
*
* @return A path representing the current selected case, null if there is
* no selection.
*/
private Path getSelectedCase() {
try {
int selectedRow = casesTable.getSelectedRow();
if (selectedRow >= 0 && selectedRow < casesTable.getRowCount()) {
return Paths.get(caseTableModel.getValueAt(selectedRow, COLUMN_HEADERS.CASE.ordinal()).toString());
}
} catch (Exception ignored) {
return null;
}
return null;
}
/**
* Sets the current selection in the cases table.
*
* @param path The case folder path of the case to select.
*/
private void setSelectedCase(Path path) {
if (path != null) {
try {
for (int row = 0; row < casesTable.getRowCount(); ++row) {
Path temp = Paths.get(caseTableModel.getValueAt(row, COLUMN_HEADERS.CASE.ordinal()).toString());
if (temp.compareTo(path) == 0) { // found it
casesTable.setRowSelectionInterval(row, row);
return;
}
}
} catch (Exception ignored) {
casesTable.clearSelection();
}
}
casesTable.clearSelection();
}
/**
* Enables/disables the Open and Show Log buttons based on the case selected
* in the cases table.
*/
private void setButtons() {
boolean enabled = casesTable.getSelectedRow() >= 0 && casesTable.getSelectedRow() < casesTable.getRowCount();
bnOpen.setEnabled(enabled);
bnShowLog.setEnabled(enabled);
}
/**
* Opens a case.
*
* @param caseMetadataFilePath The path to the case metadata file.
*/
private void openCase(Path caseMetadataFilePath) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
ReviewModeCaseManager.getInstance().openCaseInEDT(caseMetadataFilePath);
stopCasesTableRefreshes();
StartupWindowProvider.getInstance().close();
} catch (ReviewModeCaseManagerException ex) {
logger.log(Level.SEVERE, String.format("Error while opening case with case metadata file path %s", caseMetadataFilePath), ex);
/*
* ReviewModeCaseManagerExceptions have user-friendly error
* messages.
*/
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
ex.getMessage(),
org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.cannotOpenCase"),
JOptionPane.ERROR_MESSAGE);
} finally {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
/**
* A task that refreshes the cases table using a list of auto ingest cases.
*/
private class CaseTableRefreshTask implements Runnable {
private final List<AutoIngestCase> cases;
CaseTableRefreshTask(List<AutoIngestCase> cases) {
setButtons();
this.cases = cases;
}
/**
* @inheritDoc
*/
@Override
public void run() {
cases.sort(reverseDateModifiedComparator);
caseTableModel.setRowCount(0);
long now = new Date().getTime();
for (AutoIngestCase autoIngestCase : cases) {
if (passesTimeFilter(now, autoIngestCase.getLastAccessedDate().getTime())) {
caseTableModel.addRow(new Object[]{
autoIngestCase.getCaseName(),
autoIngestCase.getCreationDate(),
autoIngestCase.getLastAccessedDate(),
(AutoIngestCase.CaseStatus.OK != autoIngestCase.getStatus()),
autoIngestCase.getCaseDirectoryPath().toString()});
}
}
setSelectedCase(currentlySelectedCase);
}
/**
* Indicates whether or not a time satisfies a time filter defined by
* this panel's time filter radio buttons.
*
* @param currentTime The current date and time in milliseconds from the
* Unix epoch.
* @param inputTime The date and time to be tested as milliseconds
* from the Unix epoch.
*/
private boolean passesTimeFilter(long currentTime, long inputTime) {
long numberOfUnits = 10;
long multiplier = 1;
if (rbAllCases.isSelected()) {
return true;
} else {
if (rbMonths.isSelected()) {
multiplier = 31;
} else {
if (rbWeeks.isSelected()) {
multiplier = 7;
} else {
if (rbDays.isSelected()) {
multiplier = 1;
}
}
}
}
return ((currentTime - inputTime) / (1000 * 60 * 60 * 24)) < (numberOfUnits * multiplier);
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
rbGroupHistoryLength = new javax.swing.ButtonGroup();
bnOpen = new javax.swing.JButton();
scrollPaneTable = new javax.swing.JScrollPane();
casesTable = new javax.swing.JTable();
bnRefresh = new javax.swing.JButton();
panelFilter = new javax.swing.JPanel();
rbAllCases = new javax.swing.JRadioButton();
rbMonths = new javax.swing.JRadioButton();
rbWeeks = new javax.swing.JRadioButton();
rbDays = new javax.swing.JRadioButton();
rbGroupLabel = new javax.swing.JLabel();
bnShowLog = new javax.swing.JButton();
setName("Completed Cases"); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(bnOpen, org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.bnOpen.text")); // NOI18N
bnOpen.setEnabled(false);
bnOpen.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnOpenActionPerformed(evt);
}
});
casesTable.setAutoCreateRowSorter(true);
casesTable.setModel(caseTableModel);
casesTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_ALL_COLUMNS);
casesTable.setRowHeight(20);
casesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
casesTable.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
casesTableMouseClicked(evt);
}
});
scrollPaneTable.setViewportView(casesTable);
org.openide.awt.Mnemonics.setLocalizedText(bnRefresh, org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.bnRefresh.text")); // NOI18N
bnRefresh.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnRefreshActionPerformed(evt);
}
});
rbGroupHistoryLength.add(rbAllCases);
rbAllCases.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(rbAllCases, org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.rbAllCases.text")); // NOI18N
rbAllCases.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
rbAllCasesItemStateChanged(evt);
}
});
rbGroupHistoryLength.add(rbMonths);
org.openide.awt.Mnemonics.setLocalizedText(rbMonths, org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.rbMonths.text")); // NOI18N
rbMonths.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
rbMonthsItemStateChanged(evt);
}
});
rbGroupHistoryLength.add(rbWeeks);
org.openide.awt.Mnemonics.setLocalizedText(rbWeeks, org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.rbWeeks.text")); // NOI18N
rbWeeks.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
rbWeeksItemStateChanged(evt);
}
});
rbGroupHistoryLength.add(rbDays);
org.openide.awt.Mnemonics.setLocalizedText(rbDays, org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.rbDays.text")); // NOI18N
rbDays.setName(""); // NOI18N
rbDays.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
rbDaysItemStateChanged(evt);
}
});
rbGroupLabel.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(rbGroupLabel, org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.rbGroupLabel.text")); // NOI18N
javax.swing.GroupLayout panelFilterLayout = new javax.swing.GroupLayout(panelFilter);
panelFilter.setLayout(panelFilterLayout);
panelFilterLayout.setHorizontalGroup(
panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelFilterLayout.createSequentialGroup()
.addContainerGap()
.addGroup(panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(rbGroupLabel)
.addComponent(rbAllCases)
.addComponent(rbMonths)
.addComponent(rbWeeks)
.addComponent(rbDays))
.addContainerGap(34, Short.MAX_VALUE))
);
panelFilterLayout.setVerticalGroup(
panelFilterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelFilterLayout.createSequentialGroup()
.addContainerGap()
.addComponent(rbGroupLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(rbDays)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(rbWeeks)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(rbMonths)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(rbAllCases)
.addContainerGap())
);
org.openide.awt.Mnemonics.setLocalizedText(bnShowLog, org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.bnShowLog.text")); // NOI18N
bnShowLog.setToolTipText(org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "ReviewModeCasePanel.bnShowLog.toolTipText")); // NOI18N
bnShowLog.setEnabled(false);
bnShowLog.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnShowLogActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(13, 13, 13)
.addComponent(bnOpen, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(bnRefresh)
.addGap(18, 18, 18)
.addComponent(bnShowLog)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(panelFilter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(20, 20, 20))
.addGroup(layout.createSequentialGroup()
.addComponent(scrollPaneTable, javax.swing.GroupLayout.DEFAULT_SIZE, 1007, Short.MAX_VALUE)
.addContainerGap())))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(43, 43, 43)
.addComponent(scrollPaneTable, javax.swing.GroupLayout.PREFERRED_SIZE, 450, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(layout.createSequentialGroup()
.addComponent(panelFilter, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(bnOpen)
.addComponent(bnRefresh)
.addComponent(bnShowLog))
.addGap(36, 36, 36))))
);
}// </editor-fold>//GEN-END:initComponents
/**
* Open button action
*
* @param evt -- The event that caused this to be called
*/
private void bnOpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOpenActionPerformed
Path caseMetadataFilePath = Paths.get((String) caseTableModel.getValueAt(casesTable.getSelectedRow(),
COLUMN_HEADERS.OUTPUTFOLDER.ordinal()),
caseTableModel.getValueAt(casesTable.getSelectedRow(), COLUMN_HEADERS.CASE.ordinal()) + CaseMetadata.getFileExtension());
openCase(caseMetadataFilePath);
}//GEN-LAST:event_bnOpenActionPerformed
/**
* Refresh button action
*
* @param evt -- The event that caused this to be called
*/
private void bnRefreshActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_bnRefreshActionPerformed
{//GEN-HEADEREND:event_bnRefreshActionPerformed
updateView();
}//GEN-LAST:event_bnRefreshActionPerformed
private void rbDaysItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbDaysItemStateChanged
if (rbDays.isSelected()) {
updateView();
}
}//GEN-LAST:event_rbDaysItemStateChanged
private void rbAllCasesItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbAllCasesItemStateChanged
if (rbAllCases.isSelected()) {
updateView();
}
}//GEN-LAST:event_rbAllCasesItemStateChanged
private void rbMonthsItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbMonthsItemStateChanged
if (rbMonths.isSelected()) {
updateView();
}
}//GEN-LAST:event_rbMonthsItemStateChanged
private void rbWeeksItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_rbWeeksItemStateChanged
if (rbWeeks.isSelected()) {
updateView();
}
}//GEN-LAST:event_rbWeeksItemStateChanged
private void bnShowLogActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnShowLogActionPerformed
int selectedRow = casesTable.getSelectedRow();
int rowCount = casesTable.getRowCount();
if (selectedRow >= 0 && selectedRow < rowCount) {
String thePath = (String) caseTableModel.getValueAt(selectedRow, COLUMN_HEADERS.OUTPUTFOLDER.ordinal());
Path pathToLog = AutoIngestJobLogger.getLogPath(Paths.get(thePath));
try {
if (pathToLog.toFile().exists()) {
Desktop.getDesktop().edit(pathToLog.toFile());
} else {
JOptionPane.showMessageDialog(this, org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "DisplayLogDialog.cannotFindLog"),
org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "DisplayLogDialog.unableToShowLogFile"), JOptionPane.ERROR_MESSAGE);
}
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error attempting to open case auto ingest log file %s", pathToLog), ex);
JOptionPane.showMessageDialog(this,
org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "DisplayLogDialog.cannotOpenLog"),
org.openide.util.NbBundle.getMessage(ReviewModeCasePanel.class, "DisplayLogDialog.unableToShowLogFile"),
JOptionPane.PLAIN_MESSAGE);
}
}
}//GEN-LAST:event_bnShowLogActionPerformed
private void casesTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_casesTableMouseClicked
if (evt.getClickCount() == 2) {
Path caseMetadataFilePath = Paths.get((String) caseTableModel.getValueAt(casesTable.getSelectedRow(),
COLUMN_HEADERS.OUTPUTFOLDER.ordinal()),
caseTableModel.getValueAt(casesTable.getSelectedRow(), COLUMN_HEADERS.CASE.ordinal()) + CaseMetadata.getFileExtension());
openCase(caseMetadataFilePath);
}
}//GEN-LAST:event_casesTableMouseClicked
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton bnOpen;
private javax.swing.JButton bnRefresh;
private javax.swing.JButton bnShowLog;
private javax.swing.JTable casesTable;
private javax.swing.JPanel panelFilter;
private javax.swing.JRadioButton rbAllCases;
private javax.swing.JRadioButton rbDays;
private javax.swing.ButtonGroup rbGroupHistoryLength;
private javax.swing.JLabel rbGroupLabel;
private javax.swing.JRadioButton rbMonths;
private javax.swing.JRadioButton rbWeeks;
private javax.swing.JScrollPane scrollPaneTable;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,49 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Component;
import java.text.SimpleDateFormat;
import javax.swing.JTable;
import static javax.swing.SwingConstants.CENTER;
/**
* A JTable cell renderer that renders a date represented as a long as a
* center-aligned, short-format date string. It also grays out the cell if the
* table is disabled.
*/
class ShortDateCellRenderer extends GrayableCellRenderer {
private static final long serialVersionUID = 1L;
private static final String FORMAT_STRING = "MM/dd HH:mm"; //NON-NLS
private static final SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT_STRING);
public ShortDateCellRenderer() {
setHorizontalAlignment(CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value != null) {
setText(dateFormat.format(value));
}
grayCellIfTableNotEnabled(table, isSelected);
return this;
}
}

View File

@ -0,0 +1,478 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Dimension;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Level;
import static javax.security.auth.callback.ConfirmationCallback.OK_CANCEL_OPTION;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import org.apache.commons.io.FileUtils;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.SingleUserCaseConverter;
import org.sleuthkit.autopsy.casemodule.SingleUserCaseConverter.ImportCaseData;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
public class SingleUserCaseImporter implements Runnable {
private static final String AIM_LOG_FILE_NAME = "auto_ingest_log.txt"; //NON-NLS
static final String CASE_IMPORT_LOG_FILE = "case_import_log.txt"; //NON-NLS
private static final String DOTAUT = ".aut"; //NON-NLS
private static final String SEP = System.getProperty("line.separator");
private static final String logDateFormat = "yyyy/MM/dd HH:mm:ss"; //NON-NLS
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(logDateFormat);
private final Object threadWaitNotifyLock = new Object();
private final ImportDoneCallback notifyOnComplete;
private final Path baseImageInput;
private final Path baseCaseInput;
private final Path baseImageOutput;
private final Path baseCaseOutput;
private final boolean copyImages;
private final boolean deleteCase;
private String oldCaseName = null;
private String newCaseName = null;
private int userAnswer = 0;
private PrintWriter writer;
public SingleUserCaseImporter(String baseImageInput, String baseCaseInput, String baseImageOutput, String baseCaseOutput, boolean copyImages, boolean deleteCase, ImportDoneCallback callback) {
this.baseImageInput = Paths.get(baseImageInput);
this.baseCaseInput = Paths.get(baseCaseInput);
this.baseImageOutput = Paths.get(baseImageOutput);
this.baseCaseOutput = Paths.get(baseCaseOutput);
this.copyImages = copyImages;
this.deleteCase = deleteCase;
this.notifyOnComplete = callback;
}
/**
* This causes iteration over all .aut files in the baseCaseInput path,
* calling SingleUserCaseConverter.importCase() for each one.
*/
public void importCases() throws Exception {
openLog(baseCaseOutput.toFile());
log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.StartingBatch")
+ baseCaseInput.toString() + " "
+ NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.to")
+ " " + baseCaseOutput.toString()); //NON-NLS
// iterate for .aut files
FindDotAutFolders dotAutFolders = new FindDotAutFolders();
try {
Path walked = Files.walkFileTree(baseCaseInput, dotAutFolders);
} catch (IOException ex) {
log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.ErrorFindingAutFiles") + " " + ex.getMessage()); //NON-NLS
}
ArrayList<ImportCaseData> ableToProcess = new ArrayList<>();
ArrayList<ImportCaseData> unableToProcess = new ArrayList<>();
SingleUserCaseConverter scc = new SingleUserCaseConverter();
// validate we can convert the .aut file, one by one
for (FoundAutFile f : dotAutFolders.getCandidateList()) {
this.oldCaseName = f.getPath().getFileName().toString();
// Test image output folder for uniqueness, find a unique folder for it if we can
File specificOutputFolder = baseImageOutput.resolve(oldCaseName).toFile();
String newImageName = oldCaseName;
if (specificOutputFolder.exists()) {
// Not unique. add numbers before timestamp to specific image output name
String timeStamp = TimeStampUtils.getTimeStampOnly(oldCaseName);
newImageName = TimeStampUtils.removeTimeStamp(oldCaseName);
int number = 1;
String temp = ""; //NON-NLS
while (specificOutputFolder.exists()) {
if (number == Integer.MAX_VALUE) {
// It never became unique, so give up.
throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.NonUniqueOutputFolder") + newImageName); //NON-NLS
}
temp = newImageName + "_" + Integer.toString(number) + timeStamp; //NON-NLS
specificOutputFolder = baseImageOutput.resolve(temp).toFile();
++number;
}
newImageName = temp;
}
Path imageOutput = baseImageOutput.resolve(newImageName);
imageOutput.toFile().mkdirs(); // Create image output folder
// Test case output folder for uniqueness, find a unique folder for it if we can
specificOutputFolder = baseCaseOutput.resolve(oldCaseName).toFile();
newCaseName = oldCaseName;
if (specificOutputFolder.exists()) {
// not unique. add numbers before timestamp to specific case output name
String timeStamp = TimeStampUtils.getTimeStampOnly(oldCaseName); //NON-NLS
newCaseName = TimeStampUtils.removeTimeStamp(oldCaseName);
int number = 1;
String temp = ""; //NON-NLS
while (specificOutputFolder.exists()) {
if (number == Integer.MAX_VALUE) {
// It never became unique, so give up.
throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.NonUniqueOutputFolder") + newCaseName); //NON-NLS
}
temp = newCaseName + "_" + Integer.toString(number) + timeStamp; //NON-NLS
specificOutputFolder = baseCaseOutput.resolve(temp).toFile();
++number;
}
newCaseName = temp;
}
Path caseOutput = baseCaseOutput.resolve(newCaseName);
caseOutput.toFile().mkdirs(); // Create case output folder
/**
* Test if the input path has a corresponding image input folder and
* no repeated case names in the path. If both of these conditions
* are true, we can process this case, otherwise not.
*/
// Check that there is an image folder if they are trying to copy it
boolean canProcess = true;
Path imageInput = null;
String relativeCaseName = TimeStampUtils.removeTimeStamp(baseCaseInput.relativize(f.getPath()).toString());
Path testImageInputsFromOldCase = Paths.get(baseImageInput.toString(), relativeCaseName);
if (copyImages) {
if (!testImageInputsFromOldCase.toFile().isDirectory()) {
// Mark that we are unable to process this item
canProcess = false;
} else {
imageInput = testImageInputsFromOldCase;
}
if (imageInput == null) {
throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.SourceImageMissing") + " " + f.getPath()); //NON-NLS
}
// If case name is in the image path, it causes bad things to happen with the parsing. Test for this.
for (int x = 0; x < imageInput.getNameCount(); ++x) {
if (oldCaseName.toLowerCase().equals(imageInput.getName(x).toString().toLowerCase())) {
// Mark that we are unable to process this item
canProcess = false;
}
}
} else {
imageInput = testImageInputsFromOldCase;
}
// Create an Import Case Data object for this case
SingleUserCaseConverter.ImportCaseData icd = scc.new ImportCaseData(
imageInput,
f.getPath(),
imageOutput,
caseOutput,
oldCaseName,
newCaseName,
f.getAutFile().toString(),
f.getFolderName().toString(),
copyImages,
deleteCase);
if (canProcess) {
ableToProcess.add(icd);
} else {
unableToProcess.add(icd);
}
}
// Create text to be populated in the confirmation dialog
StringBuilder casesThatWillBeProcessed = new StringBuilder();
StringBuilder casesThatWillNotBeProcessed = new StringBuilder();
casesThatWillBeProcessed.append(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.WillImport")).append(SEP); // NON-NLS
if (ableToProcess.isEmpty()) {
casesThatWillBeProcessed.append(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.None")).append(SEP); // NON-NLS
} else {
for (ImportCaseData i : ableToProcess) {
casesThatWillBeProcessed.append(i.getCaseInputFolder().toString()).append(SEP);
}
}
if (!unableToProcess.isEmpty()) {
casesThatWillNotBeProcessed.append(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.WillNotImport")).append(SEP); // NON-NLS
for (ImportCaseData i : unableToProcess) {
casesThatWillNotBeProcessed.append(i.getCaseInputFolder().toString()).append(SEP);
}
}
JTextArea jta = new JTextArea(casesThatWillBeProcessed.toString() + SEP + casesThatWillNotBeProcessed.toString());
jta.setEditable(false);
JScrollPane jsp = new JScrollPane(jta) {
private static final long serialVersionUID = 1L;
@Override
public Dimension getPreferredSize() {
return new Dimension(700, 480);
}
};
// Show confirmation dialog
SwingUtilities.invokeLater(() -> {
userAnswer = JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
jsp,
NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.ContinueWithImport"), // NON-NLS
OK_CANCEL_OPTION);
synchronized (threadWaitNotifyLock) {
threadWaitNotifyLock.notify();
}
});
// Wait while the user handles the confirmation dialog
synchronized (threadWaitNotifyLock) {
try {
threadWaitNotifyLock.wait();
} catch (InterruptedException ex) {
Logger.getLogger(SingleUserCaseImporter.class.getName()).log(Level.SEVERE, "Threading Issue", ex); //NON-NLS
throw new Exception(ex);
}
}
// If the user wants to proceed, do so.
if (userAnswer == JOptionPane.OK_OPTION) {
boolean result = true; // if anything went wrong, result becomes false.
// Feed .aut files in one by one for processing
for (ImportCaseData i : ableToProcess) {
try {
log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.StartedProcessing")
+ i.getCaseInputFolder()
+ " " + NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.to") + " "
+ i.getCaseOutputFolder()); //NON-NLS
SingleUserCaseConverter.importCase(i);
handleAutoIngestLog(i);
log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.FinishedProcessing")
+ i.getCaseInputFolder()
+ " " + NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.to") + " "
+ i.getCaseOutputFolder()); //NON-NLS
} catch (Exception ex) {
log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.FailedToComplete")
+ i.getCaseInputFolder()
+ " " + NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.to") + " "
+ i.getCaseOutputFolder() + " " + ex.getMessage()); //NON-NLS
result = false;
}
}
log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.CompletedBatch")
+ baseCaseInput.toString()
+ " " + NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.to") + " "
+ baseCaseOutput.toString()); //NON-NLS
closeLog();
if (notifyOnComplete != null) {
notifyOnComplete.importDoneCallback(result, ""); // NON-NLS
}
} else {
// The user clicked cancel. Abort.
log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.AbortingBatch")
+ baseCaseInput.toString()
+ " " + NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.to") + " "
+ baseCaseOutput.toString()); //NON-NLS
closeLog();
if (notifyOnComplete != null) {
notifyOnComplete.importDoneCallback(false, NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.Cancelled")); // NON-NLS
}
}
}
@Override
public void run() {
try {
importCases();
} catch (Exception ex) {
log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.FailedToComplete")
+ baseCaseInput.toString()
+ " " + NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.to") + " "
+ baseCaseOutput.toString()
+ " " + ex.getMessage()); //NON-NLS
closeLog();
if (notifyOnComplete != null) {
notifyOnComplete.importDoneCallback(false, ex.getMessage()); // NON-NLS
}
}
}
/**
* Move the Auto Ingest log if we can
*
* @param icd the Import Case Data structure detailing where the files are
*/
void handleAutoIngestLog(ImportCaseData icd) {
try {
Path source = icd.getCaseInputFolder().resolve(AIM_LOG_FILE_NAME);
Path destination = icd.getCaseOutputFolder().resolve(AIM_LOG_FILE_NAME);
if (source.toFile().exists()) {
FileUtils.copyFile(source.toFile(), destination.toFile());
}
try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(destination.toString(), true)))) {
out.println(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.ImportedAsMultiUser") + new Date()); //NON-NLS
} catch (IOException e) {
// If unable to log it, no problem, move on
}
File oldIngestLog = Paths.get(icd.getCaseOutputFolder().toString(), NetworkUtils.getLocalHostName(), AIM_LOG_FILE_NAME).toFile();
if (oldIngestLog.exists()) {
oldIngestLog.delete();
}
} catch (Exception ex) {
// If unable to copy Auto Ingest log, no problem, move on
}
}
/**
* Open the case import log in the base output folder.
*
* @param location holds the path to the log file
*/
private void openLog(File location) {
location.mkdirs();
File logFile = Paths.get(location.toString(), CASE_IMPORT_LOG_FILE).toFile();
try {
writer = new PrintWriter(new BufferedWriter(new FileWriter(logFile, logFile.exists())), true);
} catch (IOException ex) {
writer = null;
Logger.getLogger(SingleUserCaseImporter.class.getName()).log(Level.WARNING, "Error opening log file " + logFile.toString(), ex); //NON-NLS
}
}
/**
* Log a message to the case import log in the base output folder.
*
* @param message the message to log.
*/
private void log(String message) {
if (writer != null) {
writer.println(String.format("%s %s", simpleDateFormat.format((Date.from(Instant.now()).getTime())), message)); //NON-NLS
}
}
/**
* Close the case import log
*/
private void closeLog() {
if (writer != null) {
writer.close();
}
}
/**
* Extend SimpleFileVisitor to find all the cases to process based upon
* presence of .aut files.
*/
private class FindDotAutFolders extends SimpleFileVisitor<Path> {
private final ArrayList<FoundAutFile> candidateList;
public FindDotAutFolders() {
this.candidateList = new ArrayList<>();
}
/**
* Handle comparing .aut file and containing folder names without
* timestamps on either one. It strips them off if they exist.
*
* @param directory the directory we are currently visiting.
* @param attrs file attributes.
*
* @return CONTINUE if we want to carry on, SKIP_SUBTREE if we've found
* a .aut file, precluding searching any deeper into this
* folder.
*
* @throws IOException
*/
@Override
public FileVisitResult preVisitDirectory(Path directory, BasicFileAttributes attrs) throws IOException {
// Find all files that end in .aut
File[] dotAutFiles = directory.toFile().listFiles((File dir, String name) -> name.toLowerCase().endsWith(DOTAUT));
for (File specificFile : dotAutFiles) {
// If the case name ends in a timestamp, strip it off
String sanitizedCaseName = specificFile.getName();
sanitizedCaseName = TimeStampUtils.removeTimeStamp(sanitizedCaseName);
// If the folder ends in a timestamp, strip it off
String sanitizedFolderName = TimeStampUtils.removeTimeStamp(directory.getFileName().toString());
// If file and folder match, found leaf node case
if (sanitizedCaseName.toLowerCase().startsWith(sanitizedFolderName.toLowerCase())) {
candidateList.add(new FoundAutFile(directory, Paths.get(sanitizedCaseName), Paths.get(sanitizedFolderName)));
return FileVisitResult.SKIP_SUBTREE;
}
}
// If no matching .aut files, continue to traverse subfolders
return FileVisitResult.CONTINUE;
}
/**
* Returns the list of folders we've found that need to be looked at for
* possible import from single-user to multi-user cases.
*
* @return the candidateList
*/
public ArrayList<FoundAutFile> getCandidateList() {
return candidateList;
}
}
/**
* This class holds information about .aut files that have been found by the
* FileWalker.
*/
public class FoundAutFile {
private final Path path;
private final Path autFile;
private final Path folderName;
public FoundAutFile(Path path, Path autFile, Path folderName) {
this.path = path;
this.autFile = autFile;
this.folderName = folderName;
}
Path getPath() {
return this.path;
}
Path getAutFile() {
return this.autFile;
}
Path getFolderName() {
return this.folderName;
}
}
}

View File

@ -0,0 +1,104 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.core.UserPreferencesException;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.experimental.configuration.AutoIngestUserPreferences;
/**
* Write auto-ingest status updates to a database.
*/
public class StatusDatabaseLogger {
/**
* Log the current status to the database using the database
* parameters saved in AutoIngestUserPreferences.
* @param message Current status message
* @param isError true if we're in an error state, false otherwise
* @throws SQLException
*/
public static void logToStatusDatabase(String message, boolean isError) throws SQLException, UserPreferencesException{
try{
Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException ex){
java.util.logging.Logger SYS_LOGGER = AutoIngestSystemLogger.getLogger();
SYS_LOGGER.log(Level.WARNING, "Error loading postgresql driver", ex);
}
try (Connection connection = DriverManager.getConnection("jdbc:postgresql://"
+ AutoIngestUserPreferences.getLoggingDatabaseHostnameOrIP()
+ ":" + AutoIngestUserPreferences.getLoggingPort()
+ "/" + AutoIngestUserPreferences.getLoggingDatabaseName(),
AutoIngestUserPreferences.getLoggingUsername(),
AutoIngestUserPreferences.getLoggingPassword());
Statement statement = connection.createStatement();) {
logToStatusDatabase(statement, message, isError);
}
}
/**
* Log the current status to the database using an already
* configured Statement.
* @param statement SQL statement (must have already been created)
* @param message Current status message
* @param isError true if we're in an error state, false otherwise
* @throws SQLException
*/
public static void logToStatusDatabase(Statement statement, String message, boolean isError) throws SQLException{
if((statement == null) || statement.isClosed()){
throw new SQLException("SQL Statement is null/closed");
}
int status;
if(isError){
status = 1;
} else {
status = 0;
}
String timestamp = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date());
String checkForPreviousEntry = "SELECT * FROM statusUpdates WHERE tool='" + UserPreferences.getAppName() + "' AND " +
"node='" + NetworkUtils.getLocalHostName() + "'";
ResultSet resultSet = statement.executeQuery(checkForPreviousEntry);
String logMessage;
if(resultSet.next()){
logMessage = "UPDATE statusUpdates SET reportTime='" + timestamp +
"', message='" + message + "', status=" + status
+ " WHERE tool='" + UserPreferences.getAppName() + "' AND node='" + NetworkUtils.getLocalHostName() + "'";
} else {
logMessage = "INSERT INTO statusUpdates (tool, node, reportTime, message, status) " +
"VALUES ('" + UserPreferences.getAppName()
+ "', '" + NetworkUtils.getLocalHostName() +
"', '" +
timestamp + "', '" + message + "', '" + status + "')";
}
statement.execute(logMessage);
}
}

View File

@ -0,0 +1,104 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility methods for working with strings with the time-stamp suffixes used by
* auto ingest.
*/
public final class TimeStampUtils {
/*
* Sample time stamp suffix: 2015_02_02_12_10_31
*/
private static final Pattern timeStampPattern = Pattern.compile("\\d{4}_\\d{2}_\\d{2}_\\d{2}_\\d{2}_\\d{2}$");
private static final int LENGTH_OF_DATE_TIME_STAMP = 20; // length of the above time stamp
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
/**
* Checks whether a string ends with an auto ingest time stamp.
*
* @param inputString The string to check.
*
* @return True or false.
*/
public static boolean endsWithTimeStamp(String inputString) {
Matcher m = timeStampPattern.matcher(inputString);
return m.find();
}
/**
* Gets the fixed length of the auto-ingest time stamp suffix.
*
* @return The length.
*/
public static int getTimeStampLength() {
return LENGTH_OF_DATE_TIME_STAMP;
}
/**
* Creates an auto ingest time stamp suffix using the current time.
*
* @return The suffix.
*/
public static String createTimeStamp() {
return dateFormat.format(Calendar.getInstance().getTime());
}
/**
* Removes an auto ingest timestamp suffix, if it present.
*
* @param inputString The string to trim.
*
* @return The trimmed string.
*/
public static String removeTimeStamp(String inputString) {
String trimmedString = inputString;
if (inputString != null && endsWithTimeStamp(inputString)) {
trimmedString = inputString.substring(0, inputString.length() - getTimeStampLength());
}
return trimmedString;
}
/**
* Gets the auto ingest time stamp suffix from a string, if it is present.
*
* @param inputString the name to check for a timestamp
*
* @return The time stamp, may be the empty.
*/
public static String getTimeStampOnly(String inputString) {
String timeStamp = "";
if (inputString != null && endsWithTimeStamp(inputString)) {
timeStamp = inputString.substring(inputString.length() - getTimeStampLength(), inputString.length());
}
return timeStamp;
}
/*
* Private contructor to prevent instantiation.
*/
private TimeStampUtils() {
}
}

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
<filesystem>
<!-- ======================================================
Actions
====================================================== -->
<folder name="Actions">
<folder name="Case">
<file name="org-sleuthkit-autopsy-experimental-autoingest-AutoIngestCaseOpenAction.instance">
<attr name="delegate" newvalue="org.sleuthkit.autopsy.experimental.autoingest.AutoIngestCaseOpenAction"/>
<attr name="displayName" stringvalue="Open Case"/>
<attr name="instanceCreate" methodvalue="org.openide.awt.Actions.alwaysEnabled"/>
<attr name="noIconInMenu" boolvalue="false"/>
</file>
</folder>
</folder>
<!-- ======================================================
Menu hidden
=========================================================== -->
<folder name="Menu">
<folder name="Help">
<file name="org-sleuthkit-autopsy-corecomponents-CustomAboutAction.shadow_hidden"/>
</folder>
<folder name="Case">
<file name="org-sleuthkit-autopsy-casemodule-CaseOpenAction.shadow_hidden"/>
<file name="org-sleuthkit-autopsy-experimental-autoingest-AutoIngestCaseOpenAction.shadow">
<attr name="originalFile" stringvalue="Actions/Case/org-sleuthkit-autopsy-experimental-autoingest-AutoIngestCaseOpenAction.instance"/>
<attr name="position" intvalue="101"/>
</file>
</folder>
</folder>
</filesystem>

View File

@ -0,0 +1,375 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="1" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="jPanelAutoIngestJobSettings" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="16" pref="16" max="-2" attributes="0"/>
<Component id="spMainScrollPane" pref="640" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace min="-2" pref="16" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
<Component id="spMainScrollPane" min="-2" pref="106" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jPanelAutoIngestJobSettings" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="spMainScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextArea" name="tbWarning">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="columns" type="int" value="20"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Monospaced" size="14" style="1"/>
</Property>
<Property name="rows" type="int" value="5"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.tbWarning.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="autoscrolls" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="jPanelAutoIngestJobSettings">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Automated Ingest Job Settings">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.jPanelAutoIngestJobSettings.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
<Property name="name" type="java.lang.String" value="Automated Ingest Job Settings" noResource="true"/>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="cbTimeoutEnabled" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="1" max="-2" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="lbInputScanInterval" min="-2" max="-2" attributes="0"/>
<EmptySpace min="49" pref="49" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Component id="lbRetriesAllowed" min="-2" max="-2" attributes="0"/>
<EmptySpace min="54" pref="54" max="-2" attributes="0"/>
</Group>
<Component id="lbConcurrentJobsPerCase" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbNumberOfThreads" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="0" pref="41" max="32767" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="spInputScanInterval" min="-2" pref="90" max="-2" attributes="0"/>
<Component id="spMaximumRetryAttempts" min="-2" pref="90" max="-2" attributes="0"/>
<Component id="spConcurrentJobsPerCase" alignment="1" min="-2" pref="90" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Component id="numberOfFileIngestThreadsComboBox" min="-2" pref="91" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<Group type="102" attributes="0">
<Component id="lbSecondsBetweenJobs" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="spSecondsBetweenJobs" min="-2" pref="90" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Component id="lbTimeoutText" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="spTimeoutHours" min="-2" pref="90" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lbRestartRequired" min="-2" max="-2" attributes="0"/>
<Component id="lbSecondsBetweenJobsSeconds" min="-2" max="-2" attributes="0"/>
<Component id="lbTimeoutHours" min="-2" max="-2" attributes="0"/>
<Component id="lbInputScanIntervalMinutes" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="50" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="lbRestartRequired" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbSecondsBetweenJobs" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="spSecondsBetweenJobs" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbSecondsBetweenJobsSeconds" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbTimeoutText" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="spTimeoutHours" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbTimeoutHours" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="cbTimeoutEnabled" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbInputScanInterval" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="spInputScanInterval" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbInputScanIntervalMinutes" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbRetriesAllowed" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="spMaximumRetryAttempts" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbConcurrentJobsPerCase" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="spConcurrentJobsPerCase" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbNumberOfThreads" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="numberOfFileIngestThreadsComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="lbSecondsBetweenJobs">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbSecondsBetweenJobs.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbSecondsBetweenJobs.toolTipText_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbTimeoutText">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbTimeoutText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbTimeoutText.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbInputScanInterval">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbInputScanInterval.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbInputScanInterval.toolTipText_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbRetriesAllowed">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.toolTipText_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbNumberOfThreads">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbNumberOfThreads.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbNumberOfThreads.toolTipText_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbConcurrentJobsPerCase">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.toolTipText_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="cbTimeoutEnabled">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.cbTimeoutEnabled.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.cbTimeoutEnabled.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cbTimeoutEnabledItemStateChanged"/>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbTimeoutEnabledActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="numberOfFileIngestThreadsComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.numberOfFileIngestThreadsComboBox.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="numberOfFileIngestThreadsComboBoxActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;Integer&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="lbRestartRequired">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/warning16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbRestartRequired.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JSpinner" name="spConcurrentJobsPerCase">
<Properties>
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
<SpinnerModel initial="3" maximum="100" minimum="1" numberType="java.lang.Integer" stepSize="1" type="number"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JSpinner" name="spMaximumRetryAttempts">
<Properties>
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
<SpinnerModel initial="2" maximum="9999999" minimum="0" numberType="java.lang.Integer" stepSize="1" type="number"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.toolTipText_2" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.spMaximumRetryAttempts.AccessibleContext.accessibleDescription" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
</Component>
<Component class="javax.swing.JSpinner" name="spInputScanInterval">
<Properties>
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
<SpinnerModel initial="60" maximum="100000" minimum="1" numberType="java.lang.Integer" stepSize="1" type="number"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.spInputScanInterval.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JSpinner" name="spTimeoutHours">
<Properties>
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
<SpinnerModel initial="60" maximum="100000" minimum="1" numberType="java.lang.Integer" stepSize="1" type="number"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.spTimeoutHours.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JSpinner" name="spSecondsBetweenJobs">
<Properties>
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
<SpinnerModel initial="30" maximum="3600" minimum="30" numberType="java.lang.Integer" stepSize="10" type="number"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.spSecondsBetweenJobs.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbSecondsBetweenJobsSeconds">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbSecondsBetweenJobsSeconds.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbTimeoutHours">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbTimeoutHours.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbTimeoutHours.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbInputScanIntervalMinutes">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbInputScanIntervalMinutes.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AdvancedAutoIngestSettingsPanel.lbInputScanIntervalMinutes.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,357 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.configuration;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComponent;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.core.UserPreferences;
class AdvancedAutoIngestSettingsPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
AdvancedAutoIngestSettingsPanel(AutoIngestSettingsPanel.OptionsUiMode mode) {
initComponents();
tbWarning.setLineWrap(true);
tbWarning.setWrapStyleWord(true);
load(mode);
}
private void initThreadCount() {
int availableProcessors = Runtime.getRuntime().availableProcessors();
Integer fileIngestThreadCountChoices[];
if (availableProcessors >= 16) {
fileIngestThreadCountChoices = new Integer[]{1, 2, 4, 6, 8, 12, 16};
} else if (availableProcessors >= 12 && availableProcessors <= 15) {
fileIngestThreadCountChoices = new Integer[]{1, 2, 4, 6, 8, 12};
} else if (availableProcessors >= 8 && availableProcessors <= 11) {
fileIngestThreadCountChoices = new Integer[]{1, 2, 4, 6, 8};
} else if (availableProcessors >= 6 && availableProcessors <= 7) {
fileIngestThreadCountChoices = new Integer[]{1, 2, 4, 6};
} else if (availableProcessors >= 4 && availableProcessors <= 5) {
fileIngestThreadCountChoices = new Integer[]{1, 2, 4};
} else if (availableProcessors >= 2 && availableProcessors <= 3) {
fileIngestThreadCountChoices = new Integer[]{1, 2};
} else {
fileIngestThreadCountChoices = new Integer[]{1};
}
numberOfFileIngestThreadsComboBox.setModel(new DefaultComboBoxModel<>(fileIngestThreadCountChoices));
numberOfFileIngestThreadsComboBox.setSelectedItem(UserPreferences.numberOfFileIngestThreads());
}
private void load(AutoIngestSettingsPanel.OptionsUiMode mode) {
initThreadCount();
spSecondsBetweenJobs.setValue(AutoIngestUserPreferences.getSecondsToSleepBetweenCases());
spMaximumRetryAttempts.setValue(AutoIngestUserPreferences.getMaxNumTimesToProcessImage());
int maxJobsPerCase = AutoIngestUserPreferences.getMaxConcurrentJobsForOneCase();
spConcurrentJobsPerCase.setValue(maxJobsPerCase);
spInputScanInterval.setValue(AutoIngestUserPreferences.getMinutesOfInputScanInterval());
spInputScanInterval.setEnabled(mode == AutoIngestSettingsPanel.OptionsUiMode.AIM);
spSecondsBetweenJobs.setEnabled(mode == AutoIngestSettingsPanel.OptionsUiMode.AIM);
spMaximumRetryAttempts.setEnabled(mode == AutoIngestSettingsPanel.OptionsUiMode.AIM);
cbTimeoutEnabled.setEnabled(mode == AutoIngestSettingsPanel.OptionsUiMode.AIM);
lbSecondsBetweenJobs.setEnabled(mode == AutoIngestSettingsPanel.OptionsUiMode.AIM);
lbInputScanInterval.setEnabled(mode == AutoIngestSettingsPanel.OptionsUiMode.AIM);
lbTimeoutText.setEnabled(mode == AutoIngestSettingsPanel.OptionsUiMode.AIM);
lbRetriesAllowed.setEnabled(mode == AutoIngestSettingsPanel.OptionsUiMode.AIM);
cbTimeoutEnabled.setSelected(UserPreferences.getIsTimeOutEnabled());
int timeOutHrs = UserPreferences.getProcessTimeOutHrs();
spTimeoutHours.setValue(timeOutHrs);
setCheckboxEnabledState();
}
void store() {
AutoIngestUserPreferences.setSecondsToSleepBetweenCases((int) spSecondsBetweenJobs.getValue());
AutoIngestUserPreferences.setMaxNumTimesToProcessImage((int) spMaximumRetryAttempts.getValue());
AutoIngestUserPreferences.setMaxConcurrentIngestNodesForOneCase((int) spConcurrentJobsPerCase.getValue());
AutoIngestUserPreferences.setMinutesOfInputScanInterval((int) spInputScanInterval.getValue());
UserPreferences.setNumberOfFileIngestThreads((Integer) numberOfFileIngestThreadsComboBox.getSelectedItem());
boolean isChecked = cbTimeoutEnabled.isSelected();
UserPreferences.setIsTimeOutEnabled(isChecked);
if (isChecked) {
// only store time out if it is enabled
int timeOutHrs = (int) spTimeoutHours.getValue();
UserPreferences.setProcessTimeOutHrs(timeOutHrs);
}
}
private void setCheckboxEnabledState() {
// Enable the timeout edit box iff the checkbox is checked and enabled
if (cbTimeoutEnabled.isEnabled() && cbTimeoutEnabled.isSelected()) {
spTimeoutHours.setEnabled(true);
lbTimeoutHours.setEnabled(true);
} else {
spTimeoutHours.setEnabled(false);
lbTimeoutHours.setEnabled(false);
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
spMainScrollPane = new javax.swing.JScrollPane();
tbWarning = new javax.swing.JTextArea();
jPanelAutoIngestJobSettings = new javax.swing.JPanel();
lbSecondsBetweenJobs = new javax.swing.JLabel();
lbTimeoutText = new javax.swing.JLabel();
lbInputScanInterval = new javax.swing.JLabel();
lbRetriesAllowed = new javax.swing.JLabel();
lbNumberOfThreads = new javax.swing.JLabel();
lbConcurrentJobsPerCase = new javax.swing.JLabel();
cbTimeoutEnabled = new javax.swing.JCheckBox();
numberOfFileIngestThreadsComboBox = new javax.swing.JComboBox<>();
lbRestartRequired = new javax.swing.JLabel();
spConcurrentJobsPerCase = new javax.swing.JSpinner();
spMaximumRetryAttempts = new javax.swing.JSpinner();
spInputScanInterval = new javax.swing.JSpinner();
spTimeoutHours = new javax.swing.JSpinner();
spSecondsBetweenJobs = new javax.swing.JSpinner();
lbSecondsBetweenJobsSeconds = new javax.swing.JLabel();
lbTimeoutHours = new javax.swing.JLabel();
lbInputScanIntervalMinutes = new javax.swing.JLabel();
tbWarning.setEditable(false);
tbWarning.setColumns(20);
tbWarning.setFont(new java.awt.Font("Monospaced", 1, 14)); // NOI18N
tbWarning.setRows(5);
tbWarning.setText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.tbWarning.text")); // NOI18N
tbWarning.setAutoscrolls(false);
spMainScrollPane.setViewportView(tbWarning);
jPanelAutoIngestJobSettings.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.jPanelAutoIngestJobSettings.border.title"))); // NOI18N
jPanelAutoIngestJobSettings.setName("Automated Ingest Job Settings"); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbSecondsBetweenJobs, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbSecondsBetweenJobs.text")); // NOI18N
lbSecondsBetweenJobs.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbSecondsBetweenJobs.toolTipText_1")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbTimeoutText, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbTimeoutText.text")); // NOI18N
lbTimeoutText.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbTimeoutText.toolTipText")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbInputScanInterval, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbInputScanInterval.text")); // NOI18N
lbInputScanInterval.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbInputScanInterval.toolTipText_1")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbRetriesAllowed, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.text")); // NOI18N
lbRetriesAllowed.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.toolTipText_1")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbNumberOfThreads, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbNumberOfThreads.text")); // NOI18N
lbNumberOfThreads.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbNumberOfThreads.toolTipText_1")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbConcurrentJobsPerCase, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.text")); // NOI18N
lbConcurrentJobsPerCase.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.toolTipText_1")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(cbTimeoutEnabled, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.cbTimeoutEnabled.text")); // NOI18N
cbTimeoutEnabled.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.cbTimeoutEnabled.toolTipText")); // NOI18N
cbTimeoutEnabled.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
cbTimeoutEnabledItemStateChanged(evt);
}
});
cbTimeoutEnabled.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cbTimeoutEnabledActionPerformed(evt);
}
});
numberOfFileIngestThreadsComboBox.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.numberOfFileIngestThreadsComboBox.toolTipText")); // NOI18N
numberOfFileIngestThreadsComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
numberOfFileIngestThreadsComboBoxActionPerformed(evt);
}
});
lbRestartRequired.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/warning16.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbRestartRequired, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbRestartRequired.text")); // NOI18N
spConcurrentJobsPerCase.setModel(new javax.swing.SpinnerNumberModel(3, 1, 100, 1));
spConcurrentJobsPerCase.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.toolTipText")); // NOI18N
spMaximumRetryAttempts.setModel(new javax.swing.SpinnerNumberModel(2, 0, 9999999, 1));
spMaximumRetryAttempts.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.toolTipText_2")); // NOI18N
spInputScanInterval.setModel(new javax.swing.SpinnerNumberModel(60, 1, 100000, 1));
spInputScanInterval.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.spInputScanInterval.toolTipText")); // NOI18N
spTimeoutHours.setModel(new javax.swing.SpinnerNumberModel(60, 1, 100000, 1));
spTimeoutHours.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.spTimeoutHours.toolTipText")); // NOI18N
spSecondsBetweenJobs.setModel(new javax.swing.SpinnerNumberModel(30, 30, 3600, 10));
spSecondsBetweenJobs.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.spSecondsBetweenJobs.toolTipText")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbSecondsBetweenJobsSeconds, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbSecondsBetweenJobsSeconds.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbTimeoutHours, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbTimeoutHours.text")); // NOI18N
lbTimeoutHours.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbTimeoutHours.toolTipText")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(lbInputScanIntervalMinutes, org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbInputScanIntervalMinutes.text")); // NOI18N
lbInputScanIntervalMinutes.setToolTipText(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.lbInputScanIntervalMinutes.toolTipText")); // NOI18N
javax.swing.GroupLayout jPanelAutoIngestJobSettingsLayout = new javax.swing.GroupLayout(jPanelAutoIngestJobSettings);
jPanelAutoIngestJobSettings.setLayout(jPanelAutoIngestJobSettingsLayout);
jPanelAutoIngestJobSettingsLayout.setHorizontalGroup(
jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup()
.addContainerGap()
.addComponent(cbTimeoutEnabled)
.addGap(5, 5, 5)
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup()
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup()
.addComponent(lbInputScanInterval)
.addGap(49, 49, 49))
.addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup()
.addComponent(lbRetriesAllowed)
.addGap(54, 54, 54))
.addComponent(lbConcurrentJobsPerCase, javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lbNumberOfThreads, javax.swing.GroupLayout.Alignment.LEADING))
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup()
.addGap(0, 41, Short.MAX_VALUE)
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(spInputScanInterval, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(spMaximumRetryAttempts, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(spConcurrentJobsPerCase, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(numberOfFileIngestThreadsComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup()
.addComponent(lbSecondsBetweenJobs)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(spSecondsBetweenJobs, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanelAutoIngestJobSettingsLayout.createSequentialGroup()
.addComponent(lbTimeoutText)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(spTimeoutHours, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lbRestartRequired)
.addComponent(lbSecondsBetweenJobsSeconds)
.addComponent(lbTimeoutHours)
.addComponent(lbInputScanIntervalMinutes))
.addContainerGap(50, Short.MAX_VALUE))
);
jPanelAutoIngestJobSettingsLayout.setVerticalGroup(
jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(lbRestartRequired)
.addGroup(jPanelAutoIngestJobSettingsLayout.createSequentialGroup()
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbSecondsBetweenJobs)
.addComponent(spSecondsBetweenJobs, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbSecondsBetweenJobsSeconds))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbTimeoutText)
.addComponent(spTimeoutHours, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbTimeoutHours))
.addComponent(cbTimeoutEnabled))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbInputScanInterval)
.addComponent(spInputScanInterval, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbInputScanIntervalMinutes))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbRetriesAllowed)
.addComponent(spMaximumRetryAttempts, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbConcurrentJobsPerCase)
.addComponent(spConcurrentJobsPerCase, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanelAutoIngestJobSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lbNumberOfThreads)
.addComponent(numberOfFileIngestThreadsComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
spMaximumRetryAttempts.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(AdvancedAutoIngestSettingsPanel.class, "AdvancedAutoIngestSettingsPanel.spMaximumRetryAttempts.AccessibleContext.accessibleDescription")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(jPanelAutoIngestJobSettings, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
.addGap(16, 16, 16)
.addComponent(spMainScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 640, Short.MAX_VALUE)))
.addGap(16, 16, 16))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(20, 20, 20)
.addComponent(spMainScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 106, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jPanelAutoIngestJobSettings, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(26, 26, 26))
);
}// </editor-fold>//GEN-END:initComponents
private void numberOfFileIngestThreadsComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_numberOfFileIngestThreadsComboBoxActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_numberOfFileIngestThreadsComboBoxActionPerformed
private void cbTimeoutEnabledActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbTimeoutEnabledActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_cbTimeoutEnabledActionPerformed
private void cbTimeoutEnabledItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cbTimeoutEnabledItemStateChanged
setCheckboxEnabledState();
}//GEN-LAST:event_cbTimeoutEnabledItemStateChanged
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox cbTimeoutEnabled;
private javax.swing.JPanel jPanelAutoIngestJobSettings;
private javax.swing.JLabel lbConcurrentJobsPerCase;
private javax.swing.JLabel lbInputScanInterval;
private javax.swing.JLabel lbInputScanIntervalMinutes;
private javax.swing.JLabel lbNumberOfThreads;
private javax.swing.JLabel lbRestartRequired;
private javax.swing.JLabel lbRetriesAllowed;
private javax.swing.JLabel lbSecondsBetweenJobs;
private javax.swing.JLabel lbSecondsBetweenJobsSeconds;
private javax.swing.JLabel lbTimeoutHours;
private javax.swing.JLabel lbTimeoutText;
private javax.swing.JComboBox<Integer> numberOfFileIngestThreadsComboBox;
private javax.swing.JSpinner spConcurrentJobsPerCase;
private javax.swing.JSpinner spInputScanInterval;
private javax.swing.JScrollPane spMainScrollPane;
private javax.swing.JSpinner spMaximumRetryAttempts;
private javax.swing.JSpinner spSecondsBetweenJobs;
private javax.swing.JSpinner spTimeoutHours;
private javax.swing.JTextArea tbWarning;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,659 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="modeRadioButtons">
</Component>
</NonVisualComponents>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="nodePanel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="nodePanel" min="-2" max="-2" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="nodePanel">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="cbJoinAutoIngestCluster" min="-2" pref="171" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="tbOops" min="-2" pref="465" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="jPanelNodeType" max="32767" attributes="0"/>
<Component id="jPanelSharedConfig" alignment="0" max="32767" attributes="0"/>
<Component id="jPanelIngestSettings" alignment="0" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cbJoinAutoIngestCluster" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="tbOops" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
<Component id="jPanelNodeType" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jPanelIngestSettings" min="-2" pref="62" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jPanelSharedConfig" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="jPanelNodeType">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Node Type Setup">
<Border PropertyName="innerBorder" info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jPanelNodeType.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[50, 50]"/>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="1" attributes="0">
<Component id="outputPathTextField" alignment="0" max="32767" attributes="0"/>
<Component id="inputPathTextField" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="browseInputFolderButton" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="browseOutputFolderButton" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="jLabelSelectMode" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="restartRequiredNodeLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="jRadioButtonReview" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="jRadioButtonAutomated" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="jLabelSelectInputFolder" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="jLabelInvalidImageFolder" max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="jLabelSelectOutputFolder" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="jLabelInvalidResultsFolder" min="-2" pref="544" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabelSelectMode" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="restartRequiredNodeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="jRadioButtonAutomated" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jRadioButtonReview" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabelSelectInputFolder" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jLabelInvalidImageFolder" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="inputPathTextField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="browseInputFolderButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabelSelectOutputFolder" alignment="3" min="-2" pref="21" max="-2" attributes="0"/>
<Component id="jLabelInvalidResultsFolder" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="browseOutputFolderButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="outputPathTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="jLabelSelectMode">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jLabelSelectMode.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="restartRequiredNodeLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/experimental/images/warning16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.restartRequiredNodeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="jRadioButtonAutomated">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="modeRadioButtons"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jRadioButtonAutomated.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jRadioButtonAutomated.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jRadioButtonAutomatedActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="jRadioButtonReview">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="modeRadioButtons"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jRadioButtonReview.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jRadioButtonReview.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jRadioButtonReviewActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jLabelSelectInputFolder">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jLabelSelectInputFolder.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="verticalAlignment" type="int" value="3"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="inputPathTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.inputPathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.inputPathTextField.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="browseInputFolderButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.browseInputFolderButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseInputFolderButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jLabelSelectOutputFolder">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jLabelSelectOutputFolder.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="verticalAlignment" type="int" value="3"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="outputPathTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.outputPathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.outputPathTextField.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="browseOutputFolderButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.browseOutputFolderButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseOutputFolderButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jLabelInvalidImageFolder">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jLabelInvalidImageFolder.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jLabelInvalidResultsFolder">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jLabelInvalidResultsFolder.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/experimental/images/AIM.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="jPanelSharedConfig">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Shared Configuration">
<Border PropertyName="innerBorder" info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jPanelSharedConfig.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Component id="jLabelCurrentTask" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="jLabelTaskDescription" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="sharedSettingsTextField" min="-2" pref="400" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="browseSharedSettingsButton" min="-2" pref="143" max="-2" attributes="0"/>
</Group>
<Component id="uploadButton" alignment="0" min="-2" pref="143" max="-2" attributes="0"/>
<Group type="103" alignment="0" groupAlignment="1" max="-2" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="downloadButton" min="-2" pref="143" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="configButtonErrorTextField" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="sharedConfigCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="sharedSettingsErrorTextField" max="32767" attributes="0"/>
</Group>
<Component id="jSeparator1" min="-2" pref="692" max="-2" attributes="0"/>
</Group>
<Component id="pbTaskInProgress" alignment="0" min="-2" pref="695" max="-2" attributes="0"/>
<Component id="masterNodeCheckBox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="sharedConfigCheckbox" alignment="3" min="-2" pref="21" max="-2" attributes="0"/>
<Component id="sharedSettingsErrorTextField" alignment="3" min="-2" pref="21" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="sharedSettingsTextField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="browseSharedSettingsButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="downloadButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="configButtonErrorTextField" alignment="3" min="-2" pref="21" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Component id="jSeparator1" min="-2" pref="10" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
<Component id="masterNodeCheckBox" min="-2" pref="23" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="uploadButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabelCurrentTask" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jLabelTaskDescription" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="pbTaskInProgress" min="-2" pref="22" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="sharedConfigCheckbox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.sharedConfigCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[191, 14]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[191, 14]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[191, 14]"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="sharedConfigCheckboxItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="sharedSettingsTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.sharedSettingsTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="browseSharedSettingsButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.browseSharedSettingsButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseSharedSettingsButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="sharedSettingsErrorTextField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.sharedSettingsErrorTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="masterNodeCheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.masterNodeCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="masterNodeCheckBoxItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="uploadButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.uploadButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="uploadButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="downloadButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.downloadButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="downloadButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jLabelCurrentTask">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jLabelCurrentTask.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JProgressBar" name="pbTaskInProgress">
</Component>
<Component class="javax.swing.JLabel" name="jLabelTaskDescription">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jLabelTaskDescription.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="configButtonErrorTextField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.configButtonErrorTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JSeparator" name="jSeparator1">
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="jPanelIngestSettings">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Automated Ingest Settings">
<Border PropertyName="innerBorder" info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.jPanelIngestSettings.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="bnEditIngestSettings" min="-2" pref="155" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnAdvancedSettings" min="-2" pref="155" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnFileExport" min="-2" pref="155" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="bnLogging" min="-2" pref="155" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="bnEditIngestSettings" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnFileExport" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnAdvancedSettings" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnLogging" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="bnEditIngestSettings">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.bnEditIngestSettings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.bnEditIngestSettings.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnEditIngestSettingsActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnAdvancedSettings">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.bnAdvancedSettings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnAdvancedSettingsActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnFileExport">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.bnFileExport.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnFileExportActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnLogging">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.bnLogging.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnLoggingActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JCheckBox" name="cbJoinAutoIngestCluster">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="false" component="cbJoinAutoIngestCluster" property="font" relativeSize="false" size="11"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.cbJoinAutoIngestCluster.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbJoinAutoIngestClusterActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="tbOops">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="tbOops" property="font" relativeSize="false" size="12"/>
</FontInfo>
</Property>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="AutoIngestSettingsPanel.tbOops.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="tbOopsActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,131 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.configuration;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.JComponent;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
@OptionsPanelController.TopLevelRegistration(categoryName = "#OptionsCategory_Name_Auto_Ingest",
iconBase = "org/sleuthkit/autopsy/experimental/images/autoIngest32.png",
position = 3,
keywords = "#OptionsCategory_Keywords_Auto_Ingest_Settings",
keywordsCategory = "Auto Ingest")
public final class AutoIngestSettingsPanelController extends OptionsPanelController {
private AutoIngestSettingsPanel panel;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private boolean changed;
private static final Logger logger = Logger.getLogger(AutoIngestSettingsPanelController.class.getName());
@Override
public void update() {
getPanel().load(false);
changed = false;
}
@Override
public void applyChanges() {
getPanel().store();
changed = false;
}
@Override
public void cancel() {
}
@Override
public boolean isValid() {
return getPanel().valid();
}
@Override
public boolean isChanged() {
return changed;
}
@Override
public HelpCtx getHelpCtx() {
return null;
}
@Override
public JComponent getComponent(Lookup masterLookup) {
return getPanel();
}
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
if (pcs.getPropertyChangeListeners().length == 0) {
pcs.addPropertyChangeListener(l);
}
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
/**
* Note the NetBeans Framework does not appear to call this at all. We
* are using NetBeans 7.3.1 Build 201306052037. Perhaps in a future
* version of the Framework this will be resolved, but for now, simply
* don't unregister anything and add one time only in the
* addPropertyChangeListener() method above.
*/
}
private AutoIngestSettingsPanel getPanel() {
if (panel == null) {
panel = new AutoIngestSettingsPanel(this);
}
return panel;
}
void changed() {
if (!changed) {
changed = true;
try {
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
} catch (Exception e) {
logger.log(Level.SEVERE, "GeneralOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
}
try {
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
} catch (Exception e) {
logger.log(Level.SEVERE, "GeneralOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
}
}

View File

@ -0,0 +1,536 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.configuration;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.sleuthkit.autopsy.core.UserPreferencesException;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
/**
* Provides convenient access to a Preferences node for auto ingest user preferences
* with default values.
*/
public final class AutoIngestUserPreferences {
public enum SelectedMode {
STANDALONE,
AUTOMATED,
REVIEW
};
private static final String SETTINGS_PROPERTIES = "AutoIngest";
private static final String MODE = "AutopsyMode"; // NON-NLS
private static final String JOIN_AUTO_MODE_CLUSTER = "JoinAutoModeCluster"; // NON-NLS
private static final String AUTO_MODE_IMAGES_FOLDER = "AutoModeImageFolder"; // NON-NLS
private static final String AUTO_MODE_RESULTS_FOLDER = "AutoModeResultsFolder"; // NON-NLS
private static final String SHARED_CONFIG_FOLDER = "SharedSettingsFolder"; // NON-NLS
private static final String SHARED_CONFIG_ENABLED = "SharedSettingsEnabled"; // NON-NLS
private static final String SHARED_CONFIG_MASTER = "SharedSettingsMaster"; // NON-NLS
private static final String AUTO_MODE_CONTEXT_STRING = "AutoModeContext"; // NON-NLS
private static final String SLEEP_BETWEEN_CASES_TIME = "SleepBetweenCasesTime"; // NON-NLS
private static final String SHOW_TOOLS_WARNING = "ShowToolsWarning"; // NON-NLS
private static final String MAX_NUM_TIMES_TO_PROCESS_IMAGE = "MaxNumTimesToAttemptToProcessImage"; // NON-NLS
private static final String MAX_CONCURRENT_NODES_FOR_ONE_CASE = "MaxConcurrentNodesForOneCase"; // NON-NLS
private static final String STATUS_DATABASE_LOGGING_ENABLED = "StatusDatabaseLoggingEnabled"; // NON-NLS
private static final String LOGGING_DB_HOSTNAME_OR_IP = "LoggingHostnameOrIP"; // NON-NLS
private static final String LOGGING_PORT = "LoggingPort"; // NON-NLS
private static final String LOGGING_USERNAME = "LoggingUsername"; // NON-NLS
private static final String LOGGING_PASSWORD = "LoggingPassword"; // NON-NLS
private static final String LOGGING_DATABASE_NAME = "LoggingDatabaseName"; // NON-NLS
private static final String INPUT_SCAN_INTERVAL_TIME = "IntervalBetweenInputScan"; // NON-NLS
// Prevent instantiation.
private AutoIngestUserPreferences() {
}
/**
* Get mode from persistent storage.
*
* @return SelectedMode Selected mode.
*/
public static SelectedMode getMode() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, MODE)) {
int ordinal = Integer.parseInt(ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, MODE));
return SelectedMode.values()[ordinal];
}
return SelectedMode.STANDALONE;
}
/**
* Set mode to persistent storage.
*
* @param mode Selected mode.
*/
public static void setMode(SelectedMode mode) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, MODE, Integer.toString(mode.ordinal()));
}
/**
* Get "Join Automated Ingest Cluster" setting from persistent storage.
*
* @return SelectedMode Selected setting.
*/
public static boolean getJoinAutoModeCluster() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, JOIN_AUTO_MODE_CLUSTER)) {
return Boolean.parseBoolean(ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, JOIN_AUTO_MODE_CLUSTER));
}
return false;
}
/**
* Set "Join Automated Ingest Cluster" setting to persistent storage.
*
* @param join boolean value of whether to join auto ingest cluster or not
*/
public static void setJoinAutoModeCluster(boolean join) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, JOIN_AUTO_MODE_CLUSTER, Boolean.toString(join));
}
/**
* Get input folder for automated mode from persistent storage.
*
* @return String Selected input folder.
*/
public static String getAutoModeImageFolder() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, AUTO_MODE_IMAGES_FOLDER)) {
return ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, AUTO_MODE_IMAGES_FOLDER);
}
return "";
}
/**
* Set input image folder for automated mode from persistent storage.
*
* @param folder Selected input folder.
*/
public static void setAutoModeImageFolder(String folder) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, AUTO_MODE_IMAGES_FOLDER, folder);
}
/**
* Get results folder for automated mode from persistent storage.
*
* @return String Selected output folder.
*/
public static String getAutoModeResultsFolder() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, AUTO_MODE_RESULTS_FOLDER)) {
return ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, AUTO_MODE_RESULTS_FOLDER);
}
return "";
}
/**
* Set results folder for automated mode from persistent storage.
*
* @param folder Selected output folder.
*/
public static void setAutoModeResultsFolder(String folder) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, AUTO_MODE_RESULTS_FOLDER, folder);
}
/**
* Get shared config folder for automated mode from persistent
* storage.
*
* @return String Selected settings folder.
*/
public static String getSharedConfigFolder() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, SHARED_CONFIG_FOLDER)) {
return ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, SHARED_CONFIG_FOLDER);
}
return "";
}
/**
* Set shared config folder for automated mode from persistent
* storage.
*/
public static void setSharedConfigFolder(String folder) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, SHARED_CONFIG_FOLDER, folder);
}
/**
* Get shared config checkbox state for automated mode from
* persistent storage.
*
* @return Boolean true if shared settings are enabled.
*/
public static Boolean getSharedConfigEnabled() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, SHARED_CONFIG_ENABLED)) {
return Boolean.parseBoolean(ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, SHARED_CONFIG_ENABLED));
}
return false;
}
/**
* Save shared config checkbox state for automated mode to persistent
* storage.
*
* @param sharedSettingsEnabled true = use shared settings in auto-ingest
* mode
*/
public static void setSharedConfigEnabled(boolean sharedSettingsEnabled) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, SHARED_CONFIG_ENABLED, Boolean.toString(sharedSettingsEnabled));
}
/**
* Get shared config master checkbox state for automated mode from
* persistent storage.
*
* @return true if this node is set as a shared configuration master
*/
public static Boolean getSharedConfigMaster() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, SHARED_CONFIG_MASTER)) {
return Boolean.parseBoolean(ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, SHARED_CONFIG_MASTER));
}
return false;
}
/**
* Save shared config master checkbox state to persistent storage.
*
* @param sharedSettingsMaster true = this node can upload configuration
*/
public static void setSharedConfigMaster(boolean sharedSettingsMaster) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, SHARED_CONFIG_MASTER, Boolean.toString(sharedSettingsMaster));
}
/**
* Get context string for automated mode ingest module settings.
*
* @return String Context string for automated mode ingest module
* settings.
*/
public static String getAutoModeIngestModuleContextString() {
return AUTO_MODE_CONTEXT_STRING;
}
/**
* Save whether tools warning dialog should be shown on startup.
*
* @param showToolsWarning true = show warning dialog, false = don't show
*/
public static void setShowToolsWarning(boolean showToolsWarning) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, SHOW_TOOLS_WARNING, Boolean.toString(showToolsWarning));
}
/**
* Retrieve tools warning dialog setting.
*
* @return
*/
public static boolean getShowToolsWarning() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, SHOW_TOOLS_WARNING)) {
return Boolean.parseBoolean(ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, SHOW_TOOLS_WARNING));
}
return true;
}
/**
* Get the configured time to sleep between cases to prevent
* database locks
*
* @return int the value in seconds, default is 30 seconds.
*/
public static int getSecondsToSleepBetweenCases() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, SLEEP_BETWEEN_CASES_TIME)) {
return Integer.parseInt(ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, SLEEP_BETWEEN_CASES_TIME));
}
return 30;
}
/**
* Sets the wait time used by auto ingest nodes to ensure proper
* synchronization of node operations in circumstances where delays may
* occur, e.g., network file system latency effects on the visibility of
* newly created shared directories and files.
*
* @param value value the number of seconds to sleep between cases
*/
public static void setSecondsToSleepBetweenCases(int value) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, SLEEP_BETWEEN_CASES_TIME, Integer.toString(value));
}
/**
* Get maximum number of times to attempt processing an image folder. This
* is used to avoid endless attempts to process an image folder with corrupt
* data that causes a crash.
*
* @return int maximum number of attempts, default is 2.
*/
public static int getMaxNumTimesToProcessImage() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, MAX_NUM_TIMES_TO_PROCESS_IMAGE)) {
return Integer.parseInt(ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, MAX_NUM_TIMES_TO_PROCESS_IMAGE));
}
return 2;
}
/**
* Set the maximum number of times to attempt to reprocess an image. This is
* used to avoid endless attempts to process an image folder with corrupt
* data that causes a crash.
*
* @param retries the number of retries to allow
*/
public static void setMaxNumTimesToProcessImage(int retries) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, MAX_NUM_TIMES_TO_PROCESS_IMAGE, Integer.toString(retries));
}
/**
* Get maximum number of concurrent ingest nodes allowable for one case at a
* time.
*
* @return maximum number of concurrent nodes for one case. Default is 3.
*/
public static int getMaxConcurrentJobsForOneCase() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, MAX_CONCURRENT_NODES_FOR_ONE_CASE)) {
return Integer.parseInt(ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, MAX_CONCURRENT_NODES_FOR_ONE_CASE));
}
return 3;
}
/**
* Get maximum number of concurrent ingest nodes allowable for one case at a
* time.
*
* @param numberOfNodes the number of concurrent nodes to allow for one case
*/
public static void setMaxConcurrentIngestNodesForOneCase(int numberOfNodes) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, MAX_CONCURRENT_NODES_FOR_ONE_CASE, Integer.toString(numberOfNodes));
}
/**
* Get status database logging checkbox state for automated ingest mode from
* persistent storage.
*
* @return Boolean true if database logging is enabled.
*/
public static Boolean getStatusDatabaseLoggingEnabled() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, STATUS_DATABASE_LOGGING_ENABLED)) {
return Boolean.parseBoolean(ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, STATUS_DATABASE_LOGGING_ENABLED));
}
return false;
}
/**
* Save status database logging checkbox state for automated ingest mode to
* persistent storage.
*
* @param databaseLoggingEnabled true = use database logging in auto-ingest
* mode
*/
public static void setStatusDatabaseLoggingEnabled(boolean databaseLoggingEnabled) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, STATUS_DATABASE_LOGGING_ENABLED, Boolean.toString(databaseLoggingEnabled));
}
/**
* Get the logging database hostname from persistent storage.
*
* @return Logging database hostname or IP
*/
public static String getLoggingDatabaseHostnameOrIP() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, LOGGING_DB_HOSTNAME_OR_IP)) {
return ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, LOGGING_DB_HOSTNAME_OR_IP);
}
return "";
}
/**
* Save the logging database hostname to persistent storage.
*
* @param hostname Logging database hostname or IP
*/
public static void setLoggingDatabaseHostnameOrIP(String hostname) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, LOGGING_DB_HOSTNAME_OR_IP, hostname);
}
/**
* Get the logging database port from persistent storage.
*
* @return logging database port
*/
public static String getLoggingPort() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, LOGGING_PORT)) {
return ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, LOGGING_PORT);
}
return "";
}
/**
* Save the logging database port to persistent storage.
*
* @param port Logging database port
*/
public static void setLoggingPort(String port) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, LOGGING_PORT, port);
}
/**
* Get the logging database username from persistent storage.
*
* @return logging database username
*/
public static String getLoggingUsername() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, LOGGING_USERNAME)) {
return ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, LOGGING_USERNAME);
}
return "";
}
/**
* Save the logging database username to persistent storage.
*
* @param username Logging database username
*/
public static void setLoggingUsername(String username) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, LOGGING_USERNAME, username);
}
/**
* Get the logging database password from persistent storage.
*
* @return logging database password
*/
public static String getLoggingPassword() throws UserPreferencesException {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, LOGGING_PASSWORD)) {
return TextConverter.convertHexTextToText(ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, LOGGING_PASSWORD));
}
return "";
}
/**
* Save the logging database password to persistent storage.
*
* @param password Logging database password
*/
public static void setLoggingPassword(String password) throws UserPreferencesException {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, LOGGING_PASSWORD, TextConverter.convertTextToHexText(password));
}
/**
* Get the logging database name from persistent storage.
*
* @return logging database name
*/
public static String getLoggingDatabaseName() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, LOGGING_DATABASE_NAME)) {
return ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, LOGGING_DATABASE_NAME);
}
return "";
}
/**
* Save the logging database name to persistent storage.
*
* @param name Logging database name
*/
public static void setLoggingDatabaseName(String name) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, LOGGING_DATABASE_NAME, name);
}
/**
* Get the configured time for input scan interval
*
* @return int the value in minutes, default is 60 minutes.
*/
public static int getMinutesOfInputScanInterval() {
if (ModuleSettings.settingExists(SETTINGS_PROPERTIES, INPUT_SCAN_INTERVAL_TIME)) {
return Integer.parseInt(ModuleSettings.getConfigSetting(SETTINGS_PROPERTIES, INPUT_SCAN_INTERVAL_TIME));
}
return 60;
}
/**
* Set the configured time for input scan interval
*
* @param value the number of minutes for input interval
*/
public static void setMinutesOfInputScanInterval(int value) {
ModuleSettings.setConfigSetting(SETTINGS_PROPERTIES, INPUT_SCAN_INTERVAL_TIME, Integer.toString(value));
}
/**
* Copied from Autopsy UserPreferences - can be removed once everything is merged together.
* Provides ability to convert text to hex text.
*/
static final class TextConverter {
private static final char[] TMP = "hgleri21auty84fwe".toCharArray(); //NON-NLS
private static final byte[] SALT = {
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,};
/**
* Convert text to hex text.
*
* @param property Input text string.
*
* @return Converted hex string.
*
* @throws org.sleuthkit.autopsy.core.UserPreferencesException
*/
static String convertTextToHexText(String property) throws UserPreferencesException {
try {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES"); //NON-NLS
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(TMP));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES"); //NON-NLS
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
return base64Encode(pbeCipher.doFinal(property.getBytes("UTF-8")));
} catch (Exception ex) {
throw new UserPreferencesException("Error encrypting text");
}
}
private static String base64Encode(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
/**
* Convert hex text back to text.
*
* @param property Input hex text string.
*
* @return Converted text string.
*
* @throws org.sleuthkit.autopsy.core.UserPreferencesException
*/
static String convertHexTextToText(String property) throws UserPreferencesException {
try {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES"); //NON-NLS
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(TMP));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES"); //NON-NLS
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
} catch (Exception ex) {
throw new UserPreferencesException("Error decrypting text");
}
}
private static byte[] base64Decode(String property) {
return Base64.getDecoder().decode(property);
}
}
}

View File

@ -0,0 +1,149 @@
AdvancedAutoIngestSettingsPanel.AccessibleContext.accessibleName=
AdvancedAutoIngestSettingsPanel.ItemDisabled.text=Item disabled based upon current Autopsy mode.
AdvancedAutoIngestSettingsPanel.jPanelFolderMaintenanceSettings.border.title=Folder Maintenance Settings
AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.toolTipText=A soft limit on the number of concurrent jobs per case when multiple cases are processed simultaneously.
AdvancedAutoIngestSettingsPanel.lbInputScanInterval.AccessibleContext.accessibleDescription=The interval
AdvancedAutoIngestSettingsPanel.lbInputScanInterval.toolTipText=The interval between scans for new manifest files.
AdvancedAutoIngestSettingsPanel.lbInputScanIntervalMinutes1.AccessibleContext.accessibleName=minutes
AdvancedAutoIngestSettingsPanel.lbInputScanIntervalMinutes1.text=minutes
AdvancedAutoIngestSettingsPanel.lbNumberOfThreads.toolTipText=The number of threads running file level ingest modules.
AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.toolTipText=The maximum number of retries for crashed jobs.
AdvancedAutoIngestSettingsPanel.lbSecondsBetweenJobs.toolTipText=A wait time used by auto ingest nodes to ensure proper synchronization of node operations.
AdvancedAutoIngestSettingsPanel.tbWarning.text=WARNING: Ensure you know what you are doing before modifying these values. Informed use can improve system performance. Misuse can cause system performance degradation and data loss. Please consult the user guide for details.
AdvancedAutoIngestSettingsPanel.threadCountLabel.text=For this computer, a maximum of {0} file ingest threads should be used.
AIMIngestSettingsPanel.browseGlobalSettingsButton.text=Browse
AIMIngestSettingsPanel.globalSettingsCheckbox.text=Use shared configuration in folder:
AIMIngestSettingsPanel.globalSettingsErrorTextField.text=
AIMIngestSettingsPanel.globalSettingsTextField.text=
AIMIngestSettingsPanel.jButton1.text=Download shared settings
AIMIngestSettingsPanel.jButtonEditIngestSettings.text=Ingest Module Settings
AIMIngestSettingsPanel.jButtonEditIngestSettings.toolTipText=Ingest job settings for the automated processing mode context.
AIMIngestSettingsPanel.jLabel1.text=Download the current shared setting (highly recommended to do this before editing)
AIMIngestSettingsPanel.lbSecondsBetweenJobs.text=Number of seconds to wait between jobs:
AIMIngestSettingsPanel.lbSecondsBetweenJobs.toolTipText=Increase this value if database locks cause problems. It gives a little more time for finalizing.
AIMIngestSettingsPanel.spSecondsBetweenJobs.toolTipText=Increase this value if database locks cause problems. It gives a little more time for finalizing.
AutoIngestSettingsPanel.AdvancedAutoIngestSettingsPanel.Title=Advanced Settings
AutoIngestSettingsPanel.browseGlobalSettingsButton.text=Browse
AutoIngestSettingsPanel.browseSharedSettingsButton.text=Browse
AutoIngestSettingsPanel.CannotAccess=Cannot access
AutoIngestSettingsPanel.cbJoinAutoIngestCluster.text=Join Automated Ingest Cluster
AutoIngestSettingsPanel.CheckPermissions=Check permissions.
AutoIngestSettingsPanel.configButtonErrorTextField.text=configButtonErrorTextField
AutoIngestSettingsPanel.downloadButton.text=Download Config
AutoIngestSettingsPanel.EmptySettingsDirectory=Enter path to settings directory
AutoIngestSettingsPanel.ErrorSettingDefaultFolder=Error creating default folder
AutoIngestSettingsPanel.FileExportRules.text=File Export Rules
AutoIngestSettingsPanel.globalSettingsCheckbox.text=Use shared configuration in folder:
AutoIngestSettingsPanel.globalSettingsErrorTextField.text=
AutoIngestSettingsPanel.globalSettingsTextField.text=
AutoIngestSettingsPanel.ImageDirectoryUnspecified=Shared images folder must be set
AutoIngestSettingsPanel.InvalidPortNumber=Invalid port number.
AutoIngestSettingsPanel.jLabelCurrentTask.text=Current task:
AutoIngestSettingsPanel.jLabelTaskDescription.text=jLabel1
AutoIngestSettingsPanel.jPanelSharedConfig.border.title=Shared Configuration
AutoIngestSettingsPanel.jRadioButtonCopyFiles.text=File Copy mode
AutoIngestSettingsPanel.KeywordSearchNull=Cannot find Keyword Search service
AutoIngestSettingsPanel.masterNodeCheckBox.text=Use this node as a master node that can upload settings
AutoIngestSettingsPanel.MustRestart=Autopsy must be restarted for new configuration to take effect
AutoIngestSettingsPanel.nodePanel.TabConstraints.tabTitle=Node Configuration
AutoIngestSettingsPanel.NodeStatusLogging.text=Node Status Logging Settings
AutoIngestSettingsPanel.PathInvalid=Path is not valid
AutoIngestSettingsPanel.restartRequiredLabel.text=Application restart required to take effect.
AutoIngestSettingsPanel.restartRequiredLabel.text=Application restart required
AutoIngestSettingsPanel.ResultsDirectoryUnspecified=Shared results folder must be set
AutoIngestSettingsPanel.sharedConfigCheckbox.text=Use shared configuration in folder:
AutoIngestSettingsPanel.sharedSettingsErrorTextField.text=globalSettingsErrorTextField
AutoIngestSettingsPanel.sharedSettingsTextField.text=
AutoIngestSettingsPanel.tbOops.text=
AutoIngestSettingsPanel.uploadButton.text=Save & Upload Config
AutoIngestSettingsPanel.validationErrMsg.incomplete=Fill in all values
AutoIngestSettingsPanel.validationErrMsg.invalidDatabasePort=Invalid database port number
AutoIngestSettingsPanel.validationErrMsg.invalidIndexingServerPort=Invalid Solr server port number
AutoIngestSettingsPanel.validationErrMsg.invalidMessageServicePort=Invalid message service port number
AutoIngestSettingsPanel.validationErrMsg.MUdisabled=Multi user settings must be enabled and saved
GeneralOptionsPanelController.moduleErr.msg=A module caused an error listening to GeneralOptionsPanelController updates. See log to determine which module. Some data could be incomplete.
GeneralOptionsPanelController.moduleErr=Module Error
NodeStatusLogPanel.bnCancel.text=Cancel
NodeStatusLogPanel.bnOk.text=OK
NodeStatusLogPanel.bnTestDatabase.text=Test
NodeStatusLogPanel.cbEnableLogging.text=Enable Database Logging
NodeStatusLogPanel.lbDatabaseSettings.text=Database Settings
NodeStatusLogPanel.lbTestDatabase.text=
NodeStatusLogPanel.lbTestDbWarning.text=
NodeStatusLogPanel.lbTestDbWarning.text=
NodeStatusLogPanel.tbDbHostname.text=
NodeStatusLogPanel.tbDbHostname.toolTipText=Hostname or IP Address
NodeStatusLogPanel.tbDbName.text=
NodeStatusLogPanel.tbDbName.toolTipText=Database Name
NodeStatusLogPanel.tbDbPassword.text=
NodeStatusLogPanel.tbDbPassword.toolTipText=Password
NodeStatusLogPanel.tbDbPort.text=
NodeStatusLogPanel.tbDbPort.toolTipText=Port Number
NodeStatusLogPanel.tbDbUsername.text=
NodeStatusLogPanel.tbDbUsername.toolTipText=User Name
OpenOptionsPanelAction.name=Auto Ingest Options
OptionsCategory_Keywords_Auto_Ingest_Settings=Auto Ingest Settings
OptionsCategory_Keywords_General=Options
OptionsCategory_Name_Auto_Ingest=Auto Ingest
OptionsCategory_Name_General=Autopsy
OptionsDialog.jButton1.text=jButton1
OptionsDialog.jCheckBox1.text=jCheckBox1
OptionsDialog.jLabel1.text=jLabel1
StartupWindow.AutoIngestMode=Automated Ingest Node
StartupWindow.CaseImportMode=Single User Case Import
StartupWindow.CopyAndImportMode=Utilities
StartupWindow.ReviewMode=Cases
StartupWindow.title.text=Welcome
AdvancedAutoIngestSettingsPanel.lbInputScanIntervalMinutes.text=minutes
AdvancedAutoIngestSettingsPanel.lbTimeoutHours.text=hour(s)
AdvancedAutoIngestSettingsPanel.lbSecondsBetweenJobsSeconds.text=seconds
AdvancedAutoIngestSettingsPanel.spSecondsBetweenJobs.toolTipText=Increase this value if database locks cause problems. It gives a little more time for finalizing.
AdvancedAutoIngestSettingsPanel.spTimeoutHours.toolTipText=Components that spawn potentially long-running processes optionally terminate those processes if the specified time out period has elapsed.
AdvancedAutoIngestSettingsPanel.spInputScanInterval.toolTipText=Increase this value to reduce the frequency of input scan.
AdvancedAutoIngestSettingsPanel.spMaximumRetryAttempts.AccessibleContext.accessibleDescription=Maximum number of retries allowed in Automated Ingest Mode
AdvancedAutoIngestSettingsPanel.spMaximumRetryAttempts.toolTipText=The maximum number of times Automated ingest will attempt to reprocess a job if processing fails.
AdvancedAutoIngestSettingsPanel.lbRestartRequired.text=Application restart required to take effect.
AdvancedAutoIngestSettingsPanel.cbTimeoutEnabled.toolTipText=Components that spawn potentially long-running processes optionally terminate those processes if the specified time out period has elapsed.
AdvancedAutoIngestSettingsPanel.cbTimeoutEnabled.text=
AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.text=Target concurrent jobs per case:
AdvancedAutoIngestSettingsPanel.lbNumberOfThreads.text=Number of threads to use for file ingest:
AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.text=Maximum job retries allowed:
AdvancedAutoIngestSettingsPanel.lbInputScanInterval.text=Interval between input scans:
AdvancedAutoIngestSettingsPanel.lbTimeoutText.text=External processes time out:
AdvancedAutoIngestSettingsPanel.lbSecondsBetweenJobs.text=System synchronization wait time:
AdvancedAutoIngestSettingsPanel.jPanelAutoIngestJobSettings.border.title=Automated Ingest Job Settings
AdvancedAutoIngestSettingsPanel.lbSecondsBetweenJobs.toolTipText_1=Increase this value if database locks cause problems. It gives a little more time for finalizing.
AdvancedAutoIngestSettingsPanel.lbTimeoutText.toolTipText=Components that spawn potentially long-running processes optionally terminate those processes if the specified time out period has elapsed.
AdvancedAutoIngestSettingsPanel.lbInputScanInterval.toolTipText_1=Increase this value to reduce the frequency of input scan.
AdvancedAutoIngestSettingsPanel.lbInputScanIntervalMinutes.toolTipText=
AdvancedAutoIngestSettingsPanel.lbTimeoutHours.toolTipText=
AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.toolTipText_1=The maximum number of retries for crashed jobs.
AdvancedAutoIngestSettingsPanel.lbRetriesAllowed.toolTipText_2=The maximum number of retries for crashed jobs.
AdvancedAutoIngestSettingsPanel.lbConcurrentJobsPerCase.toolTipText_1=A soft limit on the number of concurrent jobs per case when multiple cases are processed simultaneously.
AdvancedAutoIngestSettingsPanel.lbNumberOfThreads.toolTipText_1=The number of threads running file level ingest modules.
AdvancedAutoIngestSettingsPanel.numberOfFileIngestThreadsComboBox.toolTipText=The number of threads running file level ingest modules.
NodeStatusLogPanel.tbDbName.toolTipText_1=Database name
AutoIngestSettingsPanel.jPanelNodeType.border.title=Node Type Setup
AutoIngestSettingsPanel.jLabel1.text=
AutoIngestSettingsPanel.jLabelInvalidResultsFolder.text=jLabelInvalidResultsFolder
AutoIngestSettingsPanel.jLabelInvalidImageFolder.text=jLabelInvalidImageFolder
AutoIngestSettingsPanel.browseOutputFolderButton.text=Browse
AutoIngestSettingsPanel.outputPathTextField.toolTipText=Results folder for automated processing, i.e., the location where results folders will be created by automated processing mode for presentation to the user in review mode. Also, cases that were single-user and were converted to multi-user cases will end up here.
AutoIngestSettingsPanel.outputPathTextField.text=
AutoIngestSettingsPanel.jLabelSelectOutputFolder.text=Select shared results folder:
AutoIngestSettingsPanel.browseInputFolderButton.text=Browse
AutoIngestSettingsPanel.inputPathTextField.toolTipText=Input folder for automated processing, i.e., the location where input case folders will be created for ingest by automated processing mode
AutoIngestSettingsPanel.inputPathTextField.text=
AutoIngestSettingsPanel.jLabelSelectInputFolder.text=Select shared images folder:
AutoIngestSettingsPanel.jRadioButtonReview.toolTipText=Review cases created in automated processing mode
AutoIngestSettingsPanel.jRadioButtonReview.text=Examiner
AutoIngestSettingsPanel.jRadioButtonAutomated.toolTipText=Automatically detect new data sources and create cases.
AutoIngestSettingsPanel.jRadioButtonAutomated.text=Automated ingest
AutoIngestSettingsPanel.restartRequiredNodeLabel.text=Application restart required
AutoIngestSettingsPanel.jLabelSelectMode.text=Select mode:
AutoIngestSettingsPanel.jPanelIngestSettings.border.title=Automated Ingest Settings
AutoIngestSettingsPanel.bnLogging.text=Node Status Logging
AutoIngestSettingsPanel.bnFileExport.text=File Export Settings
AutoIngestSettingsPanel.bnAdvancedSettings.text=Advanced Settings
AutoIngestSettingsPanel.bnEditIngestSettings.toolTipText=Ingest job settings for the automated processing mode context.
AutoIngestSettingsPanel.bnEditIngestSettings.text=Ingest Module Settings

View File

@ -0,0 +1,2 @@
AutoIngestSettingsPanel.cbJoinAutoIngestCluster.text=

View File

@ -0,0 +1,277 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Component id="pnDatabaseSettings" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="232" max="-2" attributes="0"/>
<Component id="bnOk" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="bnCancel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="cbEnableLogging" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="421" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="79" max="-2" attributes="0"/>
<Component id="pnDatabaseSettings" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="bnOk" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnCancel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="69" max="32767" attributes="0"/>
</Group>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="48" max="-2" attributes="0"/>
<Component id="cbEnableLogging" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="324" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="pnDatabaseSettings">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="lbDatabaseSettings" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="344" max="32767" attributes="0"/>
<Component id="bnTestDatabase" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="lbTestDatabase" min="-2" pref="16" max="-2" attributes="0"/>
</Group>
<Component id="tbDbHostname" alignment="0" max="32767" attributes="0"/>
<Component id="tbDbPort" alignment="0" max="32767" attributes="0"/>
<Component id="tbDbUsername" alignment="0" max="32767" attributes="0"/>
<Component id="tbDbPassword" max="32767" attributes="0"/>
<Component id="tbDbName" max="32767" attributes="0"/>
<Component id="lbTestDbWarning" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="bnTestDatabase" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbTestDatabase" alignment="0" min="-2" pref="23" max="-2" attributes="0"/>
<Component id="lbDatabaseSettings" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="tbDbHostname" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="tbDbPort" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="tbDbUsername" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="tbDbPassword" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="tbDbName" min="-2" pref="20" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="lbTestDbWarning" min="-2" pref="16" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="24" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JTextField" name="tbDbHostname">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="false" component="tbDbHostname" property="font" relativeSize="false" size="12"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbHostname.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbHostname.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbDbPort">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="false" component="tbDbPort" property="font" relativeSize="false" size="12"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbPort.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbPort.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbDbUsername">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="false" component="tbDbUsername" property="font" relativeSize="false" size="12"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbUsername.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbUsername.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JPasswordField" name="tbDbPassword">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="false" component="tbDbPassword" property="font" relativeSize="false" size="12"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbPassword.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbPassword.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbDatabaseSettings">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="false" component="lbDatabaseSettings" property="font" relativeSize="false" size="12"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.lbDatabaseSettings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="verticalAlignment" type="int" value="1"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnTestDatabase">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="false" component="bnTestDatabase" property="font" relativeSize="false" size="11"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.bnTestDatabase.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnTestDatabaseActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lbTestDatabase">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.lbTestDatabase.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="autoscrolls" type="boolean" value="true"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbTestDbWarning">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.lbTestDbWarning.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbDbName">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbName.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.tbDbName.toolTipText_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JCheckBox" name="cbEnableLogging">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="false" component="cbEnableLogging" property="font" relativeSize="false" size="11"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.cbEnableLogging.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="cbEnableLoggingItemStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnOk">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.bnOk.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOkActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnCancel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="experimental/configuration/Bundle.properties" key="NodeStatusLogPanel.bnCancel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnCancelActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,422 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.configuration;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.TextPrompt;
import java.awt.Cursor;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.openide.util.ImageUtilities;
import org.sleuthkit.autopsy.core.UserPreferencesException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.experimental.autoingest.StatusDatabaseLogger;
/**
*
*/
public class NodeStatusLogPanel extends javax.swing.JPanel {
private static final String HOST_NAME_OR_IP_PROMPT = NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbHostname.toolTipText");
private static final String PORT_PROMPT = NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbPort.toolTipText");
private static final String USER_NAME_PROMPT = NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbUsername.toolTipText");
private static final String PASSWORD_PROMPT = NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbPassword.toolTipText");
private static final String DATABASE_NAME_PROMPT = NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbName.toolTipText");
private final ImageIcon goodIcon;
private final ImageIcon badIcon;
JDialog jDialog;
private static final Logger logger = Logger.getLogger(NodeStatusLogPanel.class.getName());
/**
* Creates new form DatabaseLogPanell
*/
public NodeStatusLogPanel(JDialog jDialog) {
initComponents();
load();
validateSettings();
this.jDialog = jDialog;
tbDbHostname.getDocument().addDocumentListener(new MyDocumentListener());
tbDbPort.getDocument().addDocumentListener(new MyDocumentListener());
tbDbPassword.getDocument().addDocumentListener(new MyDocumentListener());
tbDbUsername.getDocument().addDocumentListener(new MyDocumentListener());
tbDbName.getDocument().addDocumentListener(new MyDocumentListener());
/**
* Add text prompts to all of the text fields.
*/
Collection<TextPrompt> textPrompts = new ArrayList<>();
textPrompts.add(new TextPrompt(HOST_NAME_OR_IP_PROMPT, tbDbHostname));
textPrompts.add(new TextPrompt(PORT_PROMPT, tbDbPort));
textPrompts.add(new TextPrompt(USER_NAME_PROMPT, tbDbUsername));
textPrompts.add(new TextPrompt(PASSWORD_PROMPT, tbDbPassword));
textPrompts.add(new TextPrompt(DATABASE_NAME_PROMPT, tbDbName));
configureTextPrompts(textPrompts);
goodIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/good.png", false));
badIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/bad.png", false));
}
final void validateSettings(){
if(valid()){
bnOk.setEnabled(true);
if(cbEnableLogging.isSelected()){
bnTestDatabase.setEnabled(true);
} else {
bnTestDatabase.setEnabled(false);
}
} else {
bnOk.setEnabled(false);
bnTestDatabase.setEnabled(false);
}
}
private boolean valid(){
if(cbEnableLogging.isSelected()){
if(tbDbHostname.getText().isEmpty()
|| tbDbPort.getText().isEmpty()
|| tbDbUsername.getText().isEmpty()
|| (tbDbPassword.getPassword().length == 0)
|| tbDbName.getText().isEmpty()){
return false;
}
}
return true;
}
private void enableFields(boolean enable){
tbDbHostname.setEnabled(enable);
tbDbPort.setEnabled(enable);
tbDbUsername.setEnabled(enable);
tbDbPassword.setEnabled(enable);
tbDbName.setEnabled(enable);
}
final void load(){
try{
cbEnableLogging.setSelected(AutoIngestUserPreferences.getStatusDatabaseLoggingEnabled());
tbDbHostname.setText(AutoIngestUserPreferences.getLoggingDatabaseHostnameOrIP());
tbDbPort.setText(AutoIngestUserPreferences.getLoggingPort());
tbDbUsername.setText(AutoIngestUserPreferences.getLoggingUsername());
tbDbPassword.setText(AutoIngestUserPreferences.getLoggingPassword());
} catch (UserPreferencesException ex) {
logger.log(Level.SEVERE, "Error accessing status database connection info", ex); //NON-NLS
}
tbDbName.setText(AutoIngestUserPreferences.getLoggingDatabaseName());
}
void store(){
AutoIngestUserPreferences.setStatusDatabaseLoggingEnabled(cbEnableLogging.isSelected());
if(cbEnableLogging.isSelected()){
try{
AutoIngestUserPreferences.setLoggingDatabaseHostnameOrIP(tbDbHostname.getText().trim());
AutoIngestUserPreferences.setLoggingPort(tbDbPort.getText().trim());
AutoIngestUserPreferences.setLoggingUsername(tbDbUsername.getText().trim());
AutoIngestUserPreferences.setLoggingPassword(new String(tbDbPassword.getPassword()));
AutoIngestUserPreferences.setLoggingDatabaseName(tbDbName.getText().trim());
} catch (UserPreferencesException ex) {
logger.log(Level.SEVERE, "Error saving database connection info", ex); //NON-NLS
}
}
}
/**
* Sets the foreground color and transparency of a collection of text
* prompts.
*
* @param textPrompts The text prompts to configure.
*/
private static void configureTextPrompts(Collection<TextPrompt> textPrompts) {
float alpha = 0.9f; // Mostly opaque
for (TextPrompt textPrompt : textPrompts) {
textPrompt.setForeground(Color.LIGHT_GRAY);
textPrompt.changeAlpha(alpha);
}
}
private void testDatabase(){
String host = tbDbHostname.getText();
String port = tbDbPort.getText();
String username = tbDbUsername.getText();
String password = new String(tbDbPassword.getPassword());
String dbName = tbDbName.getText();
lbTestDatabase.setIcon(null);
lbTestDbWarning.setText("");
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// First test whether we can connect to the database
try{
Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException ex){
// Continue on even if this fails
}
try (Connection connection = DriverManager.getConnection("jdbc:postgresql://" + host + ":" + port + "/" + dbName,
username, password);
Statement statement = connection.createStatement();) {
// Now make sure the database is set up for logging
try{
StatusDatabaseLogger.logToStatusDatabase(statement, "Testing configuration", false);
lbTestDatabase.setIcon(goodIcon);
lbTestDbWarning.setText("");
} catch (SQLException ex){
lbTestDatabase.setIcon(badIcon);
lbTestDbWarning.setText("Database is not correctly initialized - " + ex.getMessage());
}
} catch (SQLException ex) {
lbTestDatabase.setIcon(badIcon);
lbTestDbWarning.setText(ex.getMessage());
} finally {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
pnDatabaseSettings = new javax.swing.JPanel();
tbDbHostname = new javax.swing.JTextField();
tbDbPort = new javax.swing.JTextField();
tbDbUsername = new javax.swing.JTextField();
tbDbPassword = new javax.swing.JPasswordField();
lbDatabaseSettings = new javax.swing.JLabel();
bnTestDatabase = new javax.swing.JButton();
lbTestDatabase = new javax.swing.JLabel();
lbTestDbWarning = new javax.swing.JLabel();
tbDbName = new javax.swing.JTextField();
cbEnableLogging = new javax.swing.JCheckBox();
bnOk = new javax.swing.JButton();
bnCancel = new javax.swing.JButton();
pnDatabaseSettings.setBorder(javax.swing.BorderFactory.createEtchedBorder());
tbDbHostname.setFont(tbDbHostname.getFont().deriveFont(tbDbHostname.getFont().getStyle() & ~java.awt.Font.BOLD, 12));
tbDbHostname.setText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbHostname.text")); // NOI18N
tbDbHostname.setToolTipText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbHostname.toolTipText")); // NOI18N
tbDbPort.setFont(tbDbPort.getFont().deriveFont(tbDbPort.getFont().getStyle() & ~java.awt.Font.BOLD, 12));
tbDbPort.setText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbPort.text")); // NOI18N
tbDbPort.setToolTipText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbPort.toolTipText")); // NOI18N
tbDbUsername.setFont(tbDbUsername.getFont().deriveFont(tbDbUsername.getFont().getStyle() & ~java.awt.Font.BOLD, 12));
tbDbUsername.setText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbUsername.text")); // NOI18N
tbDbUsername.setToolTipText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbUsername.toolTipText")); // NOI18N
tbDbPassword.setFont(tbDbPassword.getFont().deriveFont(tbDbPassword.getFont().getStyle() & ~java.awt.Font.BOLD, 12));
tbDbPassword.setText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbPassword.text")); // NOI18N
tbDbPassword.setToolTipText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbPassword.toolTipText")); // NOI18N
lbDatabaseSettings.setFont(lbDatabaseSettings.getFont().deriveFont(lbDatabaseSettings.getFont().getStyle() & ~java.awt.Font.BOLD, 12));
org.openide.awt.Mnemonics.setLocalizedText(lbDatabaseSettings, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.lbDatabaseSettings.text")); // NOI18N
lbDatabaseSettings.setVerticalAlignment(javax.swing.SwingConstants.TOP);
bnTestDatabase.setFont(bnTestDatabase.getFont().deriveFont(bnTestDatabase.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
org.openide.awt.Mnemonics.setLocalizedText(bnTestDatabase, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.bnTestDatabase.text")); // NOI18N
bnTestDatabase.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnTestDatabaseActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(lbTestDatabase, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.lbTestDatabase.text")); // NOI18N
lbTestDatabase.setAutoscrolls(true);
lbTestDbWarning.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(lbTestDbWarning, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.lbTestDbWarning.text")); // NOI18N
tbDbName.setText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbName.text")); // NOI18N
tbDbName.setToolTipText(org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.tbDbName.toolTipText_1")); // NOI18N
javax.swing.GroupLayout pnDatabaseSettingsLayout = new javax.swing.GroupLayout(pnDatabaseSettings);
pnDatabaseSettings.setLayout(pnDatabaseSettingsLayout);
pnDatabaseSettingsLayout.setHorizontalGroup(
pnDatabaseSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnDatabaseSettingsLayout.createSequentialGroup()
.addContainerGap()
.addGroup(pnDatabaseSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnDatabaseSettingsLayout.createSequentialGroup()
.addComponent(lbDatabaseSettings)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 344, Short.MAX_VALUE)
.addComponent(bnTestDatabase)
.addGap(18, 18, 18)
.addComponent(lbTestDatabase, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(tbDbHostname)
.addComponent(tbDbPort)
.addComponent(tbDbUsername)
.addComponent(tbDbPassword)
.addComponent(tbDbName)
.addComponent(lbTestDbWarning, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
pnDatabaseSettingsLayout.setVerticalGroup(
pnDatabaseSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnDatabaseSettingsLayout.createSequentialGroup()
.addContainerGap()
.addGroup(pnDatabaseSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(bnTestDatabase)
.addComponent(lbTestDatabase, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbDatabaseSettings))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tbDbHostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tbDbPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tbDbUsername, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tbDbPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tbDbName, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(lbTestDbWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(24, 24, 24))
);
cbEnableLogging.setFont(cbEnableLogging.getFont().deriveFont(cbEnableLogging.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
org.openide.awt.Mnemonics.setLocalizedText(cbEnableLogging, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.cbEnableLogging.text")); // NOI18N
cbEnableLogging.addItemListener(new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
cbEnableLoggingItemStateChanged(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(bnOk, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.bnOk.text")); // NOI18N
bnOk.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnOkActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(bnCancel, org.openide.util.NbBundle.getMessage(NodeStatusLogPanel.class, "NodeStatusLogPanel.bnCancel.text")); // NOI18N
bnCancel.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnCancelActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(pnDatabaseSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
.addGroup(layout.createSequentialGroup()
.addGap(232, 232, 232)
.addComponent(bnOk)
.addGap(18, 18, 18)
.addComponent(bnCancel)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(cbEnableLogging)
.addContainerGap(421, Short.MAX_VALUE)))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(79, 79, 79)
.addComponent(pnDatabaseSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(bnOk)
.addComponent(bnCancel))
.addContainerGap(69, Short.MAX_VALUE))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(48, 48, 48)
.addComponent(cbEnableLogging)
.addContainerGap(324, Short.MAX_VALUE)))
);
}// </editor-fold>//GEN-END:initComponents
private void cbEnableLoggingItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cbEnableLoggingItemStateChanged
enableFields(cbEnableLogging.isSelected());
validateSettings();
}//GEN-LAST:event_cbEnableLoggingItemStateChanged
private void bnTestDatabaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestDatabaseActionPerformed
testDatabase();
}//GEN-LAST:event_bnTestDatabaseActionPerformed
private void bnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOkActionPerformed
store();
jDialog.dispose();
}//GEN-LAST:event_bnOkActionPerformed
private void bnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnCancelActionPerformed
jDialog.dispose();
}//GEN-LAST:event_bnCancelActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton bnCancel;
private javax.swing.JButton bnOk;
private javax.swing.JButton bnTestDatabase;
private javax.swing.JCheckBox cbEnableLogging;
private javax.swing.JLabel lbDatabaseSettings;
private javax.swing.JLabel lbTestDatabase;
private javax.swing.JLabel lbTestDbWarning;
private javax.swing.JPanel pnDatabaseSettings;
private javax.swing.JTextField tbDbHostname;
private javax.swing.JTextField tbDbName;
private javax.swing.JPasswordField tbDbPassword;
private javax.swing.JTextField tbDbPort;
private javax.swing.JTextField tbDbUsername;
// End of variables declaration//GEN-END:variables
private class MyDocumentListener implements DocumentListener {
@Override
public void changedUpdate(DocumentEvent e) {
validateSettings();
}
@Override
public void removeUpdate(DocumentEvent e) {
validateSettings();
}
@Override
public void insertUpdate(DocumentEvent e) {
validateSettings();
}
};
}

View File

@ -0,0 +1,140 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.configuration;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JDialog;
import javax.swing.WindowConstants;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.CueBannerPanel;
import org.sleuthkit.autopsy.casemodule.StartupWindowInterface;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestDashboard;
import org.sleuthkit.autopsy.experimental.autoingest.ReviewModeCasePanel;
/**
* The default implementation of the Autopsy startup window
*/
@ServiceProvider(service = StartupWindowInterface.class)
public final class StartupWindow extends JDialog implements StartupWindowInterface {
private static final String TITLE = NbBundle.getMessage(StartupWindow.class, "StartupWindow.title.text");
private static Dimension DIMENSIONS = new Dimension(750, 400);
private static CueBannerPanel welcomeWindow;
private static final long serialVersionUID = 1L;
private ReviewModeCasePanel caseManagementPanel = null;
private static final String LOCAL_HOST_NAME = NetworkUtils.getLocalHostName();
public StartupWindow() {
super(WindowManager.getDefault().getMainWindow(), TITLE, false);
init();
}
/**
* Shows the startup window.
*/
private void init() {
Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
// set the popUp window / JFrame
setSize(DIMENSIONS);
int w = getSize().width;
int h = getSize().height;
// set the location of the popUp Window on the center of the screen
setLocation((screenDimension.width - w) / 2, (screenDimension.height - h) / 2);
setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
addPanelForMode();
pack();
setResizable(false);
}
@Override
public void open() {
if (caseManagementPanel != null) {
caseManagementPanel.updateView();
caseManagementPanel.setCursor(Cursor.getDefaultCursor());
}
if (welcomeWindow != null) {
welcomeWindow.refresh();
}
this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
setVisible(true);
}
/**
* Closes the startup window.
*/
@Override
public void close() {
this.setVisible(false);
}
/**
* Adds a panel to the dialog based on operational mode selected by the
* user.
*/
private void addPanelForMode() {
AutoIngestUserPreferences.SelectedMode mode = AutoIngestUserPreferences.getMode();
switch (mode) {
case AUTOMATED:
this.setTitle(NbBundle.getMessage(StartupWindow.class, "StartupWindow.AutoIngestMode") + " (" + LOCAL_HOST_NAME + ")");
setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/frame.gif", false)); //NON-NLS
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
AutoIngestDashboard.getInstance().shutdown();
}
});
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
add(AutoIngestDashboard.getInstance());
break;
case REVIEW:
this.setTitle(NbBundle.getMessage(StartupWindow.class, "StartupWindow.ReviewMode") + " (" + LOCAL_HOST_NAME + ")");
caseManagementPanel = new ReviewModeCasePanel(this);
setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/experimental/images/frame.gif", false)); //NON-NLS
add(caseManagementPanel);
break;
default:
welcomeWindow = new CueBannerPanel();
// add the command to close the window to the button on the Volume Detail Panel
welcomeWindow.setCloseButtonActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
close();
}
});
add(welcomeWindow);
break;
}
}
}

View File

@ -0,0 +1,418 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.coordinationservice;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.curator.RetryPolicy;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
import org.apache.zookeeper.KeeperException;
import org.sleuthkit.autopsy.core.UserPreferences;
import java.io.IOException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.KeeperException.NoNodeException;
/**
* A centralized service for maintaining configuration information and providing
* distributed synchronization using a shared hierarchical namespace of nodes.
*/
public final class CoordinationService {
/**
* Category nodes are the immediate children of the root node of a shared
* hierarchical namespace managed by the coordination service.
*/
public enum CategoryNode { // RJCTODO: Move this to CoordinationServiceNamespace
CASES("cases"),
MANIFESTS("manifests"),
CONFIG("config");
private final String displayName;
private CategoryNode(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}
/**
* Exception type thrown by the coordination service.
*/
public final static class CoordinationServiceException extends Exception {
private static final long serialVersionUID = 1L;
private CoordinationServiceException(String message) {
super(message);
}
private CoordinationServiceException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* An opaque encapsulation of a lock for use in distributed synchronization.
* Instances are obtained by calling a get lock method and must be passed to
* a release lock method.
*/
public static class Lock implements AutoCloseable {
/**
* This implementation uses the Curator read/write lock. see
* http://curator.apache.org/curator-recipes/shared-reentrant-read-write-lock.html
*/
private final InterProcessMutex interProcessLock;
private final String nodePath;
private Lock(String nodePath, InterProcessMutex lock) {
this.nodePath = nodePath;
this.interProcessLock = lock;
}
public String getNodePath() {
return nodePath;
}
public void release() throws CoordinationServiceException {
try {
this.interProcessLock.release();
} catch (Exception ex) {
throw new CoordinationServiceException(String.format("Failed to release the lock on %s", nodePath), ex);
}
}
@Override
public void close() throws CoordinationServiceException {
release();
}
}
private static CuratorFramework curator = null;
private static final Map<String, CoordinationService> rootNodesToServices = new HashMap<>();
private final Map<String, String> categoryNodeToPath = new HashMap<>();
private static final int SESSION_TIMEOUT_MILLISECONDS = 300000;
private static final int CONNECTION_TIMEOUT_MILLISECONDS = 300000;
private static final int ZOOKEEPER_SESSION_TIMEOUT_MILLIS = 3000;
private static final int ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS = 15000;
private static final int PORT_OFFSET = 1000;
/**
* Gets an instance of the centralized coordination service for a specific
* namespace.
*
* @param rootNode The name of the root node that defines the namespace.
*
* @return The service for the namespace defined by the root node name.
*
* @throws CoordinationServiceException If an instaNce of the coordination
* service cannot be created.
*/
public static synchronized CoordinationService getInstance(String rootNode) throws CoordinationServiceException {
if (null == curator) {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
// When run in Solr, ZooKeeper defaults to Solr port + 1000
int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort;
curator = CuratorFrameworkFactory.newClient(connectString, SESSION_TIMEOUT_MILLISECONDS, CONNECTION_TIMEOUT_MILLISECONDS, retryPolicy);
curator.start();
}
/*
* Get or create a coordination service for the namespace defined by the
* specified root node.
*/
if (rootNodesToServices.containsKey(rootNode)) {
return rootNodesToServices.get(rootNode);
} else {
CoordinationService service;
try {
service = new CoordinationService(rootNode);
} catch (Exception ex) {
curator = null;
throw new CoordinationServiceException("Failed to create coordination service", ex);
}
rootNodesToServices.put(rootNode, service);
return service;
}
}
/**
* Constructs an instance of the centralized coordination service for a
* specific namespace.
*
* @param rootNodeName The name of the root node that defines the namespace.
*/
private CoordinationService(String rootNodeName) throws Exception {
if (false == isZooKeeperAccessible()) {
throw new Exception("Unable to access ZooKeeper");
}
String rootNode = rootNodeName;
if (!rootNode.startsWith("/")) {
rootNode = "/" + rootNode;
}
for (CategoryNode node : CategoryNode.values()) {
String nodePath = rootNode + "/" + node.getDisplayName();
try {
curator.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE).forPath(nodePath);
} catch (KeeperException ex) {
if (ex.code() != KeeperException.Code.NODEEXISTS) {
throw ex;
}
}
categoryNodeToPath.put(node.getDisplayName(), nodePath);
}
}
/**
* Tries to get an exclusive lock on a node path appended to a category path
* in the namespace managed by this coordination service. Blocks until the
* lock is obtained or the time out expires.
*
* @param category The desired category in the namespace.
* @param nodePath The node path to use as the basis for the lock.
* @param timeOut Length of the time out.
* @param timeUnit Time unit for the time out.
*
* @return The lock, or null if lock acquisition timed out.
*
* @throws CoordinationServiceException If there is an error during lock
* acquisition.
* @throws InterruptedException If interrupted while blocked during
* lock acquisition.
*/
public Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit) throws CoordinationServiceException, InterruptedException {
String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
try {
InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
if (lock.writeLock().acquire(timeOut, timeUnit)) {
return new Lock(nodePath, lock.writeLock());
} else {
return null;
}
} catch (Exception ex) {
if (ex instanceof InterruptedException) {
throw (InterruptedException) ex;
} else {
throw new CoordinationServiceException(String.format("Failed to get exclusive lock for %s", fullNodePath), ex);
}
}
}
/**
* Tries to get an exclusive lock on a node path appended to a category path
* in the namespace managed by this coordination service. Returns
* immediately if the lock can not be acquired.
*
* @param category The desired category in the namespace.
* @param nodePath The node path to use as the basis for the lock.
*
* @return The lock, or null if the lock could not be obtained.
*
* @throws CoordinationServiceException If there is an error during lock
* acquisition.
*/
public Lock tryGetExclusiveLock(CategoryNode category, String nodePath) throws CoordinationServiceException {
String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
try {
InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
if (!lock.writeLock().acquire(0, TimeUnit.SECONDS)) {
return null;
}
return new Lock(nodePath, lock.writeLock());
} catch (Exception ex) {
throw new CoordinationServiceException(String.format("Failed to get exclusive lock for %s", fullNodePath), ex);
}
}
/**
* Tries to get a shared lock on a node path appended to a category path in
* the namespace managed by this coordination service. Blocks until the lock
* is obtained or the time out expires.
*
* @param category The desired category in the namespace.
* @param nodePath The node path to use as the basis for the lock.
* @param timeOut Length of the time out.
* @param timeUnit Time unit for the time out.
*
* @return The lock, or null if lock acquisition timed out.
*
* @throws CoordinationServiceException If there is an error during lock
* acquisition.
* @throws InterruptedException If interrupted while blocked during
* lock acquisition.
*/
public Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit) throws CoordinationServiceException, InterruptedException {
String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
try {
InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
if (lock.readLock().acquire(timeOut, timeUnit)) {
return new Lock(nodePath, lock.readLock());
} else {
return null;
}
} catch (Exception ex) {
if (ex instanceof InterruptedException) {
throw (InterruptedException) ex;
} else {
throw new CoordinationServiceException(String.format("Failed to get shared lock for %s", fullNodePath), ex);
}
}
}
/**
* Tries to get a shared lock on a node path appended to a category path in
* the namespace managed by this coordination service. Returns immediately
* if the lock can not be acquired.
*
* @param category The desired category in the namespace.
* @param nodePath The node path to use as the basis for the lock.
*
* @return The lock, or null if the lock could not be obtained.
*
* @throws CoordinationServiceException If there is an error during lock
* acquisition.
*/
public Lock tryGetSharedLock(CategoryNode category, String nodePath) throws CoordinationServiceException {
String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
try {
InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
if (!lock.readLock().acquire(0, TimeUnit.SECONDS)) {
return null;
}
return new Lock(nodePath, lock.readLock());
} catch (Exception ex) {
throw new CoordinationServiceException(String.format("Failed to get shared lock for %s", fullNodePath), ex);
}
}
/**
* Retrieve the data associated with the specified node.
*
* @param category The desired category in the namespace.
* @param nodePath The node to retrieve the data for.
*
* @return The data associated with the node, if any, or null if the node
* has not been created yet.
*
* @throws CoordinationServiceException If there is an error setting the
* node data.
* @throws InterruptedException If interrupted while blocked during
* setting of node data.
*/
public byte[] getNodeData(CategoryNode category, String nodePath) throws CoordinationServiceException, InterruptedException {
String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
try {
return curator.getData().forPath(fullNodePath);
} catch (NoNodeException ex) {
return null;
} catch (Exception ex) {
if (ex instanceof InterruptedException) {
throw (InterruptedException) ex;
} else {
throw new CoordinationServiceException(String.format("Failed to get data for %s", fullNodePath), ex);
}
}
}
/**
* Store the given data with the specified node.
*
* @param category The desired category in the namespace.
* @param nodePath The node to associate the data with.
* @param data The data to store with the node.
*
* @throws CoordinationServiceException If there is an error setting the
* node data.
* @throws InterruptedException If interrupted while blocked during
* setting of node data.
*/
public void setNodeData(CategoryNode category, String nodePath, byte[] data) throws CoordinationServiceException, InterruptedException {
String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
try {
curator.setData().forPath(fullNodePath, data);
} catch (Exception ex) {
if (ex instanceof InterruptedException) {
throw (InterruptedException) ex;
} else {
throw new CoordinationServiceException(String.format("Failed to set data for %s", fullNodePath), ex);
}
}
}
/**
* Creates a node path within a given category.
*
* @param category A category node.
* @param nodePath A node path relative to a category node path.
*
* @return
*/
private String getFullyQualifiedNodePath(CategoryNode category, String nodePath) {
return categoryNodeToPath.get(category.getDisplayName()) + "/" + nodePath.toUpperCase();
}
/**
* Determines if ZooKeeper is accessible with the current settings. Closes
* the connection prior to returning.
*
* @return true if a connection was achieved, false otherwise
*/
private static boolean isZooKeeperAccessible() {
boolean result = false;
Object workerThreadWaitNotifyLock = new Object();
int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort;
try {
ZooKeeper zooKeeper = new ZooKeeper(connectString, ZOOKEEPER_SESSION_TIMEOUT_MILLIS,
(WatchedEvent event) -> {
synchronized (workerThreadWaitNotifyLock) {
workerThreadWaitNotifyLock.notify();
}
});
synchronized (workerThreadWaitNotifyLock) {
workerThreadWaitNotifyLock.wait(ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS);
}
ZooKeeper.States state = zooKeeper.getState();
if (state == ZooKeeper.States.CONNECTED || state == ZooKeeper.States.CONNECTEDREADONLY) {
result = true;
}
zooKeeper.close();
} catch (InterruptedException | IOException ignored) {
}
return result;
}
}

Some files were not shown because too many files have changed in this diff Show More