diff --git a/Core/ivy.xml b/Core/ivy.xml
index f20453a141..cbe45d8c33 100644
--- a/Core/ivy.xml
+++ b/Core/ivy.xml
@@ -23,6 +23,8 @@
+
+
diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties
index 1b0a695edd..89bf4a93d7 100644
--- a/Core/nbproject/project.properties
+++ b/Core/nbproject/project.properties
@@ -18,8 +18,8 @@ file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar
file.reference.sqlite-jdbc-3.8.11.jar=release\\modules\\ext\\sqlite-jdbc-3.8.11.jar
file.reference.StixLib.jar=release/modules/ext/StixLib.jar
file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar
-file.reference.jackcess-2.1.8.jar=release/modules/ext/jackcess-2.1.8.jar
-file.reference.jackcess-encrypt-2.1.2.jar=release/modules/ext/jackcess-encrypt-2.1.2.jar
+file.reference.jackcess-2.2.0.jar=release/modules/ext/jackcess-2.2.0.jar
+file.reference.jackcess-encrypt-2.1.4.jar=release/modules/ext/jackcess-encrypt-2.1.4.jar
file.reference.jempbox-1.8.13.jar=release/modules/ext/jempbox-1.8.13.jar
file.reference.javax.ws.rs-api-2.0.1.jar=release/modules/ext/javax.ws.rs-api-2.0.1.jar
file.reference.cxf-core-3.0.16.jar=release/modules/ext/cxf-core-3.0.16.jar
@@ -29,7 +29,7 @@ file.reference.cxf-rt-transports-http-3.0.16.jar=release/modules/ext/cxf-rt-tran
file.reference.fontbox-2.0.8.jar=release/modules/ext/fontbox-2.0.8.jar
file.reference.pdfbox-2.0.8.jar=release/modules/ext/pdfbox-2.0.8.jar
file.reference.pdfbox-tools-2.0.8.jar=release/modules/ext/pdfbox-tools-2.0.8.jar
-file.reference.sleuthkit-postgresql-4.6.3.jar=release/modules/ext/sleuthkit-postgresql-4.6.3.jar
+file.reference.sleuthkit-postgresql-4.6.4.jar=release/modules/ext/sleuthkit-postgresql-4.6.4.jar
file.reference.tika-core-1.17.jar=release/modules/ext/tika-core-1.17.jar
file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar
diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml
index d142e0b8c9..7a89519e34 100644
--- a/Core/nbproject/project.xml
+++ b/Core/nbproject/project.xml
@@ -342,8 +342,8 @@
org.sleuthkit.datamodel
- ext/jackcess-2.1.8.jar
- release/modules/ext/jackcess-2.1.8.jar
+ ext/jackcess-2.2.0.jar
+ release/modules/ext/jackcess-2.2.0.jar
ext/zookeeper-3.4.6.jar
@@ -394,8 +394,8 @@
release/modules/ext/sevenzipjbinding.jar
- ext/sleuthkit-postgresql-4.6.3.jar
- release/modules/ext/sleuthkit-postgresql-4.6.3.jar
+ ext/sleuthkit-postgresql-4.6.4.jar
+ release/modules/ext/sleuthkit-postgresql-4.6.4.jar
ext/mchange-commons-java-0.2.9.jar
@@ -482,8 +482,8 @@
release\modules\ext\commons-pool2-2.4.2.jar
- ext/jackcess-encrypt-2.1.2.jar
- release/modules/ext/jackcess-encrypt-2.1.2.jar
+ ext/jackcess-encrypt-2.1.4.jar
+ release/modules/ext/jackcess-encrypt-2.1.4.jar
ext/jsoup-1.10.3.jar
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java
index eba4119adf..ec74c362aa 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java
@@ -122,6 +122,7 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TimelineManager;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
+import org.sleuthkit.autopsy.coreutils.StopWatch;
/**
* An Autopsy case. Currently, only one case at a time may be open.
@@ -743,11 +744,15 @@ public class Case {
"Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or there is a problem with the coordination service."
})
public static void deleteCase(CaseMetadata metadata) throws CaseActionException {
+ StopWatch stopWatch = new StopWatch();
+ stopWatch.start();
synchronized (caseActionSerializationLock) {
if (null != currentCase) {
throw new CaseActionException(Bundle.Case_exceptionMessage_cannotDeleteCurrentCase());
}
}
+ stopWatch.stop();
+ logger.log(Level.INFO, String.format("Used %d s to acquire caseActionSerializationLock (Java monitor in Case class) for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
/*
* Set up either a GUI progress indicator without a cancel button (can't
@@ -769,10 +774,19 @@ public class Case {
* cannot be deleted if another node has it open.
*/
progressIndicator.progress(Bundle.Case_progressMessage_checkingForOtherUser());
+ stopWatch.reset();
+ stopWatch.start();
try (CoordinationService.Lock dirLock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CASES, metadata.getCaseDirectory())) {
- assert (null != dirLock);
- deleteCase(metadata, progressIndicator);
+ stopWatch.stop();
+ logger.log(Level.INFO, String.format("Used %d s to acquire case directory coordination service lock for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
+ if (dirLock != null) {
+ deleteCase(metadata, progressIndicator);
+ } else {
+ throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
+ }
} catch (CoordinationServiceException ex) {
+ stopWatch.stop();
+ logger.log(Level.INFO, String.format("Used %d s to fail to acquire case directory coordination service lock for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
throw new CaseActionException(Bundle.Case_exceptionMessage_cannotGetLockToDeleteCase(), ex);
}
}
@@ -982,11 +996,13 @@ public class Case {
"Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details"
})
private static void deleteCase(CaseMetadata metadata, ProgressIndicator progressIndicator) throws CaseActionException {
+ StopWatch stopWatch = new StopWatch();
boolean errorsOccurred = false;
if (CaseType.MULTI_USER_CASE == metadata.getCaseType()) {
/*
* Delete the case database from the database server.
*/
+ stopWatch.start();
try {
progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDatabase());
CaseDbConnectionInfo db;
@@ -996,10 +1012,14 @@ public class Case {
Statement statement = connection.createStatement();) {
String deleteCommand = "DROP DATABASE \"" + metadata.getCaseDatabaseName() + "\""; //NON-NLS
statement.execute(deleteCommand);
+ stopWatch.stop();
+ logger.log(Level.INFO, String.format("Used %d s to delete case database for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
}
} catch (UserPreferencesException | ClassNotFoundException | SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to delete case database %s for %s (%s) in %s", metadata.getCaseDatabaseName(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
errorsOccurred = true;
+ stopWatch.stop();
+ logger.log(Level.INFO, String.format("Used %d s to fail delete case database for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
}
}
@@ -1009,10 +1029,16 @@ public class Case {
progressIndicator.progress(Bundle.Case_progressMessage_deletingTextIndex());
for (KeywordSearchService searchService : Lookup.getDefault().lookupAll(KeywordSearchService.class)) {
try {
+ stopWatch.reset();
+ stopWatch.start();
searchService.deleteTextIndex(metadata);
+ stopWatch.stop();
+ logger.log(Level.INFO, String.format("Used %d s to delete text index for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
} catch (KeywordSearchServiceException ex) {
logger.log(Level.SEVERE, String.format("Failed to delete text index for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex);
errorsOccurred = true;
+ stopWatch.stop();
+ logger.log(Level.INFO, String.format("Used %d s to fail to delete text index for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
}
}
@@ -1020,9 +1046,16 @@ public class Case {
* Delete the case directory.
*/
progressIndicator.progress(Bundle.Case_progressMessage_deletingCaseDirectory());
+ stopWatch.reset();
+ stopWatch.start();
if (!FileUtil.deleteDir(new File(metadata.getCaseDirectory()))) {
logger.log(Level.SEVERE, String.format("Failed to delete case directory for %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
errorsOccurred = true;
+ stopWatch.stop();
+ logger.log(Level.INFO, String.format("Used %d s to fail to delete case directory for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
+ } else {
+ stopWatch.stop();
+ logger.log(Level.INFO, String.format("Used %d s to delete case directory for %s (%s) in %s", stopWatch.getElapsedTimeSecs(), metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()));
}
/*
@@ -1576,11 +1609,13 @@ public class Case {
}
/**
- * Notifies case event subscribers that a central repository comment has been changed.
- *
+ * Notifies case event subscribers that a central repository comment has
+ * been changed.
+ *
* This should not be called from the event dispatch thread (EDT)
- *
- * @param contentId the objectId for the Content which has had its central repo comment changed
+ *
+ * @param contentId the objectId for the Content which has had its central
+ * repo comment changed
* @param newComment the new value of the comment
*/
public void notifyCentralRepoCommentChanged(long contentId, String newComment) {
@@ -1836,7 +1871,7 @@ public class Case {
progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources());
acquireSharedCaseDirLock(metadata.getCaseDirectory());
try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) {
- assert (null != resourcesLock);
+ assert(resourcesLock != null); // Use reference to avoid compile time warning.
open(isNewCase, progressIndicator);
} catch (CaseActionException ex) {
releaseSharedCaseDirLock(getMetadata().getCaseDirectory());
@@ -2414,7 +2449,7 @@ public class Case {
* @throws CaseActionException with a user-friendly message if the lock
* cannot be acquired.
*/
- @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory."})
+ @Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"})
private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException {
try {
caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java
index a095341c37..fed329769e 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java
@@ -21,8 +21,6 @@ package org.sleuthkit.autopsy.casemodule;
import java.io.File;
import java.util.Calendar;
import java.util.List;
-import java.util.SimpleTimeZone;
-import java.util.TimeZone;
import java.util.logging.Level;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
@@ -39,6 +37,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PathValidator;
+import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
/**
* Panel for adding an image file such as .img, .E0x, .00x, etc. Allows the user
@@ -72,11 +71,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
initComponents();
// Populate the drop down list of time zones
- for (String id : SimpleTimeZone.getAvailableIDs()) {
- timeZoneComboBox.addItem(timeZoneToString(TimeZone.getTimeZone(id)));
- }
- // set the selected timezone to the current timezone
- timeZoneComboBox.setSelectedItem(timeZoneToString(Calendar.getInstance().getTimeZone()));
+ createTimeZoneList();
// Populate the drop down list of sector size options
for (String choice : SECTOR_SIZE_CHOICES) {
@@ -95,6 +90,20 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
}
}
+ /**
+ * Creates the drop down list for the time zones and defaults the selection
+ * to the local machine time zone.
+ */
+ private void createTimeZoneList() {
+ List timeZoneList = TimeZoneUtils.createTimeZoneList();
+ for (String timeZone : timeZoneList) {
+ timeZoneComboBox.addItem(timeZone);
+ }
+
+ // set the selected timezone
+ timeZoneComboBox.setSelectedItem(TimeZoneUtils.createTimeZoneString(Calendar.getInstance().getTimeZone()));
+ }
+
/**
* Creates and returns an instance of a ImageFilePanel.
*
@@ -348,21 +357,6 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
}
}
- /**
- * Get a string representation of a TimeZone for use in the drop down list.
- *
- * @param zone The TimeZone to make a string for
- *
- * @return A string representation of a TimeZone for use in the drop down
- * list.
- */
- static private String timeZoneToString(TimeZone zone) {
- int offset = zone.getRawOffset() / 1000;
- int hour = offset / 3600;
- int minutes = (offset % 3600) / 60;
- return String.format("(GMT%+d:%02d) %s", hour, minutes, zone.getID()); //NON-NLS
- }
-
@Override
public void insertUpdate(DocumentEvent e) {
updateHelper();
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java
index c5c8fb32fb..ba5e4fab92 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalDiskPanel.java
@@ -21,8 +21,7 @@ package org.sleuthkit.autopsy.casemodule;
import java.io.File;
import java.nio.file.Paths;
import java.util.Calendar;
-import java.util.SimpleTimeZone;
-import java.util.TimeZone;
+import java.util.List;
import java.util.logging.Level;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
@@ -32,6 +31,7 @@ import org.sleuthkit.autopsy.coreutils.LocalDisk;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
+import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
/**
@@ -469,36 +469,13 @@ final class LocalDiskPanel extends JPanel {
* to the local machine time zone.
*/
public void createTimeZoneList() {
- // load and add all timezone
- String[] ids = SimpleTimeZone.getAvailableIDs();
- for (String id : ids) {
- TimeZone zone = TimeZone.getTimeZone(id);
- int offset = zone.getRawOffset() / 1000;
- int hour = offset / 3600;
- int minutes = (offset % 3600) / 60;
- String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id); //NON-NLS
-
- /*
- * DateFormat dfm = new SimpleDateFormat("z");
- * dfm.setTimeZone(zone); boolean hasDaylight =
- * zone.useDaylightTime(); String first = dfm.format(new Date(2010,
- * 1, 1)); String second = dfm.format(new Date(2011, 6, 6)); int mid
- * = hour * -1; String result = first + Integer.toString(mid);
- * if(hasDaylight){ result = result + second; }
- * timeZoneComboBox.addItem(item + " (" + result + ")");
- */
- timeZoneComboBox.addItem(item);
+ List timeZoneList = TimeZoneUtils.createTimeZoneList();
+ for (String timeZone : timeZoneList) {
+ timeZoneComboBox.addItem(timeZone);
}
- // get the current timezone
- TimeZone thisTimeZone = Calendar.getInstance().getTimeZone();
- int thisOffset = thisTimeZone.getRawOffset() / 1000;
- int thisHour = thisOffset / 3600;
- int thisMinutes = (thisOffset % 3600) / 60;
- String formatted = String.format("(GMT%+d:%02d) %s", thisHour, thisMinutes, thisTimeZone.getID()); //NON-NLS
// set the selected timezone
- timeZoneComboBox.setSelectedItem(formatted);
-
+ timeZoneComboBox.setSelectedItem(TimeZoneUtils.createTimeZoneString(Calendar.getInstance().getTimeZone()));
}
/**
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java
index 8d6dcffdd9..4d5e3eac36 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java
@@ -369,7 +369,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
@Override
public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
List filePaths = Arrays.asList(new String[]{dataSourcePath.toString()});
- run(deviceId, deviceId, filePaths, progressMonitor, callBack);
+ run(deviceId, "", filePaths, progressMonitor, callBack);
}
/**
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java
index d919706be6..5a2521ef8e 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java
@@ -492,6 +492,7 @@ public class FileManager implements Closeable {
}
}
trans.commit();
+ trans = null;
/*
* Publish content added events for the added files and directories.
@@ -502,15 +503,14 @@ public class FileManager implements Closeable {
return dataSource;
- } catch (TskCoreException ex) {
+ } finally {
if (null != trans) {
try {
trans.rollback();
- } catch (TskCoreException ex2) {
- LOGGER.log(Level.SEVERE, String.format("Failed to rollback transaction after exception: %s", ex.getMessage()), ex2);
+ } catch (TskCoreException ex) {
+ LOGGER.log(Level.SEVERE, "Failed to rollback transaction after exception", ex);
}
}
- throw ex;
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java
index 68e5b4f8c2..836f47ea85 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java
@@ -2960,14 +2960,10 @@ abstract class AbstractSqlEamDb implements EamDb {
resultSet.getString("poc_phone"));
}
- CorrelationCase eamCase = new CorrelationCase(resultSet.getInt("case_id"), resultSet.getString("case_uid"), resultSet.getString("case_name"));
- eamCase.setOrg(eamOrg);
- eamCase.setCreationDate(resultSet.getString("creation_date"));
- eamCase.setCaseNumber(resultSet.getString("case_number"));
- eamCase.setExaminerName(resultSet.getString("examiner_name"));
- eamCase.setExaminerEmail(resultSet.getString("examiner_email"));
- eamCase.setExaminerPhone(resultSet.getString("examiner_phone"));
- eamCase.setNotes(resultSet.getString("notes"));
+ CorrelationCase eamCase = new CorrelationCase(resultSet.getInt("case_id"), resultSet.getString("case_uid"), eamOrg, resultSet.getString("case_name"),
+ resultSet.getString("creation_date"), resultSet.getString("case_number"), resultSet.getString("examiner_name"),
+ resultSet.getString("examiner_email"), resultSet.getString("examiner_phone"), resultSet.getString("notes"));
+
return eamCase;
}
@@ -3017,7 +3013,6 @@ abstract class AbstractSqlEamDb implements EamDb {
if (null == resultSet) {
return null;
}
- // @@@ We should have data source ID in the previous query instead of passing -1 into the below constructor
return new CorrelationAttributeInstance(
aType,
resultSet.getString("value"),
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties
index b45b6f7579..0fc1951593 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties
@@ -66,7 +66,7 @@ GlobalSettingsPanel.lbCentralRepository.text=A central repository allows you to
GlobalSettingsPanel.pnCorrelationProperties.border.title=Correlation Properties
GlobalSettingsPanel.organizationPanel.border.title=Organizations
GlobalSettingsPanel.casesPanel.border.title=Case Details
-GlobalSettingsPanel.showCasesButton.text=Show Cases
+GlobalSettingsPanel.showCasesButton.text=Manage Cases
ShowCasesDialog.closeButton.AccessibleContext.accessibleName=Close
ShowCasesDialog.closeButton.actionCommand=Close
ShowCasesDialog.closeButton.text=Close
@@ -76,3 +76,12 @@ GlobalSettingsPanel.Case\ Details.AccessibleContext.accessibleName=Cases Details
ShowCasesDialog.caseDetailsTable.AccessibleContext.accessibleDescription=Click column name to sort.
GlobalSettingsPanel.casesTextArea.text=Display table that lists central repository case details.
GlobalSettingsPanel.ingestRunningWarningLabel.text=Cannot make changes to central repository settings when ingest is running!
+ManageCasesDialog.examinerPhoneLabel.text=Examiner Phone:
+ManageCasesDialog.examinerNameLabel.text=Examiner Name:
+ManageCasesDialog.examinerEmailLabel.text=Examiner Email:
+ManageCasesDialog.caseNumberLabel.text=Case Number:
+ManageCasesDialog.orgLabel.text=Organization:
+ManageCasesDialog.closeButton.text=Close
+ManageCasesDialog.notesLabel.text=Notes:
+ManageCasesDialog.dataSourcesLabel.text=Data Sources:
+ManageCasesDialog.caseInfoLabel.text=Case Info:
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/CaseDataSourcesWrapper.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/CaseDataSourcesWrapper.java
new file mode 100644
index 0000000000..34695fc354
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/CaseDataSourcesWrapper.java
@@ -0,0 +1,131 @@
+/*
+ * Central Repository
+ *
+ * Copyright 2018 Basis Technology Corp.
+ * Contact: carrier sleuthkit 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.centralrepository.optionspanel;
+
+import java.util.Collections;
+import java.util.List;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
+import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization;
+
+/**
+ * An object to contain both a CorrelationCase and the list of
+ * CorrelationDataSources which are associated with that case.
+ */
+class CaseDataSourcesWrapper {
+
+ private final CorrelationCase eamCase;
+ private final List dataSources;
+
+ /**
+ * Create a new CaseDataSourcesWrapper object.
+ *
+ * @param correlationCase - the CorrelationCase which is being represented
+ * @param dataSourceList - the list of CorrelationDataSource objects which
+ * are associated with the CorrelationCase
+ */
+ CaseDataSourcesWrapper(CorrelationCase correlationCase, List dataSourceList) {
+ eamCase = correlationCase;
+ dataSources = dataSourceList;
+ }
+
+ /**
+ * Get the display name of the CorrelationCase.
+ *
+ * @return the display name of the CorrelationCase.
+ */
+ String getDisplayName() {
+ return eamCase.getDisplayName();
+ }
+
+ /**
+ * Get the list of CorrelationDataSources associated with the
+ * CorrelationCase.
+ *
+ * @return the list of CorrelationDataSources associated with the
+ * CorrelationCase.
+ */
+ List getDataSources() {
+ return Collections.unmodifiableList(dataSources);
+ }
+
+ /**
+ * Get the creation date of the CorrelationCase.
+ *
+ * @return the creation date of the CorrelationCase.
+ */
+ String getCreationDate() {
+ return eamCase.getCreationDate();
+ }
+
+ /**
+ * Get the organization name of the CorrelationCase.
+ *
+ * @return the organization name of the CorrelationCase.
+ */
+ String getOrganizationName() {
+ EamOrganization org = eamCase.getOrg();
+ return org == null ? "" : org.getName();
+ }
+
+ /**
+ * Get the case number of the CorrelationCase.
+ *
+ * @return the case number of the CorrelationCase.
+ */
+ String getCaseNumber() {
+ return eamCase.getCaseNumber();
+ }
+
+ /**
+ * Get the examiner name of the CorrelationCase.
+ *
+ * @return the examiner name of the CorrelationCase.
+ */
+ String getExaminerName() {
+ return eamCase.getExaminerName();
+ }
+
+ /**
+ * Get the examiner email of the CorrelationCase.
+ *
+ * @return the examiner email of the CorrelationCase.
+ */
+ String getExaminerEmail() {
+ return eamCase.getExaminerEmail();
+ }
+
+ /**
+ * Get the notes of the CorrelationCase.
+ *
+ * @return the notes of the CorrelationCase.
+ */
+ String getNotes() {
+ return eamCase.getNotes();
+ }
+
+ /**
+ * Get the examiner phone number of the CorrelationCase.
+ *
+ * @return the examiner phone number of the CorrelationCase.
+ */
+ String getExaminerPhone() {
+ return eamCase.getExaminerPhone();
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/CasesTableModel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/CasesTableModel.java
new file mode 100644
index 0000000000..5421caef70
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/CasesTableModel.java
@@ -0,0 +1,156 @@
+/*
+ * Central Repository
+ *
+ * Copyright 2018 Basis Technology Corp.
+ * Contact: carrier sleuthkit 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.centralrepository.optionspanel;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.table.AbstractTableModel;
+import org.openide.util.NbBundle.Messages;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
+
+/**
+ * Model for cells to display correlation case information
+ */
+class CasesTableModel extends AbstractTableModel {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * list of Eam Cases from central repository.
+ */
+ private final List eamCases;
+
+ /**
+ * Model for cells to display correlation case information
+ */
+ CasesTableModel() {
+ eamCases = new ArrayList<>();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return CaseTableColumns.values().length;
+ }
+
+ @Override
+ public int getRowCount() {
+ return eamCases.size();
+ }
+
+ @Override
+ public String getColumnName(int colIdx) {
+ return CaseTableColumns.values()[colIdx].columnName();
+ }
+
+ @Override
+ public Object getValueAt(int rowIdx, int colIdx) {
+ if (eamCases.isEmpty()) {
+ return Bundle.CasesTableModel_noData();
+ }
+
+ return mapValueById(rowIdx, CaseTableColumns.values()[colIdx]);
+ }
+
+ /**
+ * Map a rowIdx and colId to the value in that cell.
+ *
+ * @param rowIdx Index of row to search
+ * @param colId ID of column to search
+ *
+ * @return value in the cell
+ */
+ @Messages({"CasesTableModel.noData=No Cases"})
+ private Object mapValueById(int rowIdx, CaseTableColumns colId) {
+ CaseDataSourcesWrapper eamCase = eamCases.get(rowIdx);
+ String value = Bundle.CasesTableModel_noData();
+
+ switch (colId) {
+ case CASE_NAME:
+ value = eamCase.getDisplayName();
+ break;
+ case CREATION_DATE:
+ value = eamCase.getCreationDate();
+ break;
+ default:
+ break;
+ }
+ return value;
+ }
+
+ @Override
+ public Class getColumnClass(int colIdx) {
+ return String.class;
+ }
+
+ /**
+ * Add one local central repository case to the table.
+ *
+ * @param eamCase central repository case to add to the table
+ */
+ void addEamCase(CorrelationCase eamCase, List dataSourceList) {
+ eamCases.add(new CaseDataSourcesWrapper(eamCase, dataSourceList));
+ fireTableDataChanged();
+ }
+
+ /**
+ * Get the CaseDataSourcesWrapper for the specified index in the table.
+ *
+ * @param listIndex the idex of the object to get
+ *
+ * @return A caseDataSourcesWrapper containing the CorrelationCase and the
+ * CorrelationDataSources associated with it for the specified
+ * index.
+ */
+ CaseDataSourcesWrapper getEamCase(int listIndex) {
+ return eamCases.get(listIndex);
+ }
+
+ /**
+ * Enum which lists columns of interest from CorrelationCase.
+ */
+ @Messages({"CasesTableModel.case=Case Name",
+ "CasesTableModel.creationDate=Creation Date"})
+ private enum CaseTableColumns {
+ // Ordering here determines displayed column order in Content Viewer.
+ // If order is changed, update the CellRenderer to ensure correct row coloring.
+ CASE_NAME(Bundle.CasesTableModel_case()),
+ CREATION_DATE(Bundle.CasesTableModel_creationDate());
+
+ private final String columnName;
+
+ /**
+ * Make a DataSourceTableColumns enum item.
+ *
+ * @param columnName the name of the column.s
+ */
+ CaseTableColumns(String columnName) {
+ this.columnName = columnName;
+ }
+
+ /**
+ * The name displayed in the column header.
+ *
+ * @return the name of the column.
+ */
+ String columnName() {
+ return columnName;
+ }
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/DataSourcesTableModel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/DataSourcesTableModel.java
new file mode 100644
index 0000000000..53d917fadf
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/DataSourcesTableModel.java
@@ -0,0 +1,152 @@
+/*
+ * Central Repository
+ *
+ * Copyright 2018 Basis Technology Corp.
+ * Contact: carrier sleuthkit 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.centralrepository.optionspanel;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.table.AbstractTableModel;
+import org.openide.util.NbBundle.Messages;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
+
+/**
+ * Model for cells to display correlation data source information
+ */
+class DataSourcesTableModel extends AbstractTableModel {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * A list of correlation data sources from central repository.
+ */
+ private final List dataSources;
+
+ /**
+ * Create a new DataSourcesTableModel, with an initially empty list of data
+ * sources.
+ */
+ DataSourcesTableModel() {
+ dataSources = new ArrayList<>();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return DataSourcesTableColumns.values().length;
+ }
+
+ @Override
+ public int getRowCount() {
+ return dataSources.size();
+ }
+
+ @Override
+ public String getColumnName(int colIdx) {
+ return DataSourcesTableColumns.values()[colIdx].columnName();
+ }
+
+ @Override
+ public Object getValueAt(int rowIdx, int colIdx) {
+ if (dataSources.isEmpty()) {
+ return Bundle.DataSourcesTableModel_noData();
+ }
+
+ return mapValueById(rowIdx, DataSourcesTableColumns.values()[colIdx]);
+ }
+
+ /**
+ * Map a rowIdx and colId to the value in that cell.
+ *
+ * @param rowIdx Index of row to search
+ * @param colId ID of column to search
+ *
+ * @return value in the cell
+ */
+ @Messages({"DataSourcesTableModel.noData=No Data Sources"})
+ private Object mapValueById(int rowIdx, DataSourcesTableColumns colId) {
+ CorrelationDataSource dataSource = dataSources.get(rowIdx);
+ String value = Bundle.DataSourcesTableModel_noData();
+
+ switch (colId) {
+ case DATA_SOURCE:
+ value = dataSource.getName();
+ break;
+ case DEVICE_ID:
+ value = dataSource.getDeviceID();
+ break;
+ default:
+ break;
+ }
+ return value;
+ }
+
+ @Override
+ public Class getColumnClass(int colIdx) {
+ return String.class;
+ }
+
+ /**
+ * Add a list of datasources to the table.
+ *
+ * @param dataSourceList the list of datasources to add to the table
+ */
+ void addDataSources(List dataSourceList) {
+ dataSources.addAll(dataSourceList);
+ fireTableDataChanged();
+ }
+
+ /**
+ * Clear the data sources currently included in the model.
+ */
+ void clearTable() {
+ dataSources.clear();
+ fireTableDataChanged();
+ }
+
+ @Messages({"DataSourcesTableModel.dataSource=Data Source Name",
+ "DataSourcesTableModel.deviceId=Device ID"})
+ /**
+ * Enum which lists columns of interest from CorrelationDataSource.
+ */
+ private enum DataSourcesTableColumns {
+ // Ordering here determines displayed column order in Content Viewer.
+ // If order is changed, update the CellRenderer to ensure correct row coloring.
+ DATA_SOURCE(Bundle.DataSourcesTableModel_dataSource()),
+ DEVICE_ID(Bundle.DataSourcesTableModel_deviceId());
+
+ private final String columnName;
+
+ /**
+ * Make a CasesTableColumns enum item.
+ *
+ * @param columnName the name of the column.
+ */
+ DataSourcesTableColumns(String columnName) {
+ this.columnName = columnName;
+ }
+
+ /**
+ * The name displayed in the column header.
+ *
+ * @return the name of the column.
+ */
+ String columnName() {
+ return columnName;
+ }
+ }
+
+}
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java
index b8c42eb66d..e24fe70ea8 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java
@@ -441,7 +441,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
private void showCasesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showCasesButtonActionPerformed
store();
- ShowCasesDialog showCasesDialog = new ShowCasesDialog();
+ ManageCasesDialog.displayManageCasesDialog();
}//GEN-LAST:event_showCasesButtonActionPerformed
@Override
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCasesDialog.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCasesDialog.form
new file mode 100644
index 0000000000..9d1b82d31a
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCasesDialog.form
@@ -0,0 +1,358 @@
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCasesDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCasesDialog.java
new file mode 100644
index 0000000000..f782af67c5
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ManageCasesDialog.java
@@ -0,0 +1,353 @@
+/*
+ * Central Repository
+ *
+ * Copyright 2018 Basis Technology Corp.
+ * Contact: carrier sleuthkit 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.centralrepository.optionspanel;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import org.openide.windows.WindowManager;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
+import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
+import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.openide.util.NbBundle.Messages;
+
+/**
+ * A dialog which displays cases existing in the central repository and the
+ * central repo information associated with them.
+ *
+ */
+final class ManageCasesDialog extends javax.swing.JDialog {
+
+ private static final long serialVersionUID = 1L;
+ private final CasesTableModel casesTableModel = new CasesTableModel();
+ private final DataSourcesTableModel dataSourcesTableModel = new DataSourcesTableModel();
+ private final static Logger logger = Logger.getLogger(ManageCasesDialog.class.getName());
+
+ /**
+ * Creates new form ManageCasesDialog
+ */
+ @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
+ @Messages({"ManageCasesDialog.title.text=Manage Cases"})
+ private ManageCasesDialog() {
+ super(WindowManager.getDefault().getMainWindow(), Bundle.ManageCasesDialog_title_text(),
+ true);
+ initComponents();
+ try {
+ EamDb dbManager = EamDb.getInstance();
+ Map> dataSourcesByCaseId = new HashMap<>();
+ for (CorrelationDataSource dataSource : dbManager.getDataSources()) {
+ int caseID = dataSource.getCaseID();
+ List dataSourceNames = dataSourcesByCaseId.getOrDefault(caseID, new ArrayList<>());
+ dataSourceNames.add(dataSource);
+ dataSourcesByCaseId.put(caseID, dataSourceNames);
+ }
+ for (CorrelationCase eamCase : dbManager.getCases()) {
+ casesTableModel.addEamCase(eamCase, dataSourcesByCaseId.getOrDefault(eamCase.getID(), new ArrayList<>()));
+ }
+ } catch (EamDbException ex) {
+ logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS
+ }
+
+ casesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ if (!e.getValueIsAdjusting()) {
+ updateSelection();
+ }
+ }
+ });
+ //sort on first column by default
+ casesTable.getRowSorter().toggleSortOrder(0);
+ }
+
+ /**
+ * Create and display the Manage Cases dialog for the currently enabled
+ * central repository.
+ */
+ static void displayManageCasesDialog() {
+ ManageCasesDialog caseInfoDialog = new ManageCasesDialog();
+ caseInfoDialog.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
+ caseInfoDialog.setVisible(true);
+ }
+
+ /**
+ * 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")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ casesSplitPane = new javax.swing.JSplitPane();
+ caseInfoPanel = new javax.swing.JPanel();
+ dataSourcesScrollPane = new javax.swing.JScrollPane();
+ dataSourcesTable = new javax.swing.JTable();
+ notesScrollPane = new javax.swing.JScrollPane();
+ notesTextArea = new javax.swing.JTextArea();
+ caseInfoLabel = new javax.swing.JLabel();
+ dataSourcesLabel = new javax.swing.JLabel();
+ notesLabel = new javax.swing.JLabel();
+ orgLabel = new javax.swing.JLabel();
+ caseNumberLabel = new javax.swing.JLabel();
+ examinerEmailLabel = new javax.swing.JLabel();
+ examinerNameLabel = new javax.swing.JLabel();
+ examinerPhoneLabel = new javax.swing.JLabel();
+ orgValueLabel = new javax.swing.JLabel();
+ caseNumberValueLabel = new javax.swing.JLabel();
+ examinerNameValueLabel = new javax.swing.JLabel();
+ examinerEmailValueLabel = new javax.swing.JLabel();
+ examinerPhoneValueLabel = new javax.swing.JLabel();
+ closeButton = new javax.swing.JButton();
+ casesPanel = new javax.swing.JPanel();
+ casesScrollPane = new javax.swing.JScrollPane();
+ casesTable = new javax.swing.JTable();
+
+ setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+ setMinimumSize(new java.awt.Dimension(400, 400));
+
+ casesSplitPane.setDividerLocation(380);
+
+ dataSourcesTable.setAutoCreateRowSorter(true);
+ dataSourcesTable.setModel(dataSourcesTableModel);
+ dataSourcesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
+ dataSourcesScrollPane.setViewportView(dataSourcesTable);
+
+ notesScrollPane.setBorder(null);
+
+ notesTextArea.setEditable(false);
+ notesTextArea.setBackground(new java.awt.Color(240, 240, 240));
+ notesTextArea.setColumns(20);
+ notesTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N
+ notesTextArea.setLineWrap(true);
+ notesTextArea.setRows(3);
+ notesTextArea.setWrapStyleWord(true);
+ notesTextArea.setBorder(null);
+ notesScrollPane.setViewportView(notesTextArea);
+
+ org.openide.awt.Mnemonics.setLocalizedText(caseInfoLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.caseInfoLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(dataSourcesLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.dataSourcesLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(notesLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.notesLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(orgLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.orgLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(caseNumberLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.caseNumberLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(examinerEmailLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.examinerEmailLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(examinerNameLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.examinerNameLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(examinerPhoneLabel, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.examinerPhoneLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(ManageCasesDialog.class, "ManageCasesDialog.closeButton.text")); // NOI18N
+ closeButton.setMaximumSize(new java.awt.Dimension(65, 23));
+ closeButton.setMinimumSize(new java.awt.Dimension(65, 23));
+ closeButton.setPreferredSize(new java.awt.Dimension(65, 23));
+ closeButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ closeButtonActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout caseInfoPanelLayout = new javax.swing.GroupLayout(caseInfoPanel);
+ caseInfoPanel.setLayout(caseInfoPanelLayout);
+ caseInfoPanelLayout.setHorizontalGroup(
+ caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(caseInfoPanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(caseInfoPanelLayout.createSequentialGroup()
+ .addGap(10, 10, 10)
+ .addComponent(dataSourcesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
+ .addGroup(caseInfoPanelLayout.createSequentialGroup()
+ .addGap(6, 6, 6)
+ .addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(caseInfoPanelLayout.createSequentialGroup()
+ .addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(orgLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(caseNumberLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(examinerNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(examinerEmailLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(examinerPhoneLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(caseInfoPanelLayout.createSequentialGroup()
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(caseNumberValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(orgValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+ .addGroup(caseInfoPanelLayout.createSequentialGroup()
+ .addGap(6, 6, 6)
+ .addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(examinerNameValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(examinerEmailValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(examinerPhoneValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))))
+ .addComponent(notesLabel)
+ .addGroup(caseInfoPanelLayout.createSequentialGroup()
+ .addGap(10, 10, 10)
+ .addComponent(notesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 428, Short.MAX_VALUE))))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, caseInfoPanelLayout.createSequentialGroup()
+ .addGap(0, 0, Short.MAX_VALUE)
+ .addComponent(closeButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(caseInfoPanelLayout.createSequentialGroup()
+ .addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(caseInfoLabel)
+ .addComponent(dataSourcesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(0, 0, Short.MAX_VALUE)))
+ .addContainerGap())
+ );
+ caseInfoPanelLayout.setVerticalGroup(
+ caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, caseInfoPanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(caseInfoLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(caseInfoPanelLayout.createSequentialGroup()
+ .addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(caseInfoPanelLayout.createSequentialGroup()
+ .addComponent(orgLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(caseNumberLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(caseNumberValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(examinerNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(examinerNameValueLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+ .addComponent(orgValueLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(examinerEmailLabel))
+ .addComponent(examinerEmailValueLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(caseInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(examinerPhoneLabel)
+ .addComponent(examinerPhoneValueLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(notesLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(notesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 55, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(dataSourcesLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(dataSourcesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 129, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(closeButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
+ );
+
+ casesSplitPane.setRightComponent(caseInfoPanel);
+
+ casesTable.setAutoCreateRowSorter(true);
+ casesTable.setModel(casesTableModel);
+ casesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
+ casesScrollPane.setViewportView(casesTable);
+
+ javax.swing.GroupLayout casesPanelLayout = new javax.swing.GroupLayout(casesPanel);
+ casesPanel.setLayout(casesPanelLayout);
+ casesPanelLayout.setHorizontalGroup(
+ casesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(casesPanelLayout.createSequentialGroup()
+ .addComponent(casesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 379, Short.MAX_VALUE)
+ .addGap(0, 0, 0))
+ );
+ casesPanelLayout.setVerticalGroup(
+ casesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(casesPanelLayout.createSequentialGroup()
+ .addComponent(casesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 361, Short.MAX_VALUE)
+ .addGap(40, 40, 40))
+ );
+
+ casesSplitPane.setLeftComponent(casesPanel);
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(casesSplitPane, javax.swing.GroupLayout.Alignment.TRAILING)
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(casesSplitPane)
+ );
+
+ pack();
+ }// //GEN-END:initComponents
+
+ private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
+ this.dispose();
+ }//GEN-LAST:event_closeButtonActionPerformed
+
+ /**
+ * Update the information displayed to reflect the currently selected case.
+ */
+ private void updateSelection() {
+ dataSourcesTableModel.clearTable();
+ if (casesTable.getSelectedRow() >= 0 && casesTable.getSelectedRow() < casesTable.getRowCount()) {
+ CaseDataSourcesWrapper caseWrapper = casesTableModel.getEamCase(casesTable.convertRowIndexToModel(casesTable.getSelectedRow()));
+ orgValueLabel.setText(caseWrapper.getOrganizationName());
+ caseNumberValueLabel.setText(caseWrapper.getCaseNumber());
+ examinerNameValueLabel.setText(caseWrapper.getExaminerName());
+ examinerPhoneValueLabel.setText(caseWrapper.getExaminerPhone());
+ examinerEmailValueLabel.setText(caseWrapper.getExaminerEmail());
+ notesTextArea.setText(caseWrapper.getNotes());
+ dataSourcesTableModel.addDataSources(caseWrapper.getDataSources());
+ } else {
+ orgValueLabel.setText("");
+ caseNumberValueLabel.setText("");
+ examinerNameValueLabel.setText("");
+ examinerPhoneValueLabel.setText("");
+ examinerEmailValueLabel.setText("");
+ notesTextArea.setText("");
+ }
+ }
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JLabel caseInfoLabel;
+ private javax.swing.JPanel caseInfoPanel;
+ private javax.swing.JLabel caseNumberLabel;
+ private javax.swing.JLabel caseNumberValueLabel;
+ private javax.swing.JPanel casesPanel;
+ private javax.swing.JScrollPane casesScrollPane;
+ private javax.swing.JSplitPane casesSplitPane;
+ private javax.swing.JTable casesTable;
+ private javax.swing.JButton closeButton;
+ private javax.swing.JLabel dataSourcesLabel;
+ private javax.swing.JScrollPane dataSourcesScrollPane;
+ private javax.swing.JTable dataSourcesTable;
+ private javax.swing.JLabel examinerEmailLabel;
+ private javax.swing.JLabel examinerEmailValueLabel;
+ private javax.swing.JLabel examinerNameLabel;
+ private javax.swing.JLabel examinerNameValueLabel;
+ private javax.swing.JLabel examinerPhoneLabel;
+ private javax.swing.JLabel examinerPhoneValueLabel;
+ private javax.swing.JLabel notesLabel;
+ private javax.swing.JScrollPane notesScrollPane;
+ private javax.swing.JTextArea notesTextArea;
+ private javax.swing.JLabel orgLabel;
+ private javax.swing.JLabel orgValueLabel;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ShowCasesDialog.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ShowCasesDialog.form
deleted file mode 100644
index c6a18ff739..0000000000
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ShowCasesDialog.form
+++ /dev/null
@@ -1,169 +0,0 @@
-
-
-
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ShowCasesDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ShowCasesDialog.java
deleted file mode 100644
index 6432c5376f..0000000000
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ShowCasesDialog.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Central Repository
- *
- * Copyright 2015-2018 Basis Technology Corp.
- * Contact: carrier sleuthkit 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.centralrepository.optionspanel;
-
-import java.util.List;
-import java.util.logging.Level;
-import javax.swing.JDialog;
-import javax.swing.JFrame;
-import org.openide.util.NbBundle.Messages;
-import org.openide.windows.WindowManager;
-import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
-import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
-import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
-import org.sleuthkit.autopsy.coreutils.Logger;
-
-/**
- * Dialog to display table of CorrelationCase information from the CR tab of options.
- */
-@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
-final class ShowCasesDialog extends JDialog {
-
- private static final long serialVersionUID = 1L;
-
- private final static Logger logger = Logger.getLogger(ShowCasesDialog.class.getName());
-
- private final ShowCasesTableModel tableModel;
- @Messages({"ShowCasesDialog.title_text=All Cases Details"})
- /**
- * Creates new form ShowCases Panel
- */
- ShowCasesDialog() {
- super((JFrame) WindowManager.getDefault().getMainWindow(),
- Bundle.ShowCasesDialog_title_text(),
- true);
- tableModel = new ShowCasesTableModel();
- initComponents();
- try {
- EamDb dbManager = EamDb.getInstance();
- List eamCases = dbManager.getCases();
- for(CorrelationCase eamCase : eamCases) {
- tableModel.addEamCase(eamCase);
- }
- } catch (EamDbException ex) {
- logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS
- }
- display();
- }
-
- private void display() {
- this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
- setVisible(true);
- }
-
-
-
- /**
- * 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")
- // //GEN-BEGIN:initComponents
- private void initComponents() {
-
- showCasesPanel = new javax.swing.JPanel();
- showCasesScrollPane = new javax.swing.JScrollPane();
- outCasesPane = new javax.swing.JPanel();
- innerCaseScrollPane = new javax.swing.JScrollPane();
- caseDetailsTable = new javax.swing.JTable();
- closeButton = new javax.swing.JButton();
-
- setTitle(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.title")); // NOI18N
- setMinimumSize(new java.awt.Dimension(545, 415));
-
- showCasesPanel.setPreferredSize(new java.awt.Dimension(527, 407));
-
- showCasesScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
- showCasesScrollPane.setPreferredSize(new java.awt.Dimension(535, 415));
-
- innerCaseScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
-
- caseDetailsTable.setAutoCreateRowSorter(true);
- caseDetailsTable.setModel(tableModel);
- caseDetailsTable.setToolTipText(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.caseDetailsTable.toolTipText")); // NOI18N
- caseDetailsTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
- caseDetailsTable.getTableHeader().setReorderingAllowed(false);
- innerCaseScrollPane.setViewportView(caseDetailsTable);
- caseDetailsTable.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.caseDetailsTable.AccessibleContext.accessibleDescription")); // NOI18N
-
- javax.swing.GroupLayout outCasesPaneLayout = new javax.swing.GroupLayout(outCasesPane);
- outCasesPane.setLayout(outCasesPaneLayout);
- outCasesPaneLayout.setHorizontalGroup(
- outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGap(0, 1423, Short.MAX_VALUE)
- .addGroup(outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(innerCaseScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1423, Short.MAX_VALUE))
- );
- outCasesPaneLayout.setVerticalGroup(
- outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGap(0, 500, Short.MAX_VALUE)
- .addGroup(outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(innerCaseScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE))
- );
-
- showCasesScrollPane.setViewportView(outCasesPane);
-
- javax.swing.GroupLayout showCasesPanelLayout = new javax.swing.GroupLayout(showCasesPanel);
- showCasesPanel.setLayout(showCasesPanelLayout);
- showCasesPanelLayout.setHorizontalGroup(
- showCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGap(0, 1188, Short.MAX_VALUE)
- .addGroup(showCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(showCasesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 527, Short.MAX_VALUE))
- );
- showCasesPanelLayout.setVerticalGroup(
- showCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGap(0, 473, Short.MAX_VALUE)
- .addGroup(showCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(showCasesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 407, Short.MAX_VALUE))
- );
-
- org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.closeButton.text")); // NOI18N
- closeButton.setActionCommand(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.closeButton.actionCommand")); // NOI18N
- closeButton.addActionListener(new java.awt.event.ActionListener() {
- public void actionPerformed(java.awt.event.ActionEvent evt) {
- closeButtonActionPerformed(evt);
- }
- });
-
- javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
- getContentPane().setLayout(layout);
- layout.setHorizontalGroup(
- layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createSequentialGroup()
- .addGap(6, 6, 6)
- .addComponent(showCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 1188, Short.MAX_VALUE)
- .addGap(6, 6, 6))
- .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
- .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
- .addComponent(closeButton)
- .addContainerGap())
- );
- layout.setVerticalGroup(
- layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(layout.createSequentialGroup()
- .addGap(6, 6, 6)
- .addComponent(showCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 473, Short.MAX_VALUE)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(closeButton)
- .addContainerGap())
- );
-
- closeButton.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.closeButton.AccessibleContext.accessibleName")); // NOI18N
-
- pack();
- }// //GEN-END:initComponents
-
- private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
- dispose();
- }//GEN-LAST:event_closeButtonActionPerformed
-
-
- // Variables declaration - do not modify//GEN-BEGIN:variables
- private javax.swing.JTable caseDetailsTable;
- private javax.swing.JButton closeButton;
- private javax.swing.JScrollPane innerCaseScrollPane;
- private javax.swing.JPanel outCasesPane;
- private javax.swing.JPanel showCasesPanel;
- private javax.swing.JScrollPane showCasesScrollPane;
- // End of variables declaration//GEN-END:variables
-
-
-
-}
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ShowCasesTableModel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ShowCasesTableModel.java
deleted file mode 100644
index def6cb15b6..0000000000
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ShowCasesTableModel.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Central Repository
- *
- * Copyright 2018 Basis Technology Corp.
- * Contact: carrier sleuthkit 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.centralrepository.optionspanel;
-
-import java.util.ArrayList;
-import java.util.List;
-import javax.swing.table.AbstractTableModel;
-import org.openide.util.NbBundle.Messages;
-import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
-
-/**
- * Model for cells to display correlation case information
- */
-class ShowCasesTableModel extends AbstractTableModel {
-
- private static final long serialVersionUID = 1L;
-
- @Messages({"ShowCasesTableModel.case=Case Name",
- "ShowCasesTableModel.creationDate=Creation Date",
- "ShowCasesTableModel.caseNumber=Case Number",
- "ShowCasesTableModel.examinerName=Examiner Name",
- "ShowCasesTableModel.examinerEmail=Examiner Email",
- "ShowCasesTableModel.examinerPhone=Examiner Phone",
- "ShowCasesTableModel.notes=Notes",
- "ShowCasesTableModel.noData=No Cases"})
- /**
- * Enum which lists columns of interest from CorrelationCase.
- */
- enum TableColumns {
- // Ordering here determines displayed column order in Content Viewer.
- // If order is changed, update the CellRenderer to ensure correct row coloring.
- CASE_NAME(Bundle.ShowCasesTableModel_case(), 200),
- CREATION_DATE(Bundle.ShowCasesTableModel_creationDate(), 150),
- CASE_NUMBER(Bundle.ShowCasesTableModel_caseNumber(), 100),
- EXAMINER_NAME(Bundle.ShowCasesTableModel_examinerName(), 200),
- EXAMINER_EMAIL(Bundle.ShowCasesTableModel_examinerEmail(), 100),
- EXAMINER_PHONE(Bundle.ShowCasesTableModel_examinerPhone(), 100),
- NOTES(Bundle.ShowCasesTableModel_notes(), 450);
-
- private final String columnName;
- private final int columnWidth;
-
- TableColumns(String columnName, int columnWidth) {
- this.columnName = columnName;
- this.columnWidth = columnWidth;
- }
-
- String columnName() {
- return columnName;
- }
-
- int columnWidth() {
- return columnWidth;
- }
- };
-
- /**
- * list of Eam Cases from central repository.
- */
- private List eamCases;
-
- ShowCasesTableModel() {
- eamCases = new ArrayList<>();
- }
-
- @Override
- public int getColumnCount() {
- return TableColumns.values().length;
- }
-
- /**
- * Get the preferred width that has been configured for this column.
- *
- * A value of 0 means that no preferred width has been defined for this
- * column.
- *
- * @param colIdx Column index
- *
- * @return preferred column width >= 0
- */
- int getColumnPreferredWidth(int colIdx) {
- return TableColumns.values()[colIdx].columnWidth();
- }
-
- @Override
- public int getRowCount() {
- return eamCases.size();
- }
-
- @Override
- public String getColumnName(int colIdx) {
- return TableColumns.values()[colIdx].columnName();
- }
-
- @Override
- public Object getValueAt(int rowIdx, int colIdx) {
- if (eamCases.isEmpty()) {
- return Bundle.ShowCasesTableModel_noData();
- }
-
- return mapValueById(rowIdx, TableColumns.values()[colIdx]);
- }
-
- Object getRow(int rowIdx) {
- return eamCases.get(rowIdx);
- }
-
- /**
- * Map a rowIdx and colId to the value in that cell.
- *
- * @param rowIdx Index of row to search
- * @param colId ID of column to search
- *
- * @return value in the cell
- */
- private Object mapValueById(int rowIdx, TableColumns colId) {
- CorrelationCase eamCase = eamCases.get(rowIdx);
- String value = Bundle.ShowCasesTableModel_noData();
-
- switch (colId) {
- case CASE_NAME:
- value = eamCase.getDisplayName();
- break;
- case CREATION_DATE:
- value = eamCase.getCreationDate();
- break;
- case CASE_NUMBER:
- value = eamCase.getCaseNumber();
- break;
- case EXAMINER_NAME:
- value = eamCase.getExaminerName();
- break;
- case EXAMINER_EMAIL:
- value = eamCase.getExaminerEmail();
- break;
- case EXAMINER_PHONE:
- value = eamCase.getExaminerPhone();
- break;
- case NOTES:
- value = eamCase.getNotes();
- break;
- default:
- break;
- }
- return value;
- }
-
- @Override
- public Class getColumnClass(int colIdx) {
- return String.class;
- }
-
- /**
- * Add one local central repository case to the table.
- *
- * @param eamCase central repository case to add to the
- * table
- */
- void addEamCase(CorrelationCase eamCase) {
- eamCases.add(eamCase);
- fireTableDataChanged();
- }
-
- void clearTable() {
- eamCases.clear();
- fireTableDataChanged();
- }
-
-
-
-}
diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form
index c19b57ea69..38a3e7adbf 100644
--- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form
@@ -303,23 +303,25 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java
index 81ffcb4805..587bf47260 100644
--- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java
@@ -124,7 +124,8 @@ final public class FiltersPanel extends JPanel {
updateFilters(true);
UserPreferences.addChangeListener(preferenceChangeEvent -> {
- if (preferenceChangeEvent.getKey().equals(UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME)) {
+ if (preferenceChangeEvent.getKey().equals(UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME) ||
+ preferenceChangeEvent.getKey().equals(UserPreferences.TIME_ZONE_FOR_DISPLAYS)) {
updateTimeZone();
}
});
@@ -175,7 +176,7 @@ final public class FiltersPanel extends JPanel {
}
private void updateTimeZone() {
- dateRangeLabel.setText("Date Range ( " + Utils.getUserPreferredZoneId().toString() + "):");
+ dateRangeLabel.setText("Date Range (" + Utils.getUserPreferredZoneId().toString() + "):");
}
/**
diff --git a/Core/src/org/sleuthkit/autopsy/communications/Utils.java b/Core/src/org/sleuthkit/autopsy/communications/Utils.java
index b155e3b4b4..c4a62209c7 100644
--- a/Core/src/org/sleuthkit/autopsy/communications/Utils.java
+++ b/Core/src/org/sleuthkit/autopsy/communications/Utils.java
@@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.communications;
import java.time.ZoneId;
import java.time.ZoneOffset;
+import java.util.TimeZone;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.datamodel.Account;
@@ -33,7 +34,8 @@ class Utils {
}
static ZoneId getUserPreferredZoneId() {
- ZoneId zone = UserPreferences.displayTimesInLocalTime() ? ZoneOffset.systemDefault() : ZoneOffset.UTC;
+ ZoneId zone = UserPreferences.displayTimesInLocalTime() ?
+ ZoneOffset.systemDefault() : TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()).toZoneId();
return zone;
}
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java
index 9c09ca01ca..f2be8c90c4 100755
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java
@@ -424,7 +424,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
- "SELECT count (*) as count FROM " + "\"" + tableName + "\"")) { //NON-NLS{
+ "SELECT count (*) as count FROM " + "\"" + tableName + "\"")) { //NON-NLS
numRows = resultSet.getInt("count");
numEntriesField.setText(numRows + " entries");
@@ -442,9 +442,23 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
} else {
exportCsvButton.setEnabled(false);
nextPageButton.setEnabled(false);
- selectedTableView.setupTable(Collections.emptyList());
- }
-
+
+ //Execute a dummy SELECT * statement so that the metadata
+ //contains all column names
+ Map columnRow;
+ try (ResultSet metaDataResultSet = statement.executeQuery(
+ "SELECT * FROM " + "\"" + tableName + "\"")) {
+ //Column names are not found in the metadata of the result set
+ //above.
+ ResultSetMetaData metaData = metaDataResultSet.getMetaData();
+ columnRow = new LinkedHashMap<>();
+ for(int i = 1; i < metaData.getColumnCount(); i++){
+ columnRow.put(metaData.getColumnName(i), "");
+ }
+ }
+
+ selectedTableView.setupTable(Collections.singletonList(columnRow));
+ }
} catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to load table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName));
diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java
index cb5f797c2b..bd55626a1b 100644
--- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java
+++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java
@@ -24,6 +24,7 @@ import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
import java.util.prefs.PreferenceChangeListener;
import java.util.prefs.Preferences;
import org.openide.util.NbPreferences;
+import org.python.icu.util.TimeZone;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.TextConverterException;
@@ -45,6 +46,7 @@ public final class UserPreferences {
public static final String HIDE_SLACK_FILES_IN_DATA_SRCS_TREE = "HideSlackFilesInDataSourcesTree"; //NON-NLS
public static final String HIDE_SLACK_FILES_IN_VIEWS_TREE = "HideSlackFilesInViewsTree"; //NON-NLS
public static final String DISPLAY_TIMES_IN_LOCAL_TIME = "DisplayTimesInLocalTime"; //NON-NLS
+ public static final String TIME_ZONE_FOR_DISPLAYS = "TimeZoneForDisplays"; //NON-NLS
public static final String NUMBER_OF_FILE_INGEST_THREADS = "NumberOfFileIngestThreads"; //NON-NLS
public static final String IS_MULTI_USER_MODE_ENABLED = "IsMultiUserModeEnabled"; //NON-NLS
public static final String EXTERNAL_DATABASE_HOSTNAME_OR_IP = "ExternalDatabaseHostnameOrIp"; //NON-NLS
@@ -181,6 +183,14 @@ public final class UserPreferences {
public static void setDisplayTimesInLocalTime(boolean value) {
preferences.putBoolean(DISPLAY_TIMES_IN_LOCAL_TIME, value);
}
+
+ public static String getTimeZoneForDisplays() {
+ return preferences.get(TIME_ZONE_FOR_DISPLAYS, TimeZone.GMT_ZONE.getID());
+ }
+
+ public static void setTimeZoneForDisplays(String timeZone) {
+ preferences.put(TIME_ZONE_FOR_DISPLAYS, timeZone);
+ }
public static int numberOfFileIngestThreads() {
return preferences.getInt(NUMBER_OF_FILE_INGEST_THREADS, 2);
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties
index 12ba493380..97815e1748 100644
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties
@@ -180,7 +180,6 @@ ViewPreferencesPanel.useBestViewerRadioButton.text=Change to the most specific f
ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=For example, stay in Hex view when a JPEG is selected.
ViewPreferencesPanel.keepCurrentViewerRadioButton.text=Stay on the same file viewer
ViewPreferencesPanel.useLocalTimeRadioButton.text=Use local time zone
-ViewPreferencesPanel.useGMTTimeRadioButton.text=Use GMT
ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=Data Sources area (the directory hierarchy)
ViewPreferencesPanel.viewsHideKnownCheckbox.text=Views area
ViewPreferencesPanel.dataSourcesHideSlackCheckbox.text=Data Sources area (the directory hierarchy)
@@ -192,3 +191,4 @@ ViewPreferencesPanel.centralRepoLabel.text=Do not use Central Repository for:
ViewPreferencesPanel.commentsOccurencesColumnsCheckbox.text=C(omments) and O(ccurences) columns to reduce loading times
ViewPreferencesPanel.deletedFilesLimitCheckbox.text=Limit to 10,000
ViewPreferencesPanel.deletedFilesLimitLabel.text=Limit number of deleted files displayed:
+ViewPreferencesPanel.useAnotherTimeRadioButton.text=Use another time zone
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties
index 0085a9f773..c9172f03a0 100644
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle_ja.properties
@@ -128,6 +128,5 @@ ViewPreferencesPanel.useBestViewerRadioButton.text=\u6700\u3082\u5c02\u9580\u768
ViewPreferencesPanel.keepCurrentViewerRadioButton.text=\u305d\u306e\u307e\u307e\u540c\u3058\u30d5\u30a1\u30a4\u30eb\u30d3\u30e5\u30fc\u30a2\u3092\u4f7f\u7528
ViewPreferencesPanel.keepCurrentViewerRadioButton.toolTipText=\u4f8b\u3048\u3070\u3001JPEG\u304c\u9078\u629e\u3055\u308c\u305f\u5834\u5408\u306b\u305d\u306e\u307e\u307eHEX\u30d3\u30e5\u30fc\u3092\u4f7f\u7528\u3002
ViewPreferencesPanel.useLocalTimeRadioButton.text=\u30ed\u30fc\u30ab\u30eb\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u4f7f\u7528
-ViewPreferencesPanel.useGMTTimeRadioButton.text=GMT\u3092\u4f7f\u7528
ViewPreferencesPanel.dataSourcesHideKnownCheckbox.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30a8\u30ea\u30a2\uff08\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u968e\u5c64\uff09
ViewPreferencesPanel.viewsHideKnownCheckbox.text=\u30d3\u30e5\u30fc\u30a8\u30ea\u30a2
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java
index 2cccd31428..39d3203b13 100644
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java
@@ -487,7 +487,8 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())
|| (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID())
- || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID())) {
+ || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID())
+ || (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID())) {
return 3;
} else {
return 6;
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/TextPrompt.java b/Core/src/org/sleuthkit/autopsy/corecomponents/TextPrompt.java
index 01e035bfdf..1a34609627 100644
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/TextPrompt.java
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/TextPrompt.java
@@ -40,6 +40,7 @@ public final class TextPrompt extends JLabel
public TextPrompt(String text, JTextComponent component, Show show) {
this.component = component;
+ component.removeAll();
setShow(show);
document = component.getDocument();
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form
index ddeb57bd74..711caf3b7b 100755
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form
@@ -31,11 +31,19 @@
+
+
+
+
+
+
+
+
@@ -87,57 +95,68 @@
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
-
-
-
-
+
-
@@ -158,6 +177,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -169,20 +198,12 @@
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
@@ -295,14 +316,14 @@
-
+
-
+
-
+
@@ -356,6 +377,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java
index 51d4120449..872a7d6d6d 100755
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java
@@ -19,12 +19,14 @@
package org.sleuthkit.autopsy.corecomponents;
import java.util.Objects;
+import java.util.TimeZone;
import javax.swing.JPanel;
import org.netbeans.spi.options.OptionsPanelController;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.core.UserPreferences;
+import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.deletedFiles.DeletedFilePreferences;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
@@ -44,6 +46,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
public ViewPreferencesPanel(boolean immediateUpdates) {
initComponents();
this.immediateUpdates = immediateUpdates;
+
+ this.timeZoneList.setListData(TimeZoneUtils.createTimeZoneList().stream().toArray(String[]::new));
}
@Override
@@ -54,8 +58,10 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
useBestViewerRadioButton.setSelected(!keepPreferredViewer);
boolean useLocalTime = UserPreferences.displayTimesInLocalTime();
+ timeZoneList.setEnabled(!useLocalTime);
+ timeZoneList.setSelectedValue(TimeZoneUtils.createTimeZoneString(TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays())), true);
useLocalTimeRadioButton.setSelected(useLocalTime);
- useGMTTimeRadioButton.setSelected(!useLocalTime);
+ useAnotherTimeRadioButton.setSelected(!useLocalTime);
dataSourcesHideKnownCheckbox.setSelected(UserPreferences.hideKnownFilesInDataSourcesTree());
viewsHideKnownCheckbox.setSelected(UserPreferences.hideKnownFilesInViewsTree());
@@ -84,6 +90,9 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
public void store() {
UserPreferences.setKeepPreferredContentViewer(keepCurrentViewerRadioButton.isSelected());
UserPreferences.setDisplayTimesInLocalTime(useLocalTimeRadioButton.isSelected());
+ if (useAnotherTimeRadioButton.isSelected()) {
+ UserPreferences.setTimeZoneForDisplays(timeZoneList.getSelectedValue().substring(11).trim());
+ }
UserPreferences.setHideKnownFilesInDataSourcesTree(dataSourcesHideKnownCheckbox.isSelected());
UserPreferences.setHideKnownFilesInViewsTree(viewsHideKnownCheckbox.isSelected());
UserPreferences.setHideSlackFilesInDataSourcesTree(dataSourcesHideSlackCheckbox.isSelected());
@@ -135,19 +144,24 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
viewsHideSlackCheckbox = new javax.swing.JCheckBox();
displayTimeLabel = new javax.swing.JLabel();
useLocalTimeRadioButton = new javax.swing.JRadioButton();
- useGMTTimeRadioButton = new javax.swing.JRadioButton();
+ useAnotherTimeRadioButton = new javax.swing.JRadioButton();
hideOtherUsersTagsCheckbox = new javax.swing.JCheckBox();
hideOtherUsersTagsLabel = new javax.swing.JLabel();
commentsOccurencesColumnsCheckbox = new javax.swing.JCheckBox();
centralRepoLabel = new javax.swing.JLabel();
deletedFilesLimitCheckbox = new javax.swing.JCheckBox();
deletedFilesLimitLabel = new javax.swing.JLabel();
+ jScrollPane1 = new javax.swing.JScrollPane();
+ timeZoneList = new javax.swing.JList<>();
currentCaseSettingsPanel = new javax.swing.JPanel();
groupByDataSourceCheckbox = new javax.swing.JCheckBox();
currentSessionSettingsPanel = new javax.swing.JPanel();
hideRejectedResultsCheckbox = new javax.swing.JCheckBox();
viewPreferencesScrollPane.setBorder(null);
+ viewPreferencesScrollPane.setPreferredSize(new java.awt.Dimension(625, 452));
+
+ viewPreferencesPanel.setPreferredSize(new java.awt.Dimension(625, 452));
globalSettingsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.globalSettingsPanel.border.title"))); // NOI18N
@@ -210,10 +224,10 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
}
});
- org.openide.awt.Mnemonics.setLocalizedText(useGMTTimeRadioButton, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.useGMTTimeRadioButton.text")); // NOI18N
- useGMTTimeRadioButton.addActionListener(new java.awt.event.ActionListener() {
+ org.openide.awt.Mnemonics.setLocalizedText(useAnotherTimeRadioButton, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.useAnotherTimeRadioButton.text")); // NOI18N
+ useAnotherTimeRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
- useGMTTimeRadioButtonActionPerformed(evt);
+ useAnotherTimeRadioButtonActionPerformed(evt);
}
});
@@ -244,6 +258,13 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
org.openide.awt.Mnemonics.setLocalizedText(deletedFilesLimitLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.deletedFilesLimitLabel.text")); // NOI18N
+ timeZoneList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
+ public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
+ timeZoneListValueChanged(evt);
+ }
+ });
+ jScrollPane1.setViewportView(timeZoneList);
+
javax.swing.GroupLayout globalSettingsPanelLayout = new javax.swing.GroupLayout(globalSettingsPanel);
globalSettingsPanel.setLayout(globalSettingsPanelLayout);
globalSettingsPanelLayout.setHorizontalGroup(
@@ -256,41 +277,48 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(commentsOccurencesColumnsCheckbox)
.addComponent(hideOtherUsersTagsCheckbox)
- .addComponent(deletedFilesLimitCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+ .addGroup(globalSettingsPanelLayout.createSequentialGroup()
+ .addComponent(deletedFilesLimitCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addGap(399, 399, 399))))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(globalSettingsPanelLayout.createSequentialGroup()
+ .addComponent(centralRepoLabel)
+ .addGap(135, 135, 135)
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 272, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(hideKnownFilesLabel)
- .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(hideKnownFilesLabel)
+ .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(globalSettingsPanelLayout.createSequentialGroup()
+ .addGap(10, 10, 10)
+ .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(dataSourcesHideSlackCheckbox)
+ .addComponent(viewsHideSlackCheckbox)))
+ .addComponent(hideSlackFilesLabel))
+ .addGroup(globalSettingsPanelLayout.createSequentialGroup()
+ .addGap(10, 10, 10)
+ .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(dataSourcesHideKnownCheckbox)
+ .addComponent(viewsHideKnownCheckbox)))))
+ .addGap(18, 18, 18)
+ .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(displayTimeLabel)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(dataSourcesHideSlackCheckbox)
- .addComponent(viewsHideSlackCheckbox)))
- .addComponent(hideSlackFilesLabel))
- .addGroup(globalSettingsPanelLayout.createSequentialGroup()
- .addGap(10, 10, 10)
- .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(dataSourcesHideKnownCheckbox)
- .addComponent(viewsHideKnownCheckbox)))))
- .addGap(18, 18, 18)
- .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(displayTimeLabel)
- .addGroup(globalSettingsPanelLayout.createSequentialGroup()
- .addGap(10, 10, 10)
- .addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(keepCurrentViewerRadioButton)
- .addComponent(useBestViewerRadioButton)
- .addComponent(useGMTTimeRadioButton)
- .addComponent(useLocalTimeRadioButton)))
- .addComponent(selectFileLabel)))
- .addComponent(hideOtherUsersTagsLabel)
- .addComponent(centralRepoLabel)
- .addComponent(deletedFilesLimitLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addGap(0, 10, Short.MAX_VALUE)))
- .addContainerGap())
+ .addComponent(keepCurrentViewerRadioButton)
+ .addComponent(useBestViewerRadioButton)
+ .addComponent(useLocalTimeRadioButton)
+ .addComponent(useAnotherTimeRadioButton)))
+ .addComponent(selectFileLabel)))
+ .addComponent(hideOtherUsersTagsLabel)
+ .addComponent(deletedFilesLimitLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(0, 0, Short.MAX_VALUE)))
+ .addContainerGap())))
);
globalSettingsPanelLayout.setVerticalGroup(
globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -308,7 +336,17 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(dataSourcesHideSlackCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(viewsHideSlackCheckbox))
+ .addComponent(viewsHideSlackCheckbox)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(hideOtherUsersTagsLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(hideOtherUsersTagsCheckbox)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(centralRepoLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(commentsOccurencesColumnsCheckbox)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(deletedFilesLimitLabel))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addComponent(selectFileLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
@@ -320,17 +358,9 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(useLocalTimeRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(useGMTTimeRadioButton)))
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
- .addComponent(hideOtherUsersTagsLabel)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(hideOtherUsersTagsCheckbox)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
- .addComponent(centralRepoLabel)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(commentsOccurencesColumnsCheckbox)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
- .addComponent(deletedFilesLimitLabel)
+ .addComponent(useAnotherTimeRadioButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 67, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(deletedFilesLimitCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0))
@@ -415,11 +445,11 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(viewPreferencesScrollPane)
+ .addComponent(viewPreferencesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(viewPreferencesScrollPane)
+ .addComponent(viewPreferencesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
}// //GEN-END:initComponents
@@ -445,7 +475,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
private void useLocalTimeRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useLocalTimeRadioButtonActionPerformed
useLocalTimeRadioButton.setSelected(true);
- useGMTTimeRadioButton.setSelected(false);
+ useAnotherTimeRadioButton.setSelected(false);
+ timeZoneList.setEnabled(false);
if (immediateUpdates) {
UserPreferences.setDisplayTimesInLocalTime(true);
} else {
@@ -453,15 +484,16 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
}
}//GEN-LAST:event_useLocalTimeRadioButtonActionPerformed
- private void useGMTTimeRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useGMTTimeRadioButtonActionPerformed
+ private void useAnotherTimeRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useAnotherTimeRadioButtonActionPerformed
useLocalTimeRadioButton.setSelected(false);
- useGMTTimeRadioButton.setSelected(true);
+ useAnotherTimeRadioButton.setSelected(true);
+ timeZoneList.setEnabled(true);
if (immediateUpdates) {
UserPreferences.setDisplayTimesInLocalTime(false);
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
- }//GEN-LAST:event_useGMTTimeRadioButtonActionPerformed
+ }//GEN-LAST:event_useAnotherTimeRadioButtonActionPerformed
private void dataSourcesHideKnownCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataSourcesHideKnownCheckboxActionPerformed
if (immediateUpdates) {
@@ -535,6 +567,14 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
}
}//GEN-LAST:event_deletedFilesLimitCheckboxActionPerformed
+ private void timeZoneListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_timeZoneListValueChanged
+ if (immediateUpdates && useAnotherTimeRadioButton.isSelected()) {
+ UserPreferences.setTimeZoneForDisplays(timeZoneList.getSelectedValue().substring(11).trim());
+ } else {
+ firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
+ }
+ }//GEN-LAST:event_timeZoneListValueChanged
+
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel centralRepoLabel;
@@ -553,10 +593,12 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
private javax.swing.JLabel hideOtherUsersTagsLabel;
private javax.swing.JCheckBox hideRejectedResultsCheckbox;
private javax.swing.JLabel hideSlackFilesLabel;
+ private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JRadioButton keepCurrentViewerRadioButton;
private javax.swing.JLabel selectFileLabel;
+ private javax.swing.JList timeZoneList;
+ private javax.swing.JRadioButton useAnotherTimeRadioButton;
private javax.swing.JRadioButton useBestViewerRadioButton;
- private javax.swing.JRadioButton useGMTTimeRadioButton;
private javax.swing.JRadioButton useLocalTimeRadioButton;
private javax.swing.JPanel viewPreferencesPanel;
private javax.swing.JScrollPane viewPreferencesScrollPane;
diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/NetworkUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/NetworkUtils.java
index af41bc1980..78547f8370 100644
--- a/Core/src/org/sleuthkit/autopsy/coreutils/NetworkUtils.java
+++ b/Core/src/org/sleuthkit/autopsy/coreutils/NetworkUtils.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2012-2015 Basis Technology Corp.
+ * Copyright 2012-2018 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,14 +18,22 @@
*/
package org.sleuthkit.autopsy.coreutils;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.net.UnknownHostException;
+import java.util.StringTokenizer;
public class NetworkUtils {
+
+ private NetworkUtils() {
+ }
/**
* Set the host name variable. Sometimes the network can be finicky, so the
* answer returned by getHostName() could throw an exception or be null.
* Have it read the environment variable if getHostName() is unsuccessful.
+ *
+ * @return the local host name
*/
public static String getLocalHostName() {
String hostName = "";
@@ -41,4 +49,78 @@ public class NetworkUtils {
}
return hostName;
}
+
+ /**
+ * Attempt to manually extract the domain from a URL.
+ *
+ * @param url
+ * @return empty string if no domain could be found
+ */
+ private static String getBaseDomain(String url) {
+ String host = null;
+
+ //strip protocol
+ String cleanUrl = url.replaceFirst(".*:\\/\\/", "");
+
+ //strip after slashes
+ String dirToks[] = cleanUrl.split("\\/");
+ if (dirToks.length > 0) {
+ host = dirToks[0];
+ } else {
+ host = cleanUrl;
+ }
+
+ //get the domain part from host (last 2)
+ StringTokenizer tok = new StringTokenizer(host, ".");
+ StringBuilder hostB = new StringBuilder();
+ int toks = tok.countTokens();
+
+ for (int count = 0; count < toks; ++count) {
+ String part = tok.nextToken();
+ int diff = toks - count;
+ if (diff < 3) {
+ hostB.append(part);
+ }
+ if (diff == 2) {
+ hostB.append(".");
+ }
+ }
+
+
+ String base = hostB.toString();
+ // verify there are no special characters in there
+ if (base.matches(".*[~`!@#$%^&\\*\\(\\)\\+={}\\[\\];:\\?<>,/ ].*")) {
+ return "";
+ }
+ return base;
+ }
+
+ /**
+ * Attempt to extract the domain from a URL.
+ * Will start by using the built-in URL class, and if that fails will
+ * try to extract it manually.
+ *
+ * @param urlString The URL to extract the domain from
+ * @return empty string if no domain name was found
+ */
+ public static String extractDomain(String urlString) {
+ if (urlString == null) {
+ return "";
+ }
+ String result = "";
+
+ try {
+ URL url = new URL(urlString);
+ result = url.getHost();
+ } catch (MalformedURLException ex) {
+ //do not log if not a valid URL - we will try to extract it ourselves
+ }
+
+ //was not a valid URL, try a less picky method
+ if (result == null || result.trim().isEmpty()) {
+ return getBaseDomain(urlString);
+ }
+ return result;
+ }
+
}
diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/TimeZoneUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/TimeZoneUtils.java
index fa175a995a..c844d9ce97 100644
--- a/Core/src/org/sleuthkit/autopsy/coreutils/TimeZoneUtils.java
+++ b/Core/src/org/sleuthkit/autopsy/coreutils/TimeZoneUtils.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2011-2016 Basis Technology Corp.
+ * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +20,13 @@ package org.sleuthkit.autopsy.coreutils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
/**
* Utility methods for workig with time zones.
@@ -41,7 +47,7 @@ public class TimeZoneUtils {
java.util.TimeZone zone = java.util.TimeZone.getTimeZone(timeZoneId);
int offset = zone.getRawOffset() / 1000;
int hour = offset / 3600;
- int min = (offset % 3600) / 60;
+ int min = Math.abs((offset % 3600) / 60);
DateFormat dfm = new SimpleDateFormat("z");
dfm.setTimeZone(zone);
@@ -59,6 +65,73 @@ public class TimeZoneUtils {
return result;
}
+
+ /**
+ * Generate a time zone string containing the GMT offset and ID.
+ *
+ * @param timeZone The time zone.
+ *
+ * @return The time zone string.
+ */
+ public static String createTimeZoneString(TimeZone timeZone) {
+ int offset = timeZone.getRawOffset() / 1000;
+ int hour = offset / 3600;
+ int minutes = Math.abs((offset % 3600) / 60);
+
+ return String.format("(GMT%+d:%02d) %s", hour, minutes, timeZone.getID()); //NON-NLS
+ }
+
+ /**
+ * Generates a list of time zones.
+ */
+ public static List createTimeZoneList() {
+ /*
+ * Create a list of time zones.
+ */
+ List timeZoneList = new ArrayList<>();
+
+ String[] ids = SimpleTimeZone.getAvailableIDs();
+ for (String id : ids) {
+ /*
+ * DateFormat dfm = new SimpleDateFormat("z");
+ * dfm.setTimeZone(zone); boolean hasDaylight =
+ * zone.useDaylightTime(); String first = dfm.format(new Date(2010,
+ * 1, 1)); String second = dfm.format(new Date(2011, 6, 6)); int mid
+ * = hour * -1; String result = first + Integer.toString(mid);
+ * if(hasDaylight){ result = result + second; }
+ * timeZoneComboBox.addItem(item + " (" + result + ")");
+ */
+ timeZoneList.add(TimeZone.getTimeZone(id));
+ }
+
+ /*
+ * Sort the list of time zones first by offset, then by ID.
+ */
+ Collections.sort(timeZoneList, new Comparator(){
+ @Override
+ public int compare(TimeZone o1, TimeZone o2){
+ int offsetDelta = Integer.compare(o1.getRawOffset(), o2.getRawOffset());
+
+ if (offsetDelta == 0) {
+ return o1.getID().compareToIgnoreCase(o2.getID());
+ }
+
+ return offsetDelta;
+ }
+ });
+
+ /*
+ * Create a list of Strings encompassing both the GMT offset and the
+ * time zone ID.
+ */
+ List outputList = new ArrayList<>();
+
+ for (TimeZone timeZone : timeZoneList) {
+ outputList.add(createTimeZoneString(timeZone));
+ }
+
+ return outputList;
+ }
/**
* Prevents instantiation.
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java b/Core/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java
index 5041250134..c44289491e 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/ContentUtils.java
@@ -145,7 +145,7 @@ public final class ContentUtils {
try {
if (!shouldDisplayTimesInLocalTime()) {
- return TimeZone.getTimeZone("GMT");
+ return TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays());
} else {
final Content dataSource = content.getDataSource();
if ((dataSource != null) && (dataSource instanceof Image)) {
diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java
index 6bee921a91..747653ee65 100644
--- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java
@@ -20,8 +20,7 @@ package org.sleuthkit.autopsy.datasourceprocessors;
import java.io.File;
import java.util.Calendar;
-import java.util.SimpleTimeZone;
-import java.util.TimeZone;
+import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.event.DocumentEvent;
@@ -32,6 +31,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PathValidator;
+import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
/**
* Allows examiner to supply a raw data source.
@@ -82,26 +82,13 @@ final class RawDSInputPanel extends JPanel implements DocumentListener {
* machine time zone to be selected.
*/
private void createTimeZoneList() {
- // load and add all timezone
- String[] ids = SimpleTimeZone.getAvailableIDs();
- for (String id : ids) {
- TimeZone zone = TimeZone.getTimeZone(id);
- int offset = zone.getRawOffset() / 1000;
- int hour = offset / 3600;
- int minutes = (offset % 3600) / 60;
- String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id);
-
- timeZoneComboBox.addItem(item);
+ List timeZoneList = TimeZoneUtils.createTimeZoneList();
+ for (String timeZone : timeZoneList) {
+ timeZoneComboBox.addItem(timeZone);
}
- // get the current timezone
- TimeZone thisTimeZone = Calendar.getInstance().getTimeZone();
- int thisOffset = thisTimeZone.getRawOffset() / 1000;
- int thisHour = thisOffset / 3600;
- int thisMinutes = (thisOffset % 3600) / 60;
- String formatted = String.format("(GMT%+d:%02d) %s", thisHour, thisMinutes, thisTimeZone.getID());
// set the selected timezone
- timeZoneComboBox.setSelectedItem(formatted);
+ timeZoneComboBox.setSelectedItem(TimeZoneUtils.createTimeZoneString(Calendar.getInstance().getTimeZone()));
}
/**
diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java
index 17ee1d078f..97bc006441 100644
--- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java
+++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java
@@ -167,6 +167,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
public void preferenceChange(PreferenceChangeEvent evt) {
switch (evt.getKey()) {
case UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME:
+ case UserPreferences.TIME_ZONE_FOR_DISPLAYS:
case UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE:
case UserPreferences.HIDE_SLACK_FILES_IN_DATA_SRCS_TREE:
case UserPreferences.HIDE_CENTRAL_REPO_COMMENTS_AND_OCCURRENCES:
diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java
index 048ccd5f79..8691fc93aa 100644
--- a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java
+++ b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java
@@ -29,7 +29,6 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.EnumSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SimpleTimeZone;
@@ -44,6 +43,7 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
+import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
/**
* Filters file date properties (modified/created/etc.. times)
@@ -144,30 +144,15 @@ class DateSearchFilter extends AbstractFileSearchFilter {
Case currentCase = Case.getCurrentCaseThrows(); // get the most updated case
Set caseTimeZones = currentCase.getTimeZones();
- Iterator iterator = caseTimeZones.iterator();
- while (iterator.hasNext()) {
- TimeZone zone = iterator.next();
- int offset = zone.getRawOffset() / 1000;
- int hour = offset / 3600;
- int minutes = (offset % 3600) / 60;
- String item = String.format("(GMT%+d:%02d) %s", hour, minutes, zone.getID()); //NON-NLS
- timeZones.add(item);
+ for (TimeZone timeZone : caseTimeZones) {
+ timeZones.add(TimeZoneUtils.createTimeZoneString(timeZone));
}
if (caseTimeZones.size() > 0) {
timeZones.add(SEPARATOR);
}
- // load and add all timezone
- String[] ids = SimpleTimeZone.getAvailableIDs();
- for (String id : ids) {
- TimeZone zone = TimeZone.getTimeZone(id);
- int offset = zone.getRawOffset() / 1000;
- int hour = offset / 3600;
- int minutes = (offset % 3600) / 60;
- String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id); //NON-NLS
- timeZones.add(item);
- }
+ timeZones.addAll(TimeZoneUtils.createTimeZoneList());
} catch (NoCurrentCaseException ex) {
// No current case.
}
diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java
index b123197f0a..e7e959e175 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java
@@ -269,8 +269,7 @@ class MSOfficeEmbeddedContentExtractor {
HWPFDocument doc = new HWPFDocument(new ReadContentInputStream(af));
PicturesTable pictureTable = doc.getPicturesTable();
listOfAllPictures = pictureTable.getAllPictures();
- } catch (IOException | IllegalArgumentException
- | IndexOutOfBoundsException | NullPointerException ex) {
+ } catch (Exception ex) {
// IOException:
// Thrown when the document has issues being read.
@@ -286,10 +285,9 @@ class MSOfficeEmbeddedContentExtractor {
// These get thrown in certain images. The reason is unknown. It is
// likely due to problems with the file formats that POI is poorly
// handling.
- return null;
- } catch (Throwable ex) {
- // instantiating POI containers throw RuntimeExceptions
- LOGGER.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.docContainer.init.err", af.getName()), ex); //NON-NLS
+
+ //Any runtime exception escaping
+ LOGGER.log(Level.WARNING, "Word document container could not be initialized. Reason: {0}", ex.getMessage()); //NON-NLS
return null;
}
@@ -333,8 +331,7 @@ class MSOfficeEmbeddedContentExtractor {
try {
HSLFSlideShow ppt = new HSLFSlideShow(new ReadContentInputStream(af));
listOfAllPictures = ppt.getPictureData();
- } catch (IOException | IllegalArgumentException
- | IndexOutOfBoundsException ex) {
+ } catch (Exception ex) {
// IllegalArgumentException:
// This will catch OldFileFormatException, which is thrown when the
// document version is unsupported. The IllegalArgumentException may
@@ -346,10 +343,7 @@ class MSOfficeEmbeddedContentExtractor {
// This gets thrown in certain images. The reason is unknown. It is
// likely due to problems with the file formats that POI is poorly
// handling.
- return null;
- } catch (Throwable ex) {
- // instantiating POI containers throw RuntimeExceptions
- LOGGER.log(Level.SEVERE, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.pptContainer.init.err", af.getName()), ex); //NON-NLS
+ LOGGER.log(Level.WARNING, "PPT container could not be initialized. Reason: {0}", ex.getMessage()); //NON-NLS
return null;
}
@@ -422,9 +416,7 @@ class MSOfficeEmbeddedContentExtractor {
try {
Workbook xls = new HSSFWorkbook(new ReadContentInputStream(af));
listOfAllPictures = xls.getAllPictures();
- } catch (IOException | LeftoverDataException
- | RecordFormatException | IllegalArgumentException
- | IndexOutOfBoundsException ex) {
+ } catch (Exception ex) {
// IllegalArgumentException:
// This will catch OldFileFormatException, which is thrown when the
// document version is unsupported. The IllegalArgumentException may
@@ -443,10 +435,7 @@ class MSOfficeEmbeddedContentExtractor {
// These get thrown in certain images. The reason is unknown. It is
// likely due to problems with the file formats that POI is poorly
// handling.
- return null;
- } catch (Throwable ex) {
- // instantiating POI containers throw RuntimeExceptions
- LOGGER.log(Level.SEVERE, String.format("%s%s", NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.xlsContainer.init.err", af.getName()), af.getName()), ex); //NON-NLS
+ LOGGER.log(Level.WARNING, "Excel (.xls) document container could not be initialized. Reason: {0}", ex.getMessage()); //NON-NLS
return null;
}
diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java
index d254347b78..41758a0ec0 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java
@@ -180,6 +180,15 @@ class SevenZipExtractor {
* @return true if potential zip bomb, false otherwise
*/
private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISevenZipInArchive inArchive, int inArchiveItemIndex, ConcurrentHashMap depthMap, String escapedFilePath) {
+ //If a file is corrupted as a result of reconstructing it from unallocated space, then
+ //7zip does a poor job estimating the original uncompressed file size.
+ //As a result, many corrupted files have wonky compression ratios and could flood the UI
+ //with false zip bomb notifications. The decision was made to skip compression ratio checks
+ //for unallocated zip files. Instead, we let the depth be an indicator of a zip bomb.
+ if(archiveFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC)) {
+ return false;
+ }
+
try {
final Long archiveItemSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.SIZE);
@@ -540,7 +549,6 @@ class SevenZipExtractor {
inArchive = SevenZip.openInArchive(options, stream, password);
}
numItems = inArchive.getNumberOfItems();
- logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{escapedArchiveFilePath, numItems}); //NON-NLS
progress.start(numItems);
progressStarted = true;
diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java
index 27863bb3f3..c2c5cff3b4 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java
@@ -28,6 +28,7 @@ import com.healthmarketscience.jackcess.util.MemFileChannel;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.BufferUnderflowException;
import java.util.logging.Level;
import org.apache.tika.exception.EncryptedDocumentException;
import org.apache.tika.exception.TikaException;
@@ -313,7 +314,12 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
DatabaseBuilder databaseBuilder = new DatabaseBuilder();
databaseBuilder.setChannel(memFileChannel);
databaseBuilder.setCodecProvider(codecProvider);
- Database accessDatabase = databaseBuilder.open();
+ Database accessDatabase;
+ try {
+ accessDatabase = databaseBuilder.open();
+ } catch (IOException | BufferUnderflowException | IndexOutOfBoundsException ignored) {
+ return passwordProtected;
+ }
/*
* No exception has been thrown at this point, so the file
* is either a JET database, or an unprotected ACE database.
diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties
index c461fc193f..11ace044fb 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties
@@ -91,7 +91,7 @@ HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg=Failed to open has
HashLookupModuleFactory.moduleName.text=Hash Lookup
HashLookupModuleFactory.moduleDescription.text=Identifies known and notable files using supplied hash sets, such as a standard NSRL hash set.
HashDbIngestModule.fileReadErrorMsg=Read Error\: {0}
-HashDbIngestModule.calcHashValueErr=Error encountered while calculating the hash value for {0}.
+HashDbIngestModule.calcHashValueErr=Error encountered while calculating the hash value for {0} ({1}).
HashDbIngestModule.hashLookupErrorMsg=Hash Lookup Error\: {0}
HashDbIngestModule.settingKnownBadStateErr=Error encountered while setting notable state for {0}.
HashDbIngestModule.lookingUpKnownBadHashValueErr=Error encountered while looking up notable hash value for {0}.
diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java
index 7f27e0d242..a562ab2a22 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java
@@ -227,7 +227,9 @@ public class HashDbIngestModule implements FileIngestModule {
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", name),
- NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr", name)));
+ NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr",
+ file.getParentPath() + file.getName(),
+ file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)?"Allocated File" : "Deleted File")));
return ProcessResult.ERROR;
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSet.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSet.java
index d17a773dbc..1e69c6deaa 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSet.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSet.java
@@ -648,6 +648,15 @@ public final class FilesSet implements Serializable {
AbstractTextCondition(Pattern regex) {
this.textMatcher = new FilesSet.Rule.RegexMatcher(regex);
}
+
+ /**
+ * Construct a case-insensitive multi-value text condition.
+ *
+ * @param values The list of values in which to look for a match.
+ */
+ AbstractTextCondition(List values) {
+ this.textMatcher = new FilesSet.Rule.CaseInsensitiveMultiValueStringComparisionMatcher(values);
+ }
/**
* Get the text the condition matches.
@@ -820,11 +829,11 @@ public final class FilesSet implements Serializable {
*
* @param extension The file name extension to be matched.
*/
- public ExtensionCondition(String extension) {
+ public ExtensionCondition(List extensions) {
// If there is a leading ".", strip it since
// AbstractFile.getFileNameExtension() returns just the
// extension chars and not the dot.
- super(extension.startsWith(".") ? extension.substring(1) : extension, false);
+ super(extensions);
}
/**
@@ -948,6 +957,60 @@ public final class FilesSet implements Serializable {
}
}
+ /**
+ * A text matcher that looks for a single case-insensitive string match
+ * in a multi-value list.
+ */
+ private static class CaseInsensitiveMultiValueStringComparisionMatcher implements TextMatcher {
+
+ private static final long serialVersionUID = 1L;
+ private final List valuesToMatch;
+
+ /**
+ * Construct a text matcher that looks for a single case-insensitive
+ * string match in a multi-value list.
+ *
+ * @param valuesToMatch The list of values in which to look for a
+ * match.
+ */
+ CaseInsensitiveMultiValueStringComparisionMatcher(List valuesToMatch) {
+ List values = new ArrayList<>(valuesToMatch);
+ for (int i=0; i < values.size(); i++) {
+ // Remove leading and trailing whitespace.
+ String tempValue = values.get(i).trim();
+
+ // Strip "." from the start of the extension if it exists.
+ if (tempValue.startsWith(".")) {
+ tempValue = tempValue.substring(1);
+ }
+
+ values.set(i, tempValue);
+ }
+ this.valuesToMatch = values;
+ }
+
+ @Override
+ public String getTextToMatch() {
+ return String.join(",", this.valuesToMatch);
+ }
+
+ @Override
+ public boolean isRegex() {
+ return false;
+ }
+
+ @Override
+ public boolean textMatches(String subject) {
+ for (String value : valuesToMatch) {
+ if (value.equalsIgnoreCase(subject)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ }
+
/**
* A text matcher that does regular expression matching.
*/
diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form
index f06010a272..334af72187 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form
@@ -29,7 +29,7 @@
-
+
@@ -173,7 +173,7 @@
-
+
diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java
index 71a4be086a..0f687fc700 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java
@@ -960,7 +960,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
.addComponent(editRuleButton)
.addGap(18, 18, 18)
.addComponent(deleteRuleButton)))
- .addGap(24, 47, Short.MAX_VALUE))))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
);
jPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {copySetButton, deleteSetButton, editSetButton, exportSetButton, importSetButton, newSetButton});
@@ -1060,7 +1060,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(jScrollPane1)
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 800, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.form b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.form
index b91bb34d36..1d75acf880 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.form
+++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.form
@@ -47,22 +47,23 @@
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -205,6 +206,9 @@
+
+
+
@@ -216,6 +220,9 @@
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java
index 36888279a9..7f8285c4a1 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java
@@ -18,7 +18,9 @@
*/
package org.sleuthkit.autopsy.modules.interestingitems;
+import java.awt.Color;
import java.awt.event.ActionEvent;
+import java.util.Arrays;
import java.util.List;
import java.util.SortedSet;
import java.util.logging.Level;
@@ -31,6 +33,7 @@ import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
+import org.sleuthkit.autopsy.corecomponents.TextPrompt;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
import org.sleuthkit.autopsy.modules.interestingitems.FilesSetDefsPanel.PANEL_TYPE;
@@ -46,6 +49,8 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
"FilesSetRulePanel.kiloBytes=Kilobytes",
"FilesSetRulePanel.megaBytes=Megabytes",
"FilesSetRulePanel.gigaBytes=Gigabytes",
+ "FilesSetRulePanel.nameTextField.fullNameExample=Example: \"file.exe\"",
+ "FilesSetRulePanel.nameTextField.extensionExample=Examples: \"jpg\" or \"jpg,jpeg,gif\"",
"FilesSetRulePanel.NoConditionError=Must have at least one condition to make a rule.",
"FilesSetRulePanel.NoMimeTypeError=Please select a valid MIME type.",
"FilesSetRulePanel.NoNameError=Name cannot be empty",
@@ -62,6 +67,7 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
private static final List ILLEGAL_FILE_PATH_CHARS = FilesSetsManager.getIllegalFilePathChars();
private JButton okButton;
private JButton cancelButton;
+ private TextPrompt nameTextFieldPrompt;
/**
* Constructs a files set rule panel in create rule mode.
@@ -87,6 +93,8 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
this.dateCheckActionPerformed(null);
populateComponentsWithDefaultValues();
this.setButtons(okButton, cancelButton);
+
+ updateNameTextFieldPrompt();
}
/**
@@ -120,6 +128,34 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
populatePathConditionComponents(rule);
populateDateConditionComponents(rule);
this.setButtons(okButton, cancelButton);
+
+ updateNameTextFieldPrompt();
+ }
+
+ /**
+ * Update the text prompt of the name text field based on the input type
+ * selection.
+ */
+ private void updateNameTextFieldPrompt() {
+ /**
+ * Add text prompt to the text field.
+ */
+ String text;
+ if (fullNameRadioButton.isSelected()) {
+ text = Bundle.FilesSetRulePanel_nameTextField_fullNameExample();
+ } else {
+ text = Bundle.FilesSetRulePanel_nameTextField_extensionExample();
+ }
+ nameTextFieldPrompt = new TextPrompt(text, nameTextField);
+
+ /**
+ * Sets the foreground color and transparency of the text prompt.
+ */
+ nameTextFieldPrompt.setForeground(Color.LIGHT_GRAY);
+ nameTextFieldPrompt.changeAlpha(0.9f); // Mostly opaque
+
+ validate();
+ repaint();
}
/**
@@ -435,7 +471,7 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
if (!this.nameTextField.getText().isEmpty()) {
if (this.nameRegexCheckbox.isSelected()) {
try {
- Pattern pattern = Pattern.compile(this.nameTextField.getText());
+ Pattern pattern = Pattern.compile(this.nameTextField.getText(), Pattern.CASE_INSENSITIVE);
if (this.fullNameRadioButton.isSelected()) {
condition = new FilesSet.Rule.FullNameCondition(pattern);
} else {
@@ -449,7 +485,7 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
if (this.fullNameRadioButton.isSelected()) {
condition = new FilesSet.Rule.FullNameCondition(this.nameTextField.getText());
} else {
- condition = new FilesSet.Rule.ExtensionCondition(this.nameTextField.getText());
+ condition = new FilesSet.Rule.ExtensionCondition(Arrays.asList(this.nameTextField.getText().split(",")));
}
} else {
logger.log(Level.SEVERE, "Attempt to get name condition with illegal chars"); // NON-NLS
@@ -520,7 +556,7 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
if (!this.pathTextField.getText().isEmpty()) {
if (this.pathRegexCheckBox.isSelected()) {
try {
- condition = new FilesSet.Rule.ParentPathCondition(Pattern.compile(this.pathTextField.getText()));
+ condition = new FilesSet.Rule.ParentPathCondition(Pattern.compile(this.pathTextField.getText(), Pattern.CASE_INSENSITIVE));
} catch (PatternSyntaxException ex) {
logger.log(Level.SEVERE, "Attempt to get malformed path condition", ex); // NON-NLS
throw new IllegalStateException("The files set rule panel path condition is not in a valid state"); // NON-NLS
@@ -657,10 +693,20 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
nameButtonGroup.add(fullNameRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(fullNameRadioButton, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.fullNameRadioButton.text")); // NOI18N
fullNameRadioButton.setEnabled(false);
+ fullNameRadioButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ fullNameRadioButtonActionPerformed(evt);
+ }
+ });
nameButtonGroup.add(extensionRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(extensionRadioButton, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.extensionRadioButton.text")); // NOI18N
extensionRadioButton.setEnabled(false);
+ extensionRadioButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ extensionRadioButtonActionPerformed(evt);
+ }
+ });
org.openide.awt.Mnemonics.setLocalizedText(nameRegexCheckbox, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.nameRegexCheckbox.text")); // NOI18N
nameRegexCheckbox.setEnabled(false);
@@ -782,18 +828,19 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
.addComponent(pathSeparatorInfoLabel))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(ruleNameTextField)
.addGroup(layout.createSequentialGroup()
.addComponent(daysIncludedTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 69, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(daysIncludedLabel))
- .addComponent(ruleNameTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 249, Short.MAX_VALUE)
- .addGroup(layout.createSequentialGroup()
- .addComponent(fullNameRadioButton)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(extensionRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 98, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(nameRegexCheckbox)))
- .addGap(1, 1, 1))))
+ .addComponent(daysIncludedLabel)
+ .addGap(0, 0, Short.MAX_VALUE)))
+ .addGap(1, 1, 1))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(fullNameRadioButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(extensionRadioButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(nameRegexCheckbox))))
.addComponent(jLabel5)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -952,6 +999,14 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
this.setOkButton();
}//GEN-LAST:event_mimeCheckActionPerformed
+ private void extensionRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_extensionRadioButtonActionPerformed
+ updateNameTextFieldPrompt();
+ }//GEN-LAST:event_extensionRadioButtonActionPerformed
+
+ private void fullNameRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fullNameRadioButtonActionPerformed
+ updateNameTextFieldPrompt();
+ }//GEN-LAST:event_fullNameRadioButtonActionPerformed
+
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton allRadioButton;
private javax.swing.JCheckBox dateCheck;
diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemsFilesSetSettings.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemsFilesSetSettings.java
index 3f4cca4465..f491fe14d6 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemsFilesSetSettings.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/InterestingItemsFilesSetSettings.java
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -280,7 +281,7 @@ class InterestingItemsFilesSetSettings implements Serializable {
if (elem.getTagName().equals(NAME_RULE_TAG)) {
nameCondition = new FilesSet.Rule.FullNameCondition(content);
} else if (elem.getTagName().equals(EXTENSION_RULE_TAG)) {
- nameCondition = new FilesSet.Rule.ExtensionCondition(content);
+ nameCondition = new FilesSet.Rule.ExtensionCondition(Arrays.asList(content.split(",")));
}
}
}
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java
index e62c58343c..2275b63d5f 100644
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java
@@ -41,6 +41,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
import org.sleuthkit.datamodel.Content;
+import org.sleuthkit.datamodel.DataSource;
/*
* A runnable that adds an archive data source as well as data sources contained
@@ -195,9 +196,18 @@ class AddArchiveTask implements Runnable {
continue;
}
- // if we are here it means the data source was addedd successfully
+ // if we are here it means the data source was added successfully
success = true;
newDataSources.addAll(internalDataSource.getContent());
+
+ // Update the names for all new data sources to be the root archive plus the name of the data source
+ for (Content c:internalDataSource.getContent()) {
+ if (c instanceof DataSource) {
+ DataSource ds = (DataSource) c;
+ String newName = Paths.get(archivePath).getFileName() + "/" + ds.getName();
+ ds.setDisplayName(newName);
+ }
+ }
// skip all other DSPs for this data source
break;
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java
index cfdf6cf973..7f80201f26 100644
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestMonitor.java
@@ -45,6 +45,7 @@ import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
+import org.sleuthkit.autopsy.coreutils.StopWatch;
import org.sleuthkit.autopsy.events.AutopsyEventException;
import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJob.ProcessingStatus;
@@ -666,43 +667,73 @@ final class AutoIngestMonitor extends Observable implements PropertyChangeListen
* @return A result code indicating success, partial success, or failure.
*/
CaseDeletionResult deleteCase(AutoIngestJob job) {
+ String caseName = job.getManifest().getCaseName();
+ Path caseDirectoryPath = job.getCaseDirectoryPath();
+ Path metadataFilePath = caseDirectoryPath.resolve(caseName + CaseMetadata.getFileExtension());
+ StopWatch stopWatch = new StopWatch();
+ stopWatch.start();
synchronized (jobsLock) {
- String caseName = job.getManifest().getCaseName();
- Path metadataFilePath = job.getCaseDirectoryPath().resolve(caseName + CaseMetadata.getFileExtension());
-
+ stopWatch.stop();
+ LOGGER.log(Level.INFO, String.format("Used %d s to acquire jobsLock (Java monitor in AutoIngestMonitor class) for case %s at %s", stopWatch.getElapsedTimeSecs(), caseName, caseDirectoryPath));
+ stopWatch.reset();
+ stopWatch.start();
try {
CaseMetadata metadata = new CaseMetadata(metadataFilePath);
+ stopWatch.stop();
+ LOGGER.log(Level.INFO, String.format("Used %d s to read case metadata for case %s at %s", stopWatch.getElapsedTimeSecs(), caseName, caseDirectoryPath));
+ stopWatch.reset();
+ stopWatch.start();
Case.deleteCase(metadata);
-
} catch (CaseMetadata.CaseMetadataException ex) {
- LOGGER.log(Level.SEVERE, String.format("Failed to get case metadata file %s for case %s at %s", metadataFilePath.toString(), caseName, job.getCaseDirectoryPath().toString()), ex);
+ LOGGER.log(Level.SEVERE, String.format("Failed to read case metadata file %s for case %s at %s", metadataFilePath, caseName, caseDirectoryPath), ex);
+ stopWatch.stop();
+ LOGGER.log(Level.INFO, String.format("Used %d s to fail to read case metadata file %s for case %s at %s", stopWatch.getElapsedTimeSecs(), metadataFilePath, caseName, caseDirectoryPath));
return CaseDeletionResult.FAILED;
} catch (CaseActionException ex) {
- LOGGER.log(Level.SEVERE, String.format("Failed to physically delete case %s at %s", caseName, job.getCaseDirectoryPath().toString()), ex);
+ LOGGER.log(Level.SEVERE, String.format("Failed to delete case %s at %s", caseName, caseDirectoryPath), ex);
return CaseDeletionResult.FAILED;
}
// Update the state of completed jobs associated with this case to indicate
// that the case has been deleted
- for (AutoIngestJob completedJob : getCompletedJobs()) {
+ stopWatch.reset();
+ stopWatch.start();
+ List completedJobs = getCompletedJobs();
+ stopWatch.stop();
+ LOGGER.log(Level.INFO, String.format("Used %d s to get completed jobs listing for case %s at %s", stopWatch.getElapsedTimeSecs(), caseName, caseDirectoryPath));
+ stopWatch.reset();
+ stopWatch.start();
+ for (AutoIngestJob completedJob : completedJobs) {
if (caseName.equals(completedJob.getManifest().getCaseName())) {
try {
completedJob.setProcessingStatus(DELETED);
AutoIngestJobNodeData nodeData = new AutoIngestJobNodeData(completedJob);
coordinationService.setNodeData(CoordinationService.CategoryNode.MANIFESTS, completedJob.getManifest().getFilePath().toString(), nodeData.toArray());
} catch (CoordinationServiceException | InterruptedException ex) {
- LOGGER.log(Level.SEVERE, String.format("Failed to update completed job node data for %s when deleting case %s", completedJob.getManifest().getFilePath().toString(), caseName), ex);
+ LOGGER.log(Level.SEVERE, String.format("Failed to update completed job node data for %s when deleting case %s at %s", completedJob.getManifest().getFilePath(), caseName, caseDirectoryPath), ex);
+ stopWatch.stop();
+ LOGGER.log(Level.INFO, String.format("Used %d s to fail to update job node data for completed jobs for case %s at %s", stopWatch.getElapsedTimeSecs(), caseName, caseDirectoryPath));
return CaseDeletionResult.PARTIALLY_DELETED;
}
}
}
+ stopWatch.stop();
+ LOGGER.log(Level.INFO, String.format("Used %d s to update job node data for completed jobs for case %s at %s", stopWatch.getElapsedTimeSecs(), caseName, caseDirectoryPath));
// Remove jobs associated with this case from the completed jobs collection.
- jobsSnapshot.completedJobs.removeIf((AutoIngestJob completedJob)
+ stopWatch.reset();
+ stopWatch.start();
+ completedJobs.removeIf((AutoIngestJob completedJob)
-> completedJob.getManifest().getCaseName().equals(caseName));
+ stopWatch.stop();
+ LOGGER.log(Level.INFO, String.format("Used %d s to remove completed jobs for case %s at %s from current jobs snapshot", stopWatch.getElapsedTimeSecs(), caseName, caseDirectoryPath));
// Publish a message to update auto ingest nodes.
+ stopWatch.reset();
+ stopWatch.start();
eventPublisher.publishRemotely(new AutoIngestCaseDeletedEvent(caseName, LOCAL_HOST_NAME, AutoIngestManager.getSystemUserNameProperty()));
+ stopWatch.stop();
+ LOGGER.log(Level.INFO, String.format("Used %d s to publish job deletion event for case %s at %s", stopWatch.getElapsedTimeSecs(), caseName,caseDirectoryPath));
}
return CaseDeletionResult.FULLY_DELETED;
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSInputPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSInputPanel.java
index 620c4ec49f..c08acee193 100644
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSInputPanel.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSInputPanel.java
@@ -25,9 +25,7 @@ import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.SimpleTimeZone;
import java.util.SortedSet;
-import java.util.TimeZone;
import java.util.TreeSet;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
@@ -43,6 +41,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PathValidator;
+import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class MemoryDSInputPanel extends JPanel implements DocumentListener {
@@ -132,26 +131,13 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener {
* machine time zone to be selected.
*/
private void createTimeZoneList() {
- // load and add all timezone
- String[] ids = SimpleTimeZone.getAvailableIDs();
- for (String id : ids) {
- TimeZone zone = TimeZone.getTimeZone(id);
- int offset = zone.getRawOffset() / 1000;
- int hour = offset / 3600;
- int minutes = (offset % 3600) / 60;
- String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id);
-
- timeZoneComboBox.addItem(item);
+ List timeZoneList = TimeZoneUtils.createTimeZoneList();
+ for (String timeZone : timeZoneList) {
+ timeZoneComboBox.addItem(timeZone);
}
- // get the current timezone
- TimeZone thisTimeZone = Calendar.getInstance().getTimeZone();
- int thisOffset = thisTimeZone.getRawOffset() / 1000;
- int thisHour = thisOffset / 3600;
- int thisMinutes = (thisOffset % 3600) / 60;
- String formatted = String.format("(GMT%+d:%02d) %s", thisHour, thisMinutes, thisTimeZone.getID());
// set the selected timezone
- timeZoneComboBox.setSelectedItem(formatted);
+ timeZoneComboBox.setSelectedItem(TimeZoneUtils.createTimeZoneString(Calendar.getInstance().getTimeZone()));
}
diff --git a/ImageGallery/manifest.mf b/ImageGallery/manifest.mf
index 38081388f4..21dea51481 100644
--- a/ImageGallery/manifest.mf
+++ b/ImageGallery/manifest.mf
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.imagegallery/2
-OpenIDE-Module-Implementation-Version: 4
+OpenIDE-Module-Implementation-Version: 5
OpenIDE-Module-Layer: org/sleuthkit/autopsy/imagegallery/layer.xml
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/imagegallery/Bundle.properties
diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties
index 1df8a62921..95eaf6042c 100644
--- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties
+++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties
@@ -11,4 +11,4 @@ ImageGalleryOptionsPanel.descriptionLabel.text=To minimize its startup tim
ImageGalleryOptionsPanel.furtherDescriptionArea.text=If Image Gallery is disabled, only the fact that an update is needed is recorded. If Image Gallery is enabled after ingest, it will do one bulk update based on the results from ingest. If Image Gallery is disabled, you will be prompted to enable it when attempting to open its window.
ImageGalleryOptionsPanel.unavailableDuringInjestLabel.text=This setting is unavailable during ingest.
ImageGalleryOptionsPanel.groupCategorizationWarningBox.text=Don't show a warning when overwriting categories, by acting on an entire group.
-CTL_OpenAction=Open Image/Video
\ No newline at end of file
+CTL_OpenAction=Open Image/Video
diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/FileTypeUtils.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/FileTypeUtils.java
index 91ce6d28f5..fb8a4d47ec 100644
--- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/FileTypeUtils.java
+++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/FileTypeUtils.java
@@ -219,8 +219,13 @@ public enum FileTypeUtils {
* type. False if a non image/video mimetype. empty Optional if a
* mimetype could not be detected.
*/
- static boolean hasDrawableMIMEType(AbstractFile file) throws FileTypeDetector.FileTypeDetectorInitException {
- String mimeType = getFileTypeDetector().getMIMEType(file).toLowerCase();
+ static boolean hasDrawableMIMEType(AbstractFile file) {
+ String mimeType = file.getMIMEType();
+ if (mimeType == null) {
+ return false;
+ }
+
+ mimeType = mimeType.toLowerCase();
return isDrawableMimeType(mimeType) || (mimeType.equals("audio/x-aiff") && "tiff".equalsIgnoreCase(file.getNameExtension()));
}
@@ -234,13 +239,13 @@ public enum FileTypeUtils {
* available, a video extension.
*/
public static boolean hasVideoMIMEType(AbstractFile file) {
- try {
- String mimeType = getFileTypeDetector().getMIMEType(file).toLowerCase();
- return mimeType.startsWith("video/") || videoMimeTypes.contains(mimeType);
- } catch (FileTypeDetector.FileTypeDetectorInitException ex) {
- LOGGER.log(Level.SEVERE, "Error determining MIME type of " + getContentPathSafe(file), ex);
+ String mimeType = file.getMIMEType();
+ if (mimeType == null) {
return false;
}
+
+ mimeType = mimeType.toLowerCase();
+ return mimeType.startsWith("video/") || videoMimeTypes.contains(mimeType);
}
/**
diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java
index 356ac5bdd1..666a2cd02d 100644
--- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java
+++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java
@@ -67,7 +67,6 @@ import org.sleuthkit.autopsy.imagegallery.datamodel.HashSetManager;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupManager;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
import org.sleuthkit.autopsy.ingest.IngestManager;
-import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase;
@@ -232,19 +231,19 @@ public final class ImageGalleryController {
dbTaskQueueSize.addListener(obs -> this.updateRegroupDisabled());
}
-
+
/**
* @return Currently displayed group or null if nothing is being displayed
*/
public GroupViewState getViewState() {
return historyManager.getCurrentState();
}
-
+
/**
* Get observable property of the current group. The UI currently changes
- * based on this property changing, which happens when other actions and
+ * based on this property changing, which happens when other actions and
* threads call advance().
- *
+ *
* @return Currently displayed group (as a property that can be observed)
*/
public ReadOnlyObjectProperty viewStateProperty() {
@@ -253,7 +252,8 @@ public final class ImageGalleryController {
/**
* Should the "forward" button on the history be enabled?
- * @return
+ *
+ * @return
*/
public ReadOnlyBooleanProperty getCanAdvance() {
return historyManager.getCanAdvance();
@@ -261,19 +261,19 @@ public final class ImageGalleryController {
/**
* Should the "Back" button on the history be enabled?
- * @return
+ *
+ * @return
*/
public ReadOnlyBooleanProperty getCanRetreat() {
return historyManager.getCanRetreat();
}
/**
- * Display the passed in group. Causes this group to
- * get recorded in the history queue and observers of the
- * current state will be notified and update their panels/widgets
- * appropriately.
- *
- * @param newState
+ * Display the passed in group. Causes this group to get recorded in the
+ * history queue and observers of the current state will be notified and
+ * update their panels/widgets appropriately.
+ *
+ * @param newState
*/
@ThreadConfined(type = ThreadConfined.ThreadType.ANY)
public void advance(GroupViewState newState) {
@@ -282,7 +282,8 @@ public final class ImageGalleryController {
/**
* Display the next group in the "forward" history stack
- * @return
+ *
+ * @return
*/
public GroupViewState advance() {
return historyManager.advance();
@@ -290,7 +291,8 @@ public final class ImageGalleryController {
/**
* Display the previous group in the "back" history stack
- * @return
+ *
+ * @return
*/
public GroupViewState retreat() {
return historyManager.retreat();
@@ -433,10 +435,6 @@ public final class ImageGalleryController {
return drawableDB.getFileFromID(fileID);
}
- public ReadOnlyDoubleProperty regroupProgress() {
- return groupManager.regroupProgress();
- }
-
public HashSetManager getHashSetManager() {
return hashSetManager;
}
@@ -696,8 +694,17 @@ public final class ImageGalleryController {
// Cycle through all of the files returned and call processFile on each
//do in transaction
drawableDbTransaction = taskDB.beginTransaction();
- caseDbTransaction = tskCase.beginTransaction();
+
+ /* We are going to periodically commit the CaseDB transaction
+ * and sleep so that the user can have Autopsy do other stuff
+ * while these bulk tasks are ongoing.
+ */
+ int caseDbCounter = 0;
for (final AbstractFile f : files) {
+ if (caseDbTransaction == null) {
+ caseDbTransaction = tskCase.beginTransaction();
+ }
+
if (isCancelled() || Thread.interrupted()) {
logger.log(Level.WARNING, "Task cancelled or interrupted: not all contents may be transfered to drawable database."); //NON-NLS
taskCompletionStatus = false;
@@ -712,6 +719,14 @@ public final class ImageGalleryController {
progressHandle.progress(f.getName(), workDone);
updateProgress(workDone - 1 / (double) files.size());
updateMessage(f.getName());
+
+ // Periodically, commit the transaction (which frees the lock) and sleep
+ // to allow other threads to get some work done in CaseDB
+ if ((++caseDbCounter % 200) == 0) {
+ caseDbTransaction.commit();
+ caseDbTransaction = null;
+ Thread.sleep(500); // 1/2 second
+ }
}
progressHandle.finish();
@@ -720,13 +735,16 @@ public final class ImageGalleryController {
updateProgress(1.0);
progressHandle.start();
- caseDbTransaction.commit();
- caseDbTransaction = null;
+ if (caseDbTransaction != null) {
+ caseDbTransaction.commit();
+ caseDbTransaction = null;
+ }
+
// pass true so that groupmanager is notified of the changes
taskDB.commitTransaction(drawableDbTransaction, true);
drawableDbTransaction = null;
- } catch (TskCoreException ex) {
+ } catch (TskCoreException | InterruptedException ex) {
progressHandle.progress(Bundle.BulkTask_stopCopy_status());
logger.log(Level.WARNING, "Stopping copy to drawable db task. Failed to transfer all database contents", ex); //NON-NLS
MessageNotifyUtil.Notify.warn(Bundle.BulkTask_errPopulating_errMsg(), ex.getMessage());
@@ -792,20 +810,16 @@ public final class ImageGalleryController {
if (known) {
taskDB.removeFile(f.getId(), tr); //remove known files
} else {
- try {
- // if mimetype of the file hasn't been ascertained, ingest might not have completed yet.
- if (null == f.getMIMEType()) {
- // set to false to force the DB to be marked as stale
- this.setTaskCompletionStatus(false);
- } //supported mimetype => analyzed
- else if (FileTypeUtils.hasDrawableMIMEType(f)) {
- taskDB.updateFile(DrawableFile.create(f, true, false), tr, caseDbTransaction);
- } //unsupported mimtype => analyzed but shouldn't include
- else {
- taskDB.removeFile(f.getId(), tr);
- }
- } catch (FileTypeDetector.FileTypeDetectorInitException ex) {
- throw new TskCoreException("Failed to initialize FileTypeDetector.", ex);
+ // if mimetype of the file hasn't been ascertained, ingest might not have completed yet.
+ if (null == f.getMIMEType()) {
+ // set to false to force the DB to be marked as stale
+ this.setTaskCompletionStatus(false);
+ } //supported mimetype => analyzed
+ else if (FileTypeUtils.hasDrawableMIMEType(f)) {
+ taskDB.updateFile(DrawableFile.create(f, true, false), tr, caseDbTransaction);
+ } //unsupported mimtype => analyzed but shouldn't include
+ else {
+ taskDB.removeFile(f.getId(), tr);
}
}
}
@@ -840,7 +854,7 @@ public final class ImageGalleryController {
@Override
void processFile(final AbstractFile f, DrawableDB.DrawableTransaction tr, CaseDbTransaction caseDBTransaction) {
- taskDB.insertFile(DrawableFile.create(f, false, false), tr, caseDBTransaction);
+ taskDB.insertBasicFileData(DrawableFile.create(f, false, false), tr, caseDBTransaction);
}
@Override
diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java
index 228b3765e5..c0336b76b1 100644
--- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java
+++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java
@@ -154,14 +154,14 @@ public class ImageGalleryModule {
IngestManager.getInstance().removeIngestModuleEventListener(this);
return;
}
-
+
/* only process individual files in realtime on the node that is
* running the ingest. on a remote node, image files are processed
* enblock when ingest is complete */
if (((AutopsyEvent) evt).getSourceType() != AutopsyEvent.SourceType.LOCAL) {
return;
}
-
+
// Bail out if the case is closed
try {
if (controller == null || Case.getCurrentCaseThrows() == null) {
@@ -208,8 +208,8 @@ public class ImageGalleryModule {
}
}
else if (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName()) == DATA_ADDED) {
- ModuleDataEvent mde = (ModuleDataEvent)evt.getOldValue();
-
+ ModuleDataEvent mde = (ModuleDataEvent) evt.getOldValue();
+
if (mde.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
DrawableDB drawableDB = controller.getDatabase();
if (mde.getArtifacts() != null) {
@@ -288,13 +288,13 @@ public class ImageGalleryModule {
break;
case CONTENT_TAG_ADDED:
final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) evt;
-
+
long objId = tagAddedEvent.getAddedTag().getContent().getId();
-
+
// update the cache
DrawableDB drawableDB = controller.getDatabase();
drawableDB.addTagCache(objId);
-
+
if (con.getDatabase().isInDB(objId)) {
con.getTagsManager().fireTagAddedEvent(tagAddedEvent);
}
@@ -336,21 +336,23 @@ public class ImageGalleryModule {
try {
ImageGalleryController con = getController();
con.setStale(true);
- if (con.isListeningEnabled() && ImageGalleryTopComponent.isImageGalleryOpen()) {
+ if (con.isListeningEnabled()) {
SwingUtilities.invokeLater(() -> {
- int showAnswer = JOptionPane.showConfirmDialog(ImageGalleryTopComponent.getTopComponent(),
- Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_msg(),
- Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_title(),
- JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
+ if (ImageGalleryTopComponent.isImageGalleryOpen()) {
+ int showAnswer = JOptionPane.showConfirmDialog(ImageGalleryTopComponent.getTopComponent(),
+ Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_msg(),
+ Bundle.ImageGalleryController_dataSourceAnalyzed_confDlg_title(),
+ JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
- switch (showAnswer) {
- case JOptionPane.YES_OPTION:
- con.rebuildDB();
- break;
- case JOptionPane.NO_OPTION:
- case JOptionPane.CANCEL_OPTION:
- default:
- break; //do nothing
+ switch (showAnswer) {
+ case JOptionPane.YES_OPTION:
+ con.rebuildDB();
+ break;
+ case JOptionPane.NO_OPTION:
+ case JOptionPane.CANCEL_OPTION:
+ default:
+ break; //do nothing
+ }
}
});
}
diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java
index 9911b1fb58..8c27f6cce5 100644
--- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java
+++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java
@@ -698,8 +698,8 @@ public final class DrawableDB {
// query to find the group id from attribute/value
return String.format(" SELECT group_id FROM " + GROUPS_TABLENAME
+ " WHERE attribute = \'%s\' AND value = \'%s\' AND data_source_obj_id = %d",
- groupKey.getAttribute().attrName.toString(),
- groupKey.getValueDisplayName(),
+ SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()),
+ SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()),
(groupKey.getAttribute() == DrawableAttribute.PATH) ? groupKey.getDataSourceObjId() : 0);
}
@@ -776,8 +776,8 @@ public final class DrawableDB {
// query to find the group id from attribute/value
String innerQuery = String.format("( SELECT group_id FROM " + GROUPS_TABLENAME
+ " WHERE attribute = \'%s\' AND value = \'%s\' and data_source_obj_id = %d )",
- groupKey.getAttribute().attrName.toString(),
- groupKey.getValueDisplayName(),
+ SleuthkitCase.escapeSingleQuotes(groupKey.getAttribute().attrName.toString()),
+ SleuthkitCase.escapeSingleQuotes(groupKey.getValueDisplayName()),
groupKey.getAttribute() == DrawableAttribute.PATH ? groupKey.getDataSourceObjId() : 0);
String insertSQL = String.format(" (group_id, examiner_id, seen) VALUES (%s, %d, %d)", innerQuery, examinerID, seen ? 1 : 0);
@@ -828,12 +828,26 @@ public final class DrawableDB {
}
- public void insertFile(DrawableFile f, DrawableTransaction tr, CaseDbTransaction caseDbTransaction) {
- insertOrUpdateFile(f, tr, insertFileStmt, caseDbTransaction);
+ /**
+ * Insert basic file data (no groups) into the DB during pre-population phase
+ * @param f
+ * @param tr
+ * @param caseDbTransaction
+ */
+ public void insertBasicFileData(DrawableFile f, DrawableTransaction tr, CaseDbTransaction caseDbTransaction) {
+ insertOrUpdateFile(f, tr, caseDbTransaction, false);
}
+ /**
+ * Update an existing entry (or make a new one) into the DB that includes group information.
+ * Called when a file has been analyzed or during a bulk rebuild
+ *
+ * @param f
+ * @param tr
+ * @param caseDbTransaction
+ */
public void updateFile(DrawableFile f, DrawableTransaction tr, CaseDbTransaction caseDbTransaction) {
- insertOrUpdateFile(f, tr, updateFileStmt, caseDbTransaction);
+ insertOrUpdateFile(f, tr, caseDbTransaction, true);
}
@@ -964,14 +978,24 @@ public final class DrawableDB {
*
* @param f The file to insert.
* @param tr a transaction to use, must not be null
- * @param stmt the statement that does the actual inserting
+ * @param caseDbTransaction
+ * @param addGroups True if groups for file should be inserted into db too
*/
- private void insertOrUpdateFile(DrawableFile f, @Nonnull DrawableTransaction tr, @Nonnull PreparedStatement stmt, @Nonnull CaseDbTransaction caseDbTransaction) {
+ private void insertOrUpdateFile(DrawableFile f, @Nonnull DrawableTransaction tr, @Nonnull CaseDbTransaction caseDbTransaction, boolean addGroups) {
+ PreparedStatement stmt;
+
if (tr.isClosed()) {
throw new IllegalArgumentException("can't update database with closed transaction");
}
+ // assume that we are doing an update if we are adding groups - i.e. not pre-populating
+ if (addGroups) {
+ stmt = updateFileStmt;
+ } else {
+ stmt = insertFileStmt;
+ }
+
// get data from caches. Default to true and force the DB lookup if we don't have caches
boolean hasExif = true;
boolean hasHashSet = true;
@@ -983,6 +1007,13 @@ public final class DrawableDB {
hasTag = hasTagCache.contains(f.getId());
}
}
+
+ // if we are going to just add basic data, then mark flags that we do not have metadata to prevent lookups
+ if (addGroups == false) {
+ hasExif = false;
+ hasHashSet = false;
+ hasTag = false;
+ }
dbWriteLock();
try {
@@ -1006,51 +1037,55 @@ public final class DrawableDB {
// Update the list of file IDs in memory
addImageFileToList(f.getId());
- // Update the hash set tables
- if (hasHashSet) {
- try {
- for (String name : f.getHashSetNames()) {
+ // update the groups if we are not doing pre-populating
+ if (addGroups) {
+
+ // Update the hash set tables
+ if (hasHashSet) {
+ try {
+ for (String name : f.getHashSetNames()) {
- // "insert or ignore into hash_sets (hash_set_name) values (?)"
- insertHashSetStmt.setString(1, name);
- insertHashSetStmt.executeUpdate();
+ // "insert or ignore into hash_sets (hash_set_name) values (?)"
+ insertHashSetStmt.setString(1, name);
+ insertHashSetStmt.executeUpdate();
- //TODO: use nested select to get hash_set_id rather than seperate statement/query
- //"select hash_set_id from hash_sets where hash_set_name = ?"
- selectHashSetStmt.setString(1, name);
- try (ResultSet rs = selectHashSetStmt.executeQuery()) {
- while (rs.next()) {
- int hashsetID = rs.getInt("hash_set_id"); //NON-NLS
- //"insert or ignore into hash_set_hits (hash_set_id, obj_id) values (?,?)";
- insertHashHitStmt.setInt(1, hashsetID);
- insertHashHitStmt.setLong(2, f.getId());
- insertHashHitStmt.executeUpdate();
- break;
+ //TODO: use nested select to get hash_set_id rather than seperate statement/query
+ //"select hash_set_id from hash_sets where hash_set_name = ?"
+ selectHashSetStmt.setString(1, name);
+ try (ResultSet rs = selectHashSetStmt.executeQuery()) {
+ while (rs.next()) {
+ int hashsetID = rs.getInt("hash_set_id"); //NON-NLS
+ //"insert or ignore into hash_set_hits (hash_set_id, obj_id) values (?,?)";
+ insertHashHitStmt.setInt(1, hashsetID);
+ insertHashHitStmt.setLong(2, f.getId());
+ insertHashHitStmt.executeUpdate();
+ break;
+ }
}
}
+ } catch (TskCoreException ex) {
+ logger.log(Level.SEVERE, "failed to insert/update hash hits for file" + f.getContentPathSafe(), ex); //NON-NLS
}
- } catch (TskCoreException ex) {
- logger.log(Level.SEVERE, "failed to insert/update hash hits for file" + f.getContentPathSafe(), ex); //NON-NLS
}
- }
- //and update all groups this file is in
- for (DrawableAttribute> attr : DrawableAttribute.getGroupableAttrs()) {
- // skip attributes that we do not have data for
- if ((attr == DrawableAttribute.TAGS) && (hasTag == false)) {
- continue;
- }
- else if ((attr == DrawableAttribute.MAKE || attr == DrawableAttribute.MODEL) && (hasExif == false)) {
- continue;
- }
- Collection extends Comparable>> vals = attr.getValue(f);
- for (Comparable> val : vals) {
- if (null != val) {
- if (attr == DrawableAttribute.PATH) {
- insertGroup(f.getAbstractFile().getDataSource().getId(), val.toString(), attr, caseDbTransaction);
- }
- else {
- insertGroup(val.toString(), attr, caseDbTransaction);
+ //and update all groups this file is in
+ for (DrawableAttribute> attr : DrawableAttribute.getGroupableAttrs()) {
+ // skip attributes that we do not have data for
+ if ((attr == DrawableAttribute.TAGS) && (hasTag == false)) {
+ continue;
+ }
+ else if ((attr == DrawableAttribute.MAKE || attr == DrawableAttribute.MODEL) && (hasExif == false)) {
+ continue;
+ }
+ Collection extends Comparable>> vals = attr.getValue(f);
+ for (Comparable> val : vals) {
+ if ((null != val) && (val.toString().isEmpty() == false)) {
+ if (attr == DrawableAttribute.PATH) {
+ insertGroup(f.getAbstractFile().getDataSource().getId(), val.toString(), attr, caseDbTransaction);
+ }
+ else {
+ insertGroup(val.toString(), attr, caseDbTransaction);
+ }
}
}
}
@@ -1408,7 +1443,7 @@ public final class DrawableDB {
try {
String insertSQL = String.format(" (data_source_obj_id, value, attribute) VALUES (%d, \'%s\', \'%s\')",
- ds_obj_id, value, groupBy.attrName.toString());
+ ds_obj_id, SleuthkitCase.escapeSingleQuotes(value), SleuthkitCase.escapeSingleQuotes(groupBy.attrName.toString()));
if (DbType.POSTGRESQL == tskCase.getDatabaseType()) {
insertSQL += " ON CONFLICT DO NOTHING";
diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/DrawableGroup.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/DrawableGroup.java
index bdb54c448f..5469afe4d3 100644
--- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/DrawableGroup.java
+++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/DrawableGroup.java
@@ -68,13 +68,6 @@ public class DrawableGroup implements Comparable {
DrawableGroup(GroupKey> groupKey, Set filesInGroup, boolean seen) {
this.groupKey = groupKey;
this.fileIDs.setAll(filesInGroup);
- fileIDs.addListener((ListChangeListener.Change extends Long> listchange) -> {
- boolean seenChanged = false;
- while (false == seenChanged && listchange.next()) {
- seenChanged |= listchange.wasAdded();
- }
- invalidateProperties(seenChanged);
- });
this.seen.set(seen);
}
@@ -183,15 +176,21 @@ public class DrawableGroup implements Comparable {
if (fileIDs.contains(f) == false) {
fileIDs.add(f);
}
+ // invalidate no matter what because the file could have new hash hits, etc.
+ invalidateProperties(true);
}
synchronized void setFiles(Set extends Long> newFileIds) {
fileIDs.removeIf(fileID -> newFileIds.contains(fileID) == false);
+ invalidateProperties(false);
newFileIds.stream().forEach(this::addFile);
}
synchronized void removeFile(Long f) {
- fileIDs.removeAll(f);
+ if (fileIDs.contains(f)) {
+ fileIDs.removeAll(f);
+ invalidateProperties(false);
+ }
}
private void invalidateProperties(boolean seenChanged) {
diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java
index 263e189b7d..bfd0fab496 100644
--- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java
+++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java
@@ -50,6 +50,7 @@ import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
+import javafx.beans.property.ReadOnlyStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Service;
@@ -266,14 +267,23 @@ public class GroupManager {
try {
Examiner examiner = controller.getSleuthKitCase().getCurrentExaminer();
getDrawableDB().markGroupSeen(group.getGroupKey(), seen, examiner.getId());
- group.setSeen(seen);
- updateUnSeenGroups(group);
+ // only update and reshuffle if its new results
+ if (group.isSeen() != seen) {
+ group.setSeen(seen);
+ updateUnSeenGroups(group);
+ }
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error marking group as seen", ex); //NON-NLS
}
});
}
+ /**
+ * Update unseenGroups list accordingly based on the current status of
+ * 'group'. Removes it if it is seen or adds it if it is unseen.
+ *
+ * @param group
+ */
synchronized private void updateUnSeenGroups(DrawableGroup group) {
if (group.isSeen()) {
unSeenGroups.removeAll(group);
@@ -496,6 +506,10 @@ public class GroupManager {
return regrouper.progressProperty();
}
+ public ReadOnlyStringProperty regroupMessage() {
+ return regrouper.messageProperty();
+ }
+
@Subscribe
synchronized public void handleTagAdded(ContentTagAddedEvent evt) {
GroupKey> newGroupKey = null;
@@ -530,15 +544,18 @@ public class GroupManager {
// NOTE: We assume that it has already been determined that GroupKey can be displayed based on Data Source filters
if (group == null) {
- //if there wasn't already a group check if there should be one now
- // path group, for example, only gets created when all files are analyzed
+ //if there wasn't already a DrawableGroup, then check if this group is now
+ // in an appropriate state to get one made.
+ // Path group, for example, only gets a DrawableGroup created when all files are analyzed
group = popuplateIfAnalyzed(groupKey, null);
} else {
//if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it.
group.addFile(fileID);
}
// reset the seen status for the group
- markGroupSeen(group, false);
+ if (group != null) {
+ markGroupSeen(group, false);
+ }
}
@Subscribe
@@ -607,6 +624,8 @@ public class GroupManager {
* If the group is analyzed (or other criteria based on grouping) and should
* be shown to the user, then add it to the appropriate data structures so
* that it can be viewed.
+ *
+ * @returns null if Group is not ready to be viewed
*/
synchronized private DrawableGroup popuplateIfAnalyzed(GroupKey> groupKey, ReGroupTask> task) {
/*
@@ -716,12 +735,7 @@ public class GroupManager {
*/
@SuppressWarnings({"unchecked", "rawtypes"})
@NbBundle.Messages({"# {0} - groupBy attribute Name",
- "# {1} - sortBy name",
- "# {2} - sort Order",
- "ReGroupTask.displayTitle=regrouping files by {0} sorted by {1} in {2} order",
- "# {0} - groupBy attribute Name",
- "# {1} - atribute value",
- "ReGroupTask.progressUpdate=regrouping files by {0} : {1}"})
+ "ReGroupTask.displayTitle=regrouping by {0}: " })
class ReGroupTask> extends LoggedTask {
private final DataSource dataSource;
@@ -729,16 +743,14 @@ public class GroupManager {
private final GroupSortBy sortBy;
private final SortOrder sortOrder;
- private final ProgressHandle groupProgress;
-
ReGroupTask(DataSource dataSource, DrawableAttribute groupBy, GroupSortBy sortBy, SortOrder sortOrder) {
- super(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString(), sortBy.getDisplayName(), sortOrder.toString()), true);
+ super(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString() ), true);
this.dataSource = dataSource;
this.groupBy = groupBy;
this.sortBy = sortBy;
this.sortOrder = sortOrder;
- groupProgress = ProgressHandle.createHandle(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString(), sortBy.getDisplayName(), sortOrder.toString()), this);
+ updateTitle(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString() ));
}
@Override
@@ -747,7 +759,8 @@ public class GroupManager {
if (isCancelled()) {
return null;
}
- groupProgress.start();
+
+ updateProgress(-1, 1);
analyzedGroups.clear();
unSeenGroups.clear();
@@ -755,7 +768,7 @@ public class GroupManager {
// Get the list of group keys
Multimap valsByDataSource = findValuesForAttribute();
- groupProgress.switchToDeterminate(valsByDataSource.entries().size());
+ updateProgress(0, valsByDataSource.entries().size());
int p = 0;
// For each key value, partially create the group and add it to the list.
for (final Map.Entry valForDataSource : valsByDataSource.entries()) {
@@ -763,9 +776,8 @@ public class GroupManager {
return null;
}
p++;
- updateMessage(Bundle.ReGroupTask_progressUpdate(groupBy.attrName.toString(), valForDataSource.getValue()));
+ updateMessage(Bundle.ReGroupTask_displayTitle(groupBy.attrName.toString()) + valForDataSource.getValue());
updateProgress(p, valsByDataSource.size());
- groupProgress.progress(Bundle.ReGroupTask_progressUpdate(groupBy.attrName.toString(), valForDataSource), p);
popuplateIfAnalyzed(new GroupKey<>(groupBy, valForDataSource.getValue(), valForDataSource.getKey()), this);
}
@@ -794,8 +806,8 @@ public class GroupManager {
}
}
} finally {
- groupProgress.finish();
updateProgress(1, 1);
+ updateMessage("");
}
return null;
}
@@ -813,12 +825,9 @@ public class GroupManager {
}
/**
- * find the distinct values for the given column (DrawableAttribute)
- *
+ * Find the distinct values for the given column (DrawableAttribute).
* These values represent the groups of files.
*
- * @param groupBy
- *
* @return map of data source (or null if group by attribute ignores
* data sources) to list of unique group values
*/
diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.fxml b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.fxml
index b02e2e2496..c0ee4488b1 100644
--- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.fxml
+++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.fxml
@@ -10,12 +10,21 @@
-
+
+
@@ -31,37 +40,27 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.java
index 2355d6d2a0..d7224c4a58 100644
--- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.java
+++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.java
@@ -28,28 +28,26 @@ import javafx.scene.control.Tooltip;
import javafx.scene.layout.AnchorPane;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
+import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupManager;
/**
*
*/
public class StatusBar extends AnchorPane {
- private final ImageGalleryController controller;
-
@FXML
private ProgressBar fileTaskProgresBar;
-
@FXML
private Label fileUpdateTaskLabel;
-
@FXML
- private Label bgTaskLabel;
-
+ private Label regroupLabel;
@FXML
private Label staleLabel;
-
@FXML
- private ProgressBar bgTaskProgressBar;
+ private ProgressBar regroupProgressBar;
+
+ private final ImageGalleryController controller;
+ private final GroupManager groupManager;
@FXML
@NbBundle.Messages({"StatusBar.fileUpdateTaskLabel.text= File Update Tasks",
@@ -58,23 +56,25 @@ public class StatusBar extends AnchorPane {
void initialize() {
assert fileTaskProgresBar != null : "fx:id=\"fileTaskProgresBar\" was not injected: check your FXML file 'StatusBar.fxml'.";
assert fileUpdateTaskLabel != null : "fx:id=\"fileUpdateTaskLabel\" was not injected: check your FXML file 'StatusBar.fxml'.";
- assert bgTaskLabel != null : "fx:id=\"bgTaskLabel\" was not injected: check your FXML file 'StatusBar.fxml'.";
- assert bgTaskProgressBar != null : "fx:id=\"bgTaskProgressBar\" was not injected: check your FXML file 'StatusBar.fxml'.";
+ assert regroupLabel != null : "fx:id=\"regroupLabel\" was not injected: check your FXML file 'StatusBar.fxml'.";
+ assert regroupProgressBar != null : "fx:id=\"regroupProgressBar\" was not injected: check your FXML file 'StatusBar.fxml'.";
fileUpdateTaskLabel.textProperty().bind(controller.getDBTasksQueueSizeProperty().asString().concat(Bundle.StatusBar_fileUpdateTaskLabel_text()));
fileTaskProgresBar.progressProperty().bind(controller.getDBTasksQueueSizeProperty().negate());
- controller.regroupProgress().addListener((ov, oldSize, newSize) -> {
+ groupManager.regroupProgress().addListener((ov, oldSize, newSize) -> {
Platform.runLater(() -> {
- if (controller.regroupProgress().lessThan(1.0).get()) {
+ if (groupManager.regroupProgress().lessThan(1.0).get()) {
// Regrouping in progress
- bgTaskProgressBar.progressProperty().setValue(-1.0);
- bgTaskLabel.setText(Bundle.StatusBar_bgTaskLabel_text());
+ regroupProgressBar.progressProperty().setValue(groupManager.regroupProgress().doubleValue());
+ regroupLabel.setText(groupManager.regroupMessage().get());
+
} else {
// Clear the progress bar
- bgTaskProgressBar.progressProperty().setValue(0.0);
- bgTaskLabel.setText("");
+ regroupProgressBar.progressProperty().setValue(0.0);
+ regroupLabel.setText("");
}
+ regroupLabel.setTooltip(new Tooltip(regroupLabel.getText()));
});
});
@@ -84,6 +84,7 @@ public class StatusBar extends AnchorPane {
public StatusBar(ImageGalleryController controller) {
this.controller = controller;
+ this.groupManager = controller.getGroupManager();
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("StatusBar.fxml")); //NON-NLS
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
@@ -93,6 +94,5 @@ public class StatusBar extends AnchorPane {
} catch (IOException exception) {
throw new RuntimeException(exception);
}
-
}
}
diff --git a/NEWS.txt b/NEWS.txt
index f0414d48ba..6130ca50da 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -1,3 +1,9 @@
+---------------- VERSION 4.9.1 --------------
+Bug Fixes:
+- Fixed possible ingest deadlock from Image Gallery database inserts.
+- Image Gallery does not need lock on Case DB during pre-population, which makes UI more responsive.
+- Other misc Image Gallery fixes.
+
---------------- VERSION 4.9.0 --------------
New Features:
diff --git a/RecentActivity/nbproject/project.xml b/RecentActivity/nbproject/project.xml
index 0f0c5c3aa9..0ab85c3bd0 100644
--- a/RecentActivity/nbproject/project.xml
+++ b/RecentActivity/nbproject/project.xml
@@ -73,9 +73,7 @@
-
- org.sleuthkit.autopsy.recentactivity
-
+
ext/gson-2.1.jar
release/modules/ext/gson-2.1.jar
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java
index 2cff40124d..840d154db2 100644
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java
@@ -37,6 +37,7 @@ import java.util.*;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
@@ -309,7 +310,7 @@ final class Chrome extends Extract {
} else {
date = Long.valueOf(0);
}
- String domain = Util.extractDomain(url);
+ String domain = NetworkUtils.extractDomain(url);
try {
Collection bbattributes = Arrays.asList(
new BlackboardAttribute(
@@ -520,6 +521,7 @@ final class Chrome extends Extract {
try {
BlackboardArtifact bbart = downloadFile.newArtifact(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD);
bbart.addAttributes(bbattributes);
+
bbartifacts.add(bbart);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error while trying to insert Chrome download artifact.", ex); //NON-NLS
@@ -589,6 +591,7 @@ final class Chrome extends Extract {
List> tempList = this.dbConnect(temps, LOGIN_QUERY);
logger.log(Level.INFO, "{0}- Now getting login information from {1} with {2}artifacts identified.", new Object[]{getModuleName(), temps, tempList.size()}); //NON-NLS
for (HashMap result : tempList) {
+
Collection bbattributes = Arrays.asList(
new BlackboardAttribute(
TSK_URL, PARENT_MODULE_NAME,
@@ -618,6 +621,7 @@ final class Chrome extends Extract {
try {
BlackboardArtifact bbart = signonFile.newArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY);
bbart.addAttributes(bbattributes);
+
bbartifacts.add(bbart);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error while trying to insert Chrome login artifact.", ex); //NON-NLS
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java
index 570071bb16..ea3b8433c7 100644
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java
@@ -159,6 +159,7 @@ class ExtractIE extends Extract {
try {
BlackboardArtifact bbart = fav.newArtifact(ARTIFACT_TYPE.TSK_WEB_BOOKMARK);
bbart.addAttributes(bbattributes);
+
bbartifacts.add(bbart);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error while trying to create Internet Explorer bookmark artifact.", ex); //NON-NLS
@@ -596,6 +597,7 @@ class ExtractIE extends Extract {
}
/**
+ *
* Determine if the URL should be ignored.
*
* @param url The URL to test.
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/FirefoxExtractor.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/FirefoxExtractor.java
index 74f7577870..9820f93f04 100644
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/FirefoxExtractor.java
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/FirefoxExtractor.java
@@ -22,14 +22,12 @@
*/
package org.sleuthkit.autopsy.recentactivity;
-import com.google.common.collect.Lists;
import static com.google.common.collect.Lists.newArrayList;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -152,7 +150,7 @@ final class FirefoxExtractor extends Extract {
}
List> tempList = this.dbConnect(temps, HISTORY_QUERY);
logger.log(Level.INFO, "{0} - Now getting history from {1} with {2} artifacts identified.", new Object[]{getModuleName(), temps, tempList.size()}); //NON-NLS
- for (HashMap result : tempList) {
+ for (HashMap result : tempList) {
String url = Objects.toString(result.get("url"), "");
Collection bbattributes = newArrayList(
new BlackboardAttribute(
@@ -169,7 +167,7 @@ final class FirefoxExtractor extends Extract {
Objects.toString(result.get("title"), "")), //NON-NLS
new BlackboardAttribute(
TSK_PROG_NAME, PARENT_MODULE_NAME,
- getModuleName()));
+ getModuleName()));
if (isIgnoredUrl(url) == false) {
bbattributes.add(new BlackboardAttribute(
@@ -249,7 +247,7 @@ final class FirefoxExtractor extends Extract {
}
List> tempList = this.dbConnect(temps, BOOKMARK_QUERY);
logger.log(Level.INFO, "{0} - Now getting bookmarks from {1} with {2} artifacts identified.", new Object[]{getModuleName(), temps, tempList.size()}); //NON-NLS
- for (HashMap result : tempList) {
+ for (HashMap result : tempList) {
String url = Objects.toString(result.get("url"), "");
Collection bbattributes = newArrayList(
@@ -266,7 +264,7 @@ final class FirefoxExtractor extends Extract {
if (isIgnoredUrl(url) == false) {
bbattributes.add(new BlackboardAttribute(
TSK_DOMAIN, PARENT_MODULE_NAME,
- Util.extractDomain(url)));
+ Util.extractDomain(url)));
}
Long createdTime = Long.valueOf(result.get("dateAdded").toString());
if (createdTime > 0) { //NON-NLS
@@ -351,7 +349,7 @@ final class FirefoxExtractor extends Extract {
List> tempList = this.dbConnect(temps, query);
logger.log(Level.INFO, "{0} - Now getting cookies from {1} with {2} artifacts identified.", new Object[]{getModuleName(), temps, tempList.size()}); //NON-NLS
- for (HashMap result : tempList) {
+ for (HashMap result : tempList) {
String host = Objects.toString(result.get("host"), "");
Collection bbattributes = newArrayList(
@@ -374,7 +372,7 @@ final class FirefoxExtractor extends Extract {
if (isIgnoredUrl(host) == false) {
bbattributes.add(new BlackboardAttribute(
TSK_DOMAIN, PARENT_MODULE_NAME,
- Util.extractDomain(host.replaceFirst("^\\.+(?!$)", ""))));//NON-NLS
+ Util.extractDomain(host.replaceFirst("^\\.+(?!$)", ""))));//NON-NLS
}
if (checkColumn) {
bbattributes.add(new BlackboardAttribute(
@@ -491,10 +489,9 @@ final class FirefoxExtractor extends Extract {
logger.log(Level.SEVERE, "Error decoding Firefox download URL in " + temps, ex); //NON-NLS
errors++;
}
- try {
-
+ try {
BlackboardArtifact bbart = downloadsFile.newArtifact(TSK_WEB_DOWNLOAD);
- bbart.addAttributes(bbattributes);
+ bbart.addAttributes(bbattributes);
bbartifacts.add(bbart);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error while trying to create Firefox download artifact.", ex); //NON-NLS
@@ -607,10 +604,10 @@ final class FirefoxExtractor extends Extract {
bbattributes.add(new BlackboardAttribute(
TSK_PATH_ID, PARENT_MODULE_NAME,
pathID));
- }
+ }
} catch (UnsupportedEncodingException ex) {
logger.log(Level.SEVERE, "Error decoding Firefox download URL in " + temps, ex); //NON-NLS
- errors++;
+ errors++;
}
try {
@@ -642,10 +639,11 @@ final class FirefoxExtractor extends Extract {
}
/**
+
* Determine if the URL should be ignored.
*
* @param url The URL to test.
- *
+ *s
* @return True if the URL should be ignored; otherwise false.
*/
private boolean isIgnoredUrl(String url) {
@@ -653,6 +651,6 @@ final class FirefoxExtractor extends Extract {
* Ignore blank URLS and URLs that begin with the matched text.
*/
return StringUtils.isBlank(url)
- || url.toLowerCase().startsWith(PLACE_URL_PREFIX);
+ || url.toLowerCase().startsWith(PLACE_URL_PREFIX);
}
}
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java
index 3e4ff1cc4d..a869f1ba05 100644
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java
@@ -50,11 +50,11 @@ import org.sleuthkit.datamodel.TskCoreException;
/**
*
- * @author Alex
+ *
*/
class Util {
- private static Logger logger = Logger.getLogger(Util.class.getName());
+ private static final Logger logger = Logger.getLogger(Util.class.getName());
private Util() {
}
@@ -83,8 +83,10 @@ class Util {
}
/**
- //JIRA-2384: There is no utility in apache or guave to do this for us?
+ * //JIRA-2384: There is no utility in apache or guave to do this for us?
+ *
* @param url
+ *
* @return empty string if no domain could be found
*/
private static String getBaseDomain(String url) {
@@ -110,8 +112,7 @@ class Util {
hostB.append(".");
}
}
-
-
+
String base = hostB.toString();
// verify there are no special characters in there
if (base.matches(".*[~`!@#$%^&\\*\\(\\)\\+={}\\[\\];:\\?<>,/ ].*")) {
@@ -121,8 +122,9 @@ class Util {
}
/**
- *
+ *
* @param value
+ *
* @return empty string if no domain name was found
*/
public static String extractDomain(String value) {
@@ -156,18 +158,8 @@ class Util {
}
public static String getFileName(String value) {
- String filename = "";
- String filematch = "^([a-zA-Z]\\:)(\\\\[^\\\\/:*?<>\"|]*(?
-
+
diff --git a/docs/doxygen-user/Doxyfile b/docs/doxygen-user/Doxyfile
index 21e14a3e46..f059ec6a9d 100755
--- a/docs/doxygen-user/Doxyfile
+++ b/docs/doxygen-user/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy User Documentation"
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = 4.9.0
+PROJECT_NUMBER = 4.9.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile
index e83d4b6735..30e1b38fff 100755
--- a/docs/doxygen/Doxyfile
+++ b/docs/doxygen/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy"
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = 4.9.0
+PROJECT_NUMBER = 4.9.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears a the top of each page and should give viewer a
diff --git a/nbproject/project.properties b/nbproject/project.properties
index 6b846f5808..8f3f577177 100644
--- a/nbproject/project.properties
+++ b/nbproject/project.properties
@@ -4,10 +4,10 @@ app.title=Autopsy
### lowercase version of above
app.name=${branding.token}
### if left unset, version will default to today's date
-app.version=4.9.0
+app.version=4.9.1
### build.type must be one of: DEVELOPMENT, RELEASE
-#build.type=RELEASE
-build.type=DEVELOPMENT
+build.type=RELEASE
+#build.type=DEVELOPMENT
project.org.netbeans.progress=org-netbeans-api-progress
project.org.sleuthkit.autopsy.experimental=Experimental
diff --git a/release/update_autopsy_version.pl b/release/update_autopsy_version.pl
new file mode 100755
index 0000000000..0a115c8daf
--- /dev/null
+++ b/release/update_autopsy_version.pl
@@ -0,0 +1,268 @@
+#!/usr/bin/perl
+
+# Updates various Autopsy version numbers
+
+use strict;
+use File::Copy;
+
+# global variables
+my $VER;
+
+
+my $TESTING = 0;
+print "TESTING MODE (no commits)\n" if ($TESTING);
+
+
+
+sub main {
+
+ # Get the Autopsy version argument
+ if (scalar (@ARGV) != 1) {
+ print stderr "Missing release version argument (i.e. 4.9.0)\n";
+ exit;
+ }
+
+ $VER = $ARGV[0];
+ die "Invalid version number: $VER (1.2.3 or 1.2.3b1 expected)" unless ($VER =~ /^\d+\.\d+\.\d+(b\d+)?$/);
+
+
+ my $AUT_RELNAME = "autopsy-${VER}";
+ # Verify the tag doesn't already exist
+ exec_pipe(*OUT, "git tag | grep \"${AUT_RELNAME}\$\"");
+ my $foo = read_pipe_line(*OUT);
+ if ($foo ne "") {
+ print "Tag ${AUT_RELNAME} already exists\n";
+ print "Remove with 'git tag -d ${AUT_RELNAME}'\n";
+ die "stopping";
+ }
+ close(OUT);
+
+ # Assume we running out of 'release' folder
+ chdir ".." or die "Error changing directories to root";
+
+
+ # verify_precheckin();
+
+
+ # Update the version info in that tag
+ update_project_properties();
+ update_doxygen_dev();
+ update_doxygen_user();
+
+ print "Files updated. You need to commit and push them\n";
+}
+
+
+
+
+
+######################################################
+# Utility functions
+
+
+# Function to execute a command and send output to pipe
+# returns handle
+# exec_pipe(HANDLE, CMD);
+sub exec_pipe {
+ my $handle = shift(@_);
+ my $cmd = shift(@_);
+
+ die "Can't open pipe for exec_pipe"
+ unless defined(my $pid = open($handle, '-|'));
+
+ if ($pid) {
+ return $handle;
+ }
+ else {
+ $| = 1;
+ exec("$cmd") or die "Can't exec program: $!";
+ }
+}
+
+# Read a line of text from an open exec_pipe handle
+sub read_pipe_line {
+ my $handle = shift(@_);
+ my $out;
+
+ for (my $i = 0; $i < 100; $i++) {
+ $out = <$handle>;
+ return $out if (defined $out);
+ }
+ return $out;
+}
+
+
+# Prompt user for argument and return response
+sub prompt_user {
+ my $q = shift(@_);
+ print "$q: ";
+ $| = 1;
+ $_ = ;
+ chomp;
+ return $_;
+}
+
+
+
+#############################################
+# File update methods
+
+
+
+# Verify that all files in the current source directory
+# are checked in. dies if any are modified.
+sub verify_precheckin {
+
+ #system ("git pull");
+
+ print "Verifying everything is checked in\n";
+ exec_pipe(*OUT, "git status -s | grep \"^ M\"");
+
+ my $foo = read_pipe_line(*OUT);
+ if ($foo ne "") {
+ print "Files not checked in\n";
+ while ($foo ne "") {
+ print "$foo";
+ $foo = read_pipe_line(*OUT);
+ }
+ die "stopping" unless ($TESTING);
+ }
+ close(OUT);
+
+ print "Verifying everything is pushed\n";
+ exec_pipe(*OUT, "git status -sb | grep \"^##\" | grep \"ahead \"");
+ my $foo = read_pipe_line(*OUT);
+ if ($foo ne "") {
+ print "$foo";
+ print "Files not pushed to remote\n";
+ die "stopping" unless ($TESTING);
+ }
+ close(OUT);
+}
+
+
+
+# update the version in nbproject/project.properties
+sub update_project_properties {
+
+ my $orig = "project.properties";
+ my $temp = "${orig}-bak";
+
+ print "Updating the version in ${orig}\n";
+
+ chdir "nbproject" or die "cannot change into nbproject directory";
+
+
+ open (CONF_IN, "<${orig}") or die "Cannot open ${orig}";
+ open (CONF_OUT, ">${temp}") or die "Cannot open ${temp}";
+
+ my $found = 0;
+ while () {
+ if (/^app\.version=/) {
+ print CONF_OUT "app.version=$VER\n";
+ $found++;
+ }
+ else {
+ print CONF_OUT $_;
+ }
+ }
+ close (CONF_IN);
+ close (CONF_OUT);
+
+ if ($found != 1) {
+ die "$found (instead of 1) occurrences of app.version found in ${orig}";
+ }
+
+ unlink ($orig) or die "Error deleting ${orig}";
+ rename ($temp, $orig) or die "Error renaming tmp $orig file";
+ system("git add ${orig}") unless ($TESTING);
+ chdir ".." or die "Error changing directories back to root";
+}
+
+
+
+# update the dev docs
+sub update_doxygen_dev {
+
+ my $orig = "Doxyfile";
+ my $temp = "${orig}-bak";
+
+ print "Updating the version in ${orig} (Dev)\n";
+
+ chdir "docs/doxygen" or die "cannot change into docs/doxygen directory";
+
+
+ open (CONF_IN, "<${orig}") or die "Cannot open ${orig}";
+ open (CONF_OUT, ">${temp}") or die "Cannot open ${temp}";
+
+ my $found = 0;
+ while () {
+ if (/^PROJECT_NUMBER/) {
+ print CONF_OUT "PROJECT_NUMBER = ${VER}\n";
+ $found++;
+ }
+ elsif (/^HTML_OUTPUT/) {
+ print CONF_OUT "HTML_OUTPUT = api-docs/${VER}/\n";
+ $found++;
+ }
+ else {
+ print CONF_OUT $_;
+ }
+ }
+ close (CONF_IN);
+ close (CONF_OUT);
+
+ if ($found != 2) {
+ die "$found (instead of 2) occurrences of version found in (DEV) ${orig}";
+ }
+
+ unlink ($orig) or die "Error deleting ${orig}";
+ rename ($temp, $orig) or die "Error renaming tmp $orig file";
+ system("git add ${orig}") unless ($TESTING);
+ chdir "../.." or die "Error changing directories back to root";
+}
+
+
+# update the user docs
+sub update_doxygen_user {
+
+ my $orig = "Doxyfile";
+ my $temp = "${orig}-bak";
+
+ print "Updating the version in ${orig} (User)\n";
+
+ chdir "docs/doxygen-user" or die "cannot change into docs/doxygen-user directory";
+
+
+ open (CONF_IN, "<${orig}") or die "Cannot open ${orig}";
+ open (CONF_OUT, ">${temp}") or die "Cannot open ${temp}";
+
+ my $found = 0;
+ while () {
+ if (/^PROJECT_NUMBER/) {
+ print CONF_OUT "PROJECT_NUMBER = ${VER}\n";
+ $found++;
+ }
+ elsif (/^HTML_OUTPUT/) {
+ print CONF_OUT "HTML_OUTPUT = ${VER}\n";
+ $found++;
+ }
+ else {
+ print CONF_OUT $_;
+ }
+ }
+ close (CONF_IN);
+ close (CONF_OUT);
+
+ if ($found != 2) {
+ die "$found (instead of 2) occurrences of version found in (USER) ${orig}";
+ }
+
+ unlink ($orig) or die "Error deleting ${orig}";
+ rename ($temp, $orig) or die "Error renaming tmp $orig file";
+ system("git add ${orig}") unless ($TESTING);
+ chdir "../.." or die "Error changing directories back to root";
+}
+
+
+main();
\ No newline at end of file
diff --git a/release/update_sleuthkit_version.pl b/release/update_sleuthkit_version.pl
new file mode 100755
index 0000000000..e630e4890b
--- /dev/null
+++ b/release/update_sleuthkit_version.pl
@@ -0,0 +1,199 @@
+#!/usr/bin/perl
+
+# Updates various TSK version numbers
+# use this when the version of TSK that Autopsy depends on changes
+
+use strict;
+use File::Copy;
+
+# global variables
+my $VER;
+
+my $TESTING = 0;
+print "TESTING MODE (no commits)\n" if ($TESTING);
+
+
+sub main {
+
+ # Get the TSK version argument
+ if (scalar (@ARGV) != 1) {
+ print stderr "Missing release version argument (i.e. 4.9.0)\n";
+ exit;
+ }
+
+ $VER = $ARGV[0];
+ die "Invalid version number: $VER (1.2.3 or 1.2.3b1 expected)" unless ($VER =~ /^\d+\.\d+\.\d+(b\d+)?$/);
+
+ # Assume we running out of 'release' folder
+ chdir ".." or die "Error changing directories to root";
+
+ # Update the version info in that tag
+ update_tsk_version();
+ update_core_project_properties();
+ update_core_project_xml();
+
+ print "Files updated. You need to commit and push them\n";
+}
+
+
+
+
+
+######################################################
+# Utility functions
+
+
+# Function to execute a command and send output to pipe
+# returns handle
+# exec_pipe(HANDLE, CMD);
+sub exec_pipe {
+ my $handle = shift(@_);
+ my $cmd = shift(@_);
+
+ die "Can't open pipe for exec_pipe"
+ unless defined(my $pid = open($handle, '-|'));
+
+ if ($pid) {
+ return $handle;
+ }
+ else {
+ $| = 1;
+ exec("$cmd") or die "Can't exec program: $!";
+ }
+}
+
+# Read a line of text from an open exec_pipe handle
+sub read_pipe_line {
+ my $handle = shift(@_);
+ my $out;
+
+ for (my $i = 0; $i < 100; $i++) {
+ $out = <$handle>;
+ return $out if (defined $out);
+ }
+ return $out;
+}
+
+
+
+#############################################
+# File update methods
+
+
+
+# update the tskversion.xml
+sub update_tsk_version {
+
+ my $orig = "TSKVersion.xml";
+ my $temp = "${orig}-bak";
+
+ print "Updating the version in ${orig}\n";
+
+ open (CONF_IN, "<${orig}") or die "Cannot open ${orig}";
+ open (CONF_OUT, ">${temp}") or die "Cannot open ${temp}";
+
+ my $found = 0;
+ while () {
+ if (/name="TSK_VERSION" value=/) {
+ print CONF_OUT " \n";
+ $found++;
+ }
+ else {
+ print CONF_OUT $_;
+ }
+ }
+ close (CONF_IN);
+ close (CONF_OUT);
+
+ if ($found != 1) {
+ die "$found (instead of 1) occurrences of app.version found in ${orig}";
+ }
+
+ unlink ($orig) or die "Error deleting ${orig}";
+ rename ($temp, $orig) or die "Error renaming tmp $orig file";
+ system("git add ${orig}") unless ($TESTING);
+
+}
+
+
+
+sub update_core_project_properties {
+
+ my $orig = "project.properties";
+ my $temp = "${orig}-bak";
+
+ print "Updating the version in ${orig}\n";
+
+ chdir "Core/nbproject" or die "cannot change into Core/nbproject directory";
+
+
+ open (CONF_IN, "<${orig}") or die "Cannot open ${orig}";
+ open (CONF_OUT, ">${temp}") or die "Cannot open ${temp}";
+
+ my $found = 0;
+ while () {
+ if (/^file\.reference\.sleuthkit\-postgresql-/) {
+ print CONF_OUT "file.reference.sleuthkit-postgresql-${VER}.jar=release/modules/ext/sleuthkit-postgresql-${VER}.jar\n";
+ $found++;
+ }
+
+ else {
+ print CONF_OUT $_;
+ }
+ }
+ close (CONF_IN);
+ close (CONF_OUT);
+
+ if ($found != 1) {
+ die "$found (instead of 1) occurrences of version found in ${orig}";
+ }
+
+ unlink ($orig) or die "Error deleting ${orig}";
+ rename ($temp, $orig) or die "Error renaming tmp $orig file";
+ system("git add ${orig}") unless ($TESTING);
+ chdir "../.." or die "Error changing directories back to root";
+}
+
+sub update_core_project_xml {
+
+ my $orig = "project.xml";
+ my $temp = "${orig}-bak";
+
+ print "Updating the version in ${orig}\n";
+
+ chdir "Core/nbproject" or die "cannot change into Core/nbproject directory";
+
+ open (CONF_IN, "<${orig}") or die "Cannot open ${orig}";
+ open (CONF_OUT, ">${temp}") or die "Cannot open ${temp}";
+
+ my $found = 0;
+ while () {
+ if (/ext\/sleuthkit-postgresql/) {
+ print CONF_OUT " ext/sleuthkit-postgresql-${VER}.jar\n";
+ $found++;
+ }
+ elsif (/release\/modules\/ext\/sleuthkit-postgresql/) {
+ print CONF_OUT " release/modules/ext/sleuthkit-postgresql-${VER}.jar\n";
+ $found++;
+ }
+ else {
+ print CONF_OUT $_;
+ }
+ }
+ close (CONF_IN);
+ close (CONF_OUT);
+
+ if ($found != 2) {
+ die "$found (instead of 2) occurrences of version found in ${orig}";
+ }
+
+ unlink ($orig) or die "Error deleting ${orig}";
+ rename ($temp, $orig) or die "Error renaming tmp $orig file";
+ system("git add ${orig}") unless ($TESTING);
+ chdir "../.." or die "Error changing directories back to root";
+}
+
+
+
+
+main();
\ No newline at end of file