diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index dcb2af3eef7c7c1220fd27a8ac2a7701a2bad6cf..eaaa8f992649d8761e43f647543cad23668ee3ae 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -116,6 +116,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; 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. @@ -707,11 +708,15 @@ public static void deleteCurrentCase() throws CaseActionException { "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 @@ -733,10 +738,19 @@ public static void deleteCase(CaseMetadata metadata) throws CaseActionException * 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); } } @@ -946,11 +960,13 @@ static Map<Long, String> getImagePaths(SleuthkitCase db) { "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; @@ -960,10 +976,14 @@ private static void deleteCase(CaseMetadata metadata, ProgressIndicator progress 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())); } } @@ -973,10 +993,16 @@ private static void deleteCase(CaseMetadata metadata, ProgressIndicator progress 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())); } } @@ -984,9 +1010,16 @@ private static void deleteCase(CaseMetadata metadata, ProgressIndicator progress * 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())); } /* @@ -1540,11 +1573,13 @@ public void notifyTagDefinitionChanged(String changedTagName) { } /** - * 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) { @@ -1800,7 +1835,7 @@ private void open(boolean isNewCase) throws CaseActionException { 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()); @@ -2375,7 +2410,7 @@ private void closeAppServiceCaseResources() { * @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/LocalFilesDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java index 8d6dcffdd9ffe77f48486ea304ab5b8c9c70c4b2..4d5e3eac36152fb260a42e47347f91f953889301 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java @@ -369,7 +369,7 @@ public int canProcess(Path dataSourcePath) throws AutoIngestDataSourceProcessorE @Override public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) { List<String> 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/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index c6301bfb89322770ae7cb15e97ee56a4b48ebecb..0080ade4fbe1cdbb3c227eb02eedf23cb1ca9c04 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -127,7 +127,7 @@ public void actionPerformed(ActionEvent e) { } } else if (jmi.equals(showCommonalityMenuItem)) { showCommonalityDetails(); - } + } } }; @@ -419,7 +419,7 @@ private Collection<CorrelationAttributeInstance> getCorrelationAttributesFromNod } // we can correlate based on the MD5 if it is enabled - if (this.file != null && EamDb.isEnabled()) { + if (this.file != null && EamDb.isEnabled() && this.file.getSize() > 0) { try { List<CorrelationAttributeInstance.Type> artifactTypes = EamDb.getInstance().getDefinedCorrelationTypes(); @@ -447,27 +447,23 @@ private Collection<CorrelationAttributeInstance> getCorrelationAttributesFromNod } catch (EamDbException | TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS } - - } else { - - // If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled. - if (this.file != null) { - String md5 = this.file.getMd5Hash(); - if (md5 != null && !md5.isEmpty()) { - try { - final CorrelationAttributeInstance.Type fileAttributeType - = CorrelationAttributeInstance.getDefaultCorrelationTypes() - .stream() - .filter(attrType -> attrType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) - .findAny() - .get(); - - ret.add(new CorrelationAttributeInstance(fileAttributeType, md5)); - } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS - } catch (CorrelationAttributeNormalizationException ex) { - LOGGER.log(Level.INFO, String.format("Unable to create CorrelationAttributeInstance for value %s", md5), ex); // NON-NLS - } + // If EamDb not enabled, get the Files default correlation type to allow Other Occurances to be enabled. + } else if (this.file != null && this.file.getSize() > 0) { + String md5 = this.file.getMd5Hash(); + if (md5 != null && !md5.isEmpty()) { + try { + final CorrelationAttributeInstance.Type fileAttributeType + = CorrelationAttributeInstance.getDefaultCorrelationTypes() + .stream() + .filter(attrType -> attrType.getId() == CorrelationAttributeInstance.FILES_TYPE_ID) + .findAny() + .get(); + + ret.add(new CorrelationAttributeInstance(fileAttributeType, md5)); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error connecting to DB", ex); // NON-NLS + } catch (CorrelationAttributeNormalizationException ex) { + LOGGER.log(Level.INFO, String.format("Unable to create CorrelationAttributeInstance for value %s", md5), ex); // NON-NLS } } } @@ -515,9 +511,9 @@ private void setEarliestCaseDate() { * artifact. If the central repo is not enabled, this will only return files * from the current case with matching MD5 hashes. * - * @param corAttr CorrelationAttribute to query for + * @param corAttr CorrelationAttribute to query for * @param dataSourceName Data source to filter results - * @param deviceId Device Id to filter results + * @param deviceId Device Id to filter results * * @return A collection of correlated artifact instances */ @@ -580,7 +576,7 @@ private Map<UniquePathKey, OtherOccurrenceNodeInstanceData> getCorrelatedInstanc * Get all other abstract files in the current case with the same MD5 as the * selected node. * - * @param corAttr The CorrelationAttribute containing the MD5 to search for + * @param corAttr The CorrelationAttribute containing the MD5 to search for * @param openCase The current case * * @return List of matching AbstractFile objects @@ -657,11 +653,9 @@ public boolean isSupported(Node node) { // - The central repo is disabled and the backing file has a valid MD5 hash this.file = this.getAbstractFileFromNode(node); if (EamDb.isEnabled()) { - return this.file != null - && this.file.getSize() > 0 - && !getCorrelationAttributesFromNode(node).isEmpty(); + return !getCorrelationAttributesFromNode(node).isEmpty(); } else { - return this.file != null + return this.file != null && this.file.getSize() > 0 && ((this.file.getMd5Hash() != null) && (!this.file.getMd5Hash().isEmpty())); } @@ -733,8 +727,8 @@ private void populateTable(Node node) { * Adjust a given column for the text provided. * * @param columnIndex The index of the column to adjust. - * @param text The text whose length will be used to adjust the column - * width. + * @param text The text whose length will be used to adjust the + * column width. */ private void setColumnWidthToText(int columnIndex, String text) { TableColumn column = otherCasesTable.getColumnModel().getColumn(columnIndex); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index 836f47ea85278e1dc0c0af79d86b8bbc5c405245..e1e87024f00a0c193c5ad8d6df056b6c009ee2f2 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -2960,11 +2960,10 @@ private CorrelationCase getEamCaseFromResultSet(ResultSet resultSet) throws SQLE resultSet.getString("poc_phone")); } - 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"), + 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; } @@ -3080,6 +3079,7 @@ public void upgradeSchema() throws EamDbException, SQLException { ResultSet resultSet = null; Statement statement = null; + PreparedStatement preparedStatement = null; Connection conn = null; try { @@ -3114,6 +3114,10 @@ public void upgradeSchema() throws EamDbException, SQLException { logger.log(Level.INFO, "Central Repository is up to date"); return; } + if (dbSchemaVersion.compareTo(CURRENT_DB_SCHEMA_VERSION) > 0) { + logger.log(Level.INFO, "Central Repository is of newer version than software creates"); + return; + } // Update from 1.0 to 1.1 if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 1)) < 0) { @@ -3126,7 +3130,56 @@ public void upgradeSchema() throws EamDbException, SQLException { // regardless of whether this succeeds. EamDbUtil.insertDefaultOrganization(conn); } + //Update to 1.2 + if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 2)) < 0) { + //update central repository to be able to store new correlation attributes + EamDbPlatformEnum selectedPlatform = EamDbPlatformEnum.getSelectedPlatform(); + final String addSsidTableTemplate; + final String addCaseIdIndexTemplate; + final String addDataSourceIdIndexTemplate; + final String addValueIndexTemplate; + final String addKnownStatusIndexTemplate; + final String addAttributeSql; + //get the data base specific code for creating a new _instance table + switch (selectedPlatform) { + case POSTGRESQL: + addAttributeSql = "INSERT INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?) " + getConflictClause(); + addSsidTableTemplate = PostgresEamDbSettings.getCreateArtifactInstancesTableTemplate(); + addCaseIdIndexTemplate = PostgresEamDbSettings.getAddCaseIdIndexTemplate(); + addDataSourceIdIndexTemplate = PostgresEamDbSettings.getAddDataSourceIdIndexTemplate(); + addValueIndexTemplate = PostgresEamDbSettings.getAddValueIndexTemplate(); + addKnownStatusIndexTemplate = PostgresEamDbSettings.getAddKnownStatusIndexTemplate(); + break; + case SQLITE: + addAttributeSql = "INSERT OR IGNORE INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?)"; + addSsidTableTemplate = SqliteEamDbSettings.getCreateArtifactInstancesTableTemplate(); + addCaseIdIndexTemplate = SqliteEamDbSettings.getAddCaseIdIndexTemplate(); + addDataSourceIdIndexTemplate = SqliteEamDbSettings.getAddDataSourceIdIndexTemplate(); + addValueIndexTemplate = SqliteEamDbSettings.getAddValueIndexTemplate(); + addKnownStatusIndexTemplate = SqliteEamDbSettings.getAddKnownStatusIndexTemplate(); + break; + default: + throw new EamDbException("Currently selected database platform \"" + selectedPlatform.name() + "\" can not be upgraded."); + } + final String wirelessNetworsDbTableName = "wireless_networks"; + final String wirelessNetworksTableInstanceName = wirelessNetworsDbTableName + "_instances"; + //add the wireless_networks attribute to the correlation_types table + preparedStatement = conn.prepareStatement(addAttributeSql); + preparedStatement.setInt(1, CorrelationAttributeInstance.SSID_TYPE_ID); + preparedStatement.setString(2, Bundle.CorrelationType_SSID_displayName()); + preparedStatement.setString(3, wirelessNetworsDbTableName); + preparedStatement.setInt(4, 1); + preparedStatement.setInt(5, 1); + preparedStatement.execute(); + + //create a new wireless_networks_instances table and add indexes for its columns + statement.execute(String.format(addSsidTableTemplate, wirelessNetworksTableInstanceName, wirelessNetworksTableInstanceName)); + statement.execute(String.format(addCaseIdIndexTemplate, wirelessNetworksTableInstanceName, wirelessNetworksTableInstanceName)); + statement.execute(String.format(addDataSourceIdIndexTemplate, wirelessNetworksTableInstanceName, wirelessNetworksTableInstanceName)); + statement.execute(String.format(addValueIndexTemplate, wirelessNetworksTableInstanceName, wirelessNetworksTableInstanceName)); + statement.execute(String.format(addKnownStatusIndexTemplate, wirelessNetworksTableInstanceName, wirelessNetworksTableInstanceName)); + } if (!updateSchemaVersion(conn)) { throw new EamDbException("Error updating schema version"); } @@ -3144,6 +3197,7 @@ public void upgradeSchema() throws EamDbException, SQLException { throw ex; } finally { EamDbUtil.closeResultSet(resultSet); + EamDbUtil.closeStatement(preparedStatement); EamDbUtil.closeStatement(statement); EamDbUtil.closeConnection(conn); } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java index c26134c5b8aeeb520f0087fe1cbb158d0eda6331..20845ff4472c5ee7edd282580a302b97336030fc 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeInstance.java @@ -171,7 +171,7 @@ public void setCorrelationType(Type correlationType) { * Is this a database instance? * * @return True if the instance ID is greater or equal to zero; otherwise - * false. + * false. */ public boolean isDatabaseInstance() { return (ID >= 0); @@ -234,7 +234,7 @@ public TskData.FileKnown getKnownStatus() { * as notable and should never be set to KNOWN. * * @param knownStatus Should be BAD if the item is tagged as notable, - * UNKNOWN otherwise + * UNKNOWN otherwise */ public void setKnownStatus(TskData.FileKnown knownStatus) { this.knownStatus = knownStatus; @@ -246,18 +246,20 @@ public void setKnownStatus(TskData.FileKnown knownStatus) { public static final int EMAIL_TYPE_ID = 2; public static final int PHONE_TYPE_ID = 3; public static final int USBID_TYPE_ID = 4; + public static final int SSID_TYPE_ID = 5; /** * Load the default correlation types * * @throws EamDbException if the Type's dbTableName has invalid - * characters/format + * characters/format */ @Messages({"CorrelationType.FILES.displayName=Files", "CorrelationType.DOMAIN.displayName=Domains", "CorrelationType.EMAIL.displayName=Email Addresses", "CorrelationType.PHONE.displayName=Phone Numbers", - "CorrelationType.USBID.displayName=USB Devices"}) + "CorrelationType.USBID.displayName=USB Devices", + "CorrelationType.SSID.displayName=Wireless Networks"}) public static List<CorrelationAttributeInstance.Type> getDefaultCorrelationTypes() throws EamDbException { List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = new ArrayList<>(); DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(FILES_TYPE_ID, Bundle.CorrelationType_FILES_displayName(), "file", true, true)); // NON-NLS @@ -265,6 +267,7 @@ public static List<CorrelationAttributeInstance.Type> getDefaultCorrelationTypes DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(EMAIL_TYPE_ID, Bundle.CorrelationType_EMAIL_displayName(), "email_address", true, true)); // NON-NLS DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(PHONE_TYPE_ID, Bundle.CorrelationType_PHONE_displayName(), "phone_number", true, true)); // NON-NLS DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(USBID_TYPE_ID, Bundle.CorrelationType_USBID_displayName(), "usb_devices", true, true)); // NON-NLS + DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(SSID_TYPE_ID, Bundle.CorrelationType_SSID_displayName(), "wireless_networks", true, true)); // NON-NLS return DEFAULT_CORRELATION_TYPES; } @@ -283,13 +286,14 @@ public static class Type implements Serializable { // NOPMD Avoid short class na /** * - * @param typeId Unique ID for this Correlation Type + * @param typeId Unique ID for this Correlation Type * @param displayName Name of this type displayed in the UI. * @param dbTableName Central repository db table where data of this - * type is stored. Must start with a lowercase letter and only contain - * lowercase letters, numbers, and '_' characters. - * @param supported Is this Type currently supported - * @param enabled Is this Type currently enabled. + * type is stored. Must start with a lowercase letter + * and only contain lowercase letters, numbers, and + * '_' characters. + * @param supported Is this Type currently supported + * @param enabled Is this Type currently enabled. */ public Type(int typeId, String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { if (dbTableName == null) { @@ -312,10 +316,11 @@ public Type(int typeId, String displayName, String dbTableName, Boolean supporte * * @param displayName Name of this type displayed in the UI. * @param dbTableName Central repository db table where data of this - * type is stored Must start with a lowercase letter and only contain - * lowercase letters, numbers, and '_' characters. - * @param supported Is this Type currently supported - * @param enabled Is this Type currently enabled. + * type is stored Must start with a lowercase letter + * and only contain lowercase letters, numbers, and + * '_' characters. + * @param supported Is this Type currently supported + * @param enabled Is this Type currently enabled. */ public Type(String displayName, String dbTableName, Boolean supported, Boolean enabled) throws EamDbException { this(-1, displayName, dbTableName, supported, enabled); @@ -477,8 +482,8 @@ public String getDbTableName() { * custom_instances) * * @param dbTableName the dbTableName to set. Must start with lowercase - * letter and can only contain lowercase letters, numbers, and '_' - * characters. + * letter and can only contain lowercase letters, + * numbers, and '_' characters. * * @throws EamDbException if dbTableName contains invalid characters */ diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizer.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizer.java index 772e1c517ec89765d4db369ec06df3a9fa8f3379..4ce04769c8d2517501cff2bd130d2cf3bb4374eb 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizer.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeNormalizer.java @@ -63,6 +63,8 @@ public static String normalize(CorrelationAttributeInstance.Type attributeType, return normalizePhone(data); case CorrelationAttributeInstance.USBID_TYPE_ID: return normalizeUsbId(data); + case CorrelationAttributeInstance.SSID_TYPE_ID: + return data; default: final String errorMessage = String.format( "Validator function not found for attribute type: %s", diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java index 30d539e87fb53bf222689ed04ce86693976b6a3c..aca04713457b213e5507415884e2d3f8d10fe5cc 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java @@ -54,9 +54,9 @@ public static String getEmailAddressAttrString() { * EamArtifact with a single EamArtifactInstance within. If not, return * null. * - * @param bbArtifact BlackboardArtifact to examine + * @param bbArtifact BlackboardArtifact to examine * @param checkEnabled If true, only create a CorrelationAttribute if it is - * enabled + * enabled * * @return List of EamArtifacts */ @@ -93,10 +93,10 @@ public static List<CorrelationAttributeInstance> makeInstancesFromBlackboardArti * based on the data in the blackboard artifact. * * @param correlationType The Central Repository artifact type to create - * @param bbArtifact The blackboard artifact to pull data from + * @param bbArtifact The blackboard artifact to pull data from * * @return the new EamArtifact, or null if one was not created because - * bbArtifact did not contain the needed data + * bbArtifact did not contain the needed data */ private static CorrelationAttributeInstance makeInstanceFromBlackboardArtifact(CorrelationAttributeInstance.Type correlationType, BlackboardArtifact bbArtifact) throws EamDbException { @@ -159,13 +159,14 @@ private static CorrelationAttributeInstance makeInstanceFromBlackboardArtifact(C return null; } } - } else if (correlationType.getId() == CorrelationAttributeInstance.USBID_TYPE_ID && BlackboardArtifact.ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID() == artifactTypeID) { value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID)).getValueString(); + } else if (correlationType.getId() == CorrelationAttributeInstance.SSID_TYPE_ID + && BlackboardArtifact.ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID() == artifactTypeID) { + value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SSID)).getValueString(); } - } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error getting attribute while getting type from BlackboardArtifact.", ex); // NON-NLS return null; @@ -185,9 +186,10 @@ private static CorrelationAttributeInstance makeInstanceFromBlackboardArtifact(C * Uses the determined type and vallue, then looks up instance details to * create proper CorrelationAttributeInstance. * - * @param bbArtifact the blackboard artifatc + * @param bbArtifact the blackboard artifact * @param correlationType the given type - * @param value the artifact value + * @param value the artifact value + * * @return CorrelationAttributeInstance from details */ private static CorrelationAttributeInstance makeCorrelationAttributeInstanceUsingTypeValue(BlackboardArtifact bbArtifact, CorrelationAttributeInstance.Type correlationType, String value) { @@ -340,7 +342,7 @@ public static CorrelationAttributeInstance makeInstanceFromContent(Content conte * @param file The file to test * * @return true if the file should be added to the central repo, false - * otherwise + * otherwise */ public static boolean isSupportedAbstractFileType(AbstractFile file) { if (file == null) { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java index c7e385928d65fc34a0c00f9e4ce996554b334e03..418181d8ab2e1679554afb337e994c3b1a3dbe9d 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java @@ -31,9 +31,9 @@ */ public interface EamDb { - public static final int SCHEMA_VERSION = 1; + public static final int SCHEMA_VERSION = 2; public static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION - = new CaseDbSchemaVersionNumber(1, 1); + = new CaseDbSchemaVersionNumber(1, 2); /** diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java index 77ab8c23db4a9e7e03bca1b0c3cb8ffa9d75f316..6ee454f915ff128ea7f9625674d735b86c106f3f 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java @@ -35,8 +35,9 @@ /** * Settings for the Postgres implementation of the Central Repository database - * - * NOTE: This is public scope because the options panel calls it directly to set/get + * + * NOTE: This is public scope because the options panel calls it directly to + * set/get */ public final class PostgresEamDbSettings { @@ -266,7 +267,7 @@ public boolean createDatabase() { return true; } - + public boolean deleteDatabase() { Connection conn = getEphemeralConnection(true); if (null == conn) { @@ -391,26 +392,13 @@ public boolean initializeDatabaseSchema() { createCorrelationTypesTable.append("CONSTRAINT correlation_types_names UNIQUE (display_name, db_table_name)"); createCorrelationTypesTable.append(")"); - // Each "%s" will be replaced with the relevant TYPE_instances table name. - StringBuilder createArtifactInstancesTableTemplate = new StringBuilder(); - createArtifactInstancesTableTemplate.append("CREATE TABLE IF NOT EXISTS %s ("); - createArtifactInstancesTableTemplate.append("id SERIAL PRIMARY KEY,"); - createArtifactInstancesTableTemplate.append("case_id integer NOT NULL,"); - createArtifactInstancesTableTemplate.append("data_source_id integer NOT NULL,"); - createArtifactInstancesTableTemplate.append("value text NOT NULL,"); - createArtifactInstancesTableTemplate.append("file_path text NOT NULL,"); - createArtifactInstancesTableTemplate.append("known_status integer NOT NULL,"); - createArtifactInstancesTableTemplate.append("comment text,"); - createArtifactInstancesTableTemplate.append("CONSTRAINT %s_multi_unique_ UNIQUE (data_source_id, value, file_path),"); - createArtifactInstancesTableTemplate.append("foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,"); - createArtifactInstancesTableTemplate.append("foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL"); - createArtifactInstancesTableTemplate.append(")"); + String createArtifactInstancesTableTemplate = getCreateArtifactInstancesTableTemplate(); - // Each "%s" will be replaced with the relevant TYPE_instances table name. - String instancesIdx1 = "CREATE INDEX IF NOT EXISTS %s_case_id ON %s (case_id)"; - String instancesIdx2 = "CREATE INDEX IF NOT EXISTS %s_data_source_id ON %s (data_source_id)"; - String instancesIdx3 = "CREATE INDEX IF NOT EXISTS %s_value ON %s (value)"; - String instancesIdx4 = "CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)"; + String instancesIdx1 = getAddCaseIdIndexTemplate(); + String instancesIdx2 = getAddDataSourceIdIndexTemplate(); + + String instancesIdx3 = getAddValueIndexTemplate(); + String instancesIdx4 = getAddKnownStatusIndexTemplate(); StringBuilder createDbInfoTable = new StringBuilder(); createDbInfoTable.append("CREATE TABLE IF NOT EXISTS db_info ("); @@ -447,14 +435,14 @@ public boolean initializeDatabaseSchema() { // Create a separate instance and reference table for each correlation type List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes(); - + String reference_type_dbname; String instance_type_dbname; for (CorrelationAttributeInstance.Type type : DEFAULT_CORRELATION_TYPES) { reference_type_dbname = EamDbUtil.correlationTypeToReferenceTableName(type); instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type); - - stmt.execute(String.format(createArtifactInstancesTableTemplate.toString(), instance_type_dbname, instance_type_dbname)); + + stmt.execute(String.format(createArtifactInstancesTableTemplate, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesIdx1, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesIdx2, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesIdx3, instance_type_dbname, instance_type_dbname)); @@ -465,7 +453,7 @@ public boolean initializeDatabaseSchema() { stmt.execute(String.format(createReferenceTypesTableTemplate.toString(), reference_type_dbname, reference_type_dbname)); stmt.execute(String.format(referenceTypesIdx1, reference_type_dbname, reference_type_dbname)); stmt.execute(String.format(referenceTypesIdx2, reference_type_dbname, reference_type_dbname)); - } + } } } catch (SQLException ex) { @@ -480,6 +468,83 @@ public boolean initializeDatabaseSchema() { return true; } + /** + * Get the template String for creating a new _instances table in a Postgres + * central repository. %s will exist in the template where the name of the + * new table will be addedd. + * + * @return a String which is a template for cretating a new _instances table + */ + static String getCreateArtifactInstancesTableTemplate() { + // Each "%s" will be replaced with the relevant TYPE_instances table name. + StringBuilder createArtifactInstancesTableTemplate = new StringBuilder(); + createArtifactInstancesTableTemplate.append("CREATE TABLE IF NOT EXISTS %s ("); + createArtifactInstancesTableTemplate.append("id SERIAL PRIMARY KEY,"); + createArtifactInstancesTableTemplate.append("case_id integer NOT NULL,"); + createArtifactInstancesTableTemplate.append("data_source_id integer NOT NULL,"); + createArtifactInstancesTableTemplate.append("value text NOT NULL,"); + createArtifactInstancesTableTemplate.append("file_path text NOT NULL,"); + createArtifactInstancesTableTemplate.append("known_status integer NOT NULL,"); + createArtifactInstancesTableTemplate.append("comment text,"); + createArtifactInstancesTableTemplate.append("CONSTRAINT %s_multi_unique_ UNIQUE (data_source_id, value, file_path),"); + createArtifactInstancesTableTemplate.append("foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,"); + createArtifactInstancesTableTemplate.append("foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL"); + createArtifactInstancesTableTemplate.append(")"); + return createArtifactInstancesTableTemplate.toString(); + } + + /** + * Get the template for creating an index on the case_id column of an + * instance table. %s will exist in the template where the name of the new + * table will be addedd. + * + * @return a String which is a template for adding an index to the case_id + * column of a _instances table + */ + static String getAddCaseIdIndexTemplate() { + // Each "%s" will be replaced with the relevant TYPE_instances table name. + return "CREATE INDEX IF NOT EXISTS %s_case_id ON %s (case_id)"; + } + + /** + * Get the template for creating an index on the data_source_id column of an + * instance table. %s will exist in the template where the name of the new + * table will be addedd. + * + * @return a String which is a template for adding an index to the + * data_source_id column of a _instances table + */ + static String getAddDataSourceIdIndexTemplate() { + // Each "%s" will be replaced with the relevant TYPE_instances table name. + return "CREATE INDEX IF NOT EXISTS %s_data_source_id ON %s (data_source_id)"; + } + + /** + * Get the template for creating an index on the value column of an instance + * table. %s will exist in the template where the name of the new table will + * be addedd. + * + * @return a String which is a template for adding an index to the value + * column of a _instances table + */ + static String getAddValueIndexTemplate() { + // Each "%s" will be replaced with the relevant TYPE_instances table name. + return "CREATE INDEX IF NOT EXISTS %s_value ON %s (value)"; + } + + /** + * Get the template for creating an index on the known_status column of an + * instance table. %s will exist in the template where the name of the new + * table will be addedd. + * + * @return a String which is a template for adding an index to the + * known_status column of a _instances table + */ + static String getAddKnownStatusIndexTemplate() { + // Each "%s" will be replaced with the relevant TYPE_instances table name. + return "CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)"; + } + public boolean insertDefaultDatabaseContent() { Connection conn = getEphemeralConnection(false); if (null == conn) { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java index 4894a570e6abdad9e6f81f1b3e23f17ad22bb931..615e49e523551c0bbcaecf5e7f9fc230818e13b5 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java @@ -35,8 +35,9 @@ /** * Settings for the sqlite implementation of the Central Repository database - * - * NOTE: This is public scope because the options panel calls it directly to set/get + * + * NOTE: This is public scope because the options panel calls it directly to + * set/get */ public final class SqliteEamDbSettings { @@ -95,7 +96,7 @@ public void saveSettings() { ModuleSettings.setConfigSetting("CentralRepository", "db.sqlite.dbDirectory", getDbDirectory()); // NON-NLS ModuleSettings.setConfigSetting("CentralRepository", "db.sqlite.bulkThreshold", Integer.toString(getBulkThreshold())); // NON-NLS } - + /** * Verify that the db file exists. * @@ -103,11 +104,11 @@ public void saveSettings() { */ public boolean dbFileExists() { File dbFile = new File(getFileNameWithPath()); - if(! dbFile.exists()){ + if (!dbFile.exists()) { return false; } // It's unlikely, but make sure the file isn't actually a directory - return ( ! dbFile.isDirectory()); + return (!dbFile.isDirectory()); } /** @@ -148,10 +149,11 @@ public boolean createDbDirectory() { return true; } - + /** * Delete the database - * @return + * + * @return */ public boolean deleteDatabase() { File dbFile = new File(this.getFileNameWithPath()); @@ -333,26 +335,13 @@ public boolean initializeDatabaseSchema() { createCorrelationTypesTable.append("CONSTRAINT correlation_types_names UNIQUE (display_name, db_table_name)"); createCorrelationTypesTable.append(")"); - // Each "%s" will be replaced with the relevant TYPE_instances table name. - StringBuilder createArtifactInstancesTableTemplate = new StringBuilder(); - createArtifactInstancesTableTemplate.append("CREATE TABLE IF NOT EXISTS %s ("); - createArtifactInstancesTableTemplate.append("id integer primary key autoincrement NOT NULL,"); - createArtifactInstancesTableTemplate.append("case_id integer NOT NULL,"); - createArtifactInstancesTableTemplate.append("data_source_id integer NOT NULL,"); - createArtifactInstancesTableTemplate.append("value text NOT NULL,"); - createArtifactInstancesTableTemplate.append("file_path text NOT NULL,"); - createArtifactInstancesTableTemplate.append("known_status integer NOT NULL,"); - createArtifactInstancesTableTemplate.append("comment text,"); - createArtifactInstancesTableTemplate.append("CONSTRAINT %s_multi_unique UNIQUE(data_source_id, value, file_path) ON CONFLICT IGNORE,"); - createArtifactInstancesTableTemplate.append("foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,"); - createArtifactInstancesTableTemplate.append("foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL"); - createArtifactInstancesTableTemplate.append(")"); + String createArtifactInstancesTableTemplate = getCreateArtifactInstancesTableTemplate(); - // Each "%s" will be replaced with the relevant TYPE_instances table name. - String instancesIdx1 = "CREATE INDEX IF NOT EXISTS %s_case_id ON %s (case_id)"; - String instancesIdx2 = "CREATE INDEX IF NOT EXISTS %s_data_source_id ON %s (data_source_id)"; - String instancesIdx3 = "CREATE INDEX IF NOT EXISTS %s_value ON %s (value)"; - String instancesIdx4 = "CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)"; + String instancesIdx1 = getAddCaseIdIndexTemplate(); + String instancesIdx2 = getAddDataSourceIdIndexTemplate(); + + String instancesIdx3 = getAddValueIndexTemplate(); + String instancesIdx4 = getAddKnownStatusIndexTemplate(); StringBuilder createDbInfoTable = new StringBuilder(); createDbInfoTable.append("CREATE TABLE IF NOT EXISTS db_info ("); @@ -402,7 +391,7 @@ public boolean initializeDatabaseSchema() { reference_type_dbname = EamDbUtil.correlationTypeToReferenceTableName(type); instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type); - stmt.execute(String.format(createArtifactInstancesTableTemplate.toString(), instance_type_dbname, instance_type_dbname)); + stmt.execute(String.format(createArtifactInstancesTableTemplate, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesIdx1, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesIdx2, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesIdx3, instance_type_dbname, instance_type_dbname)); @@ -426,6 +415,83 @@ public boolean initializeDatabaseSchema() { } return true; } + + /** + * Get the template String for creating a new _instances table in a Sqlite + * central repository. %s will exist in the template where the name of the + * new table will be addedd. + * + * @return a String which is a template for cretating a new _instances table + */ + static String getCreateArtifactInstancesTableTemplate() { + // Each "%s" will be replaced with the relevant TYPE_instances table name. + StringBuilder createArtifactInstancesTableTemplate = new StringBuilder(); + createArtifactInstancesTableTemplate.append("CREATE TABLE IF NOT EXISTS %s ("); + createArtifactInstancesTableTemplate.append("id integer primary key autoincrement NOT NULL,"); + createArtifactInstancesTableTemplate.append("case_id integer NOT NULL,"); + createArtifactInstancesTableTemplate.append("data_source_id integer NOT NULL,"); + createArtifactInstancesTableTemplate.append("value text NOT NULL,"); + createArtifactInstancesTableTemplate.append("file_path text NOT NULL,"); + createArtifactInstancesTableTemplate.append("known_status integer NOT NULL,"); + createArtifactInstancesTableTemplate.append("comment text,"); + createArtifactInstancesTableTemplate.append("CONSTRAINT %s_multi_unique UNIQUE(data_source_id, value, file_path) ON CONFLICT IGNORE,"); + createArtifactInstancesTableTemplate.append("foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,"); + createArtifactInstancesTableTemplate.append("foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL"); + createArtifactInstancesTableTemplate.append(")"); + return createArtifactInstancesTableTemplate.toString(); + } + + /** + * Get the template for creating an index on the case_id column of an + * instance table. %s will exist in the template where the name of the new + * table will be addedd. + * + * @return a String which is a template for adding an index to the case_id + * column of a _instances table + */ + static String getAddCaseIdIndexTemplate() { + // Each "%s" will be replaced with the relevant TYPE_instances table name. + return "CREATE INDEX IF NOT EXISTS %s_case_id ON %s (case_id)"; + } + + /** + * Get the template for creating an index on the data_source_id column of an + * instance table. %s will exist in the template where the name of the new + * table will be addedd. + * + * @return a String which is a template for adding an index to the + * data_source_id column of a _instances table + */ + static String getAddDataSourceIdIndexTemplate() { + // Each "%s" will be replaced with the relevant TYPE_instances table name. + return "CREATE INDEX IF NOT EXISTS %s_data_source_id ON %s (data_source_id)"; + } + + /** + * Get the template for creating an index on the value column of an instance + * table. %s will exist in the template where the name of the new table will + * be addedd. + * + * @return a String which is a template for adding an index to the value + * column of a _instances table + */ + static String getAddValueIndexTemplate() { + // Each "%s" will be replaced with the relevant TYPE_instances table name. + return "CREATE INDEX IF NOT EXISTS %s_value ON %s (value)"; + } + + /** + * Get the template for creating an index on the known_status column of an + * instance table. %s will exist in the template where the name of the new + * table will be addedd. + * + * @return a String which is a template for adding an index to the + * known_status column of a _instances table + */ + static String getAddKnownStatusIndexTemplate() { + // Each "%s" will be replaced with the relevant TYPE_instances table name. + return "CREATE INDEX IF NOT EXISTS %s_value_known_status ON %s (value, known_status)"; + } public boolean insertDefaultDatabaseContent() { Connection conn = getEphemeralConnection(); @@ -490,8 +556,6 @@ void setBulkThreshold(int bulkThreshold) throws EamDbException { } } - - /** * @return the dbDirectory */ diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index ccca659574281964d3b3a7b19f44891367126751..2f2a35ab2ac50d56cab1ad36f90e2815f8c97b79 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -183,7 +183,10 @@ private class IngestModuleEventListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent evt) { - if (getCeModuleInstanceCount() > 0) { + //if ingest is running we want there to check if there is a Correlation Engine module running + //sometimes artifacts are generated by DSPs or other sources while ingest is not running + //in these cases we still want to create correlation attributes for those artifacts when appropriate + if (!IngestManager.getInstance().isIngestRunning() || getCeModuleInstanceCount() > 0) { EamDb dbManager; try { dbManager = EamDb.getInstance(); @@ -193,7 +196,9 @@ public void propertyChange(PropertyChangeEvent evt) { } switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) { case DATA_ADDED: { - jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt, isFlagNotableItems())); + //if ingest isn't running create the interesting items otherwise use the ingest module setting to determine if we create interesting items + boolean flagNotable = !IngestManager.getInstance().isIngestRunning() || isFlagNotableItems(); + jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt, flagNotable)); break; } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java index 21baf59454711b47f48227ce03d3e8efe47541ea..7469e38bd0e2d6879e2aa2c4fb54b3813184fca9 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java @@ -71,7 +71,6 @@ final class IngestModule implements FileIngestModule { private CorrelationDataSource eamDataSource; private Blackboard blackboard; private CorrelationAttributeInstance.Type filesType; - private final boolean flagTaggedNotableItems; /** diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties index 0fc19515932f0ecbac0f63346ed150f8a45866a6..aae9fa321db4998b2449f64db9c29f12a6347461 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties @@ -34,7 +34,7 @@ AddNewOrganizationDialog.bnOK.text=OK AddNewOrganizationDialog.tfName.tooltip=POC Name ManageTagsDialog.okButton.text=OK ManageTagsDialog.cancelButton.text=Cancel -ManageArtifactTypesDialog.taInstructionsMsg.text=Enable one or more correlation properties to use for correlation during ingest. Note, these properties are global and impact all users of the central repository. +ManageArtifactTypesDialog.taInstructionsMsg.text=Enable one or more correlation properties to use for correlation during ingest. Note, these properties are global and impact all users of the Central Repository. EamSqliteSettingsDialog.bnOk.text=OK EamPostgresSettingsDialog.bnSave.text=Save EamDbSettingsDialog.bnDatabasePathFileOpen.text=Browse... @@ -58,11 +58,10 @@ ManageCorrelationPropertiesDialog.okButton.text=OK GlobalSettingsPanel.bnManageProperties.text=Manage Correlation Properties EamDbSettingsDialog.lbDatabaseDesc.text=Database File: EamDbSettingsDialog.lbFullDbPath.text= -GlobalSettingsPanel.cbUseCentralRepo.text=Use a central repository -GlobalSettingsPanel.correlationPropertiesTextArea.text=Choose which file and result properties to store in the central repository for later correlation.\n -GlobalSettingsPanel.organizationTextArea.text=Organization information can be tracked in the central repository. +GlobalSettingsPanel.cbUseCentralRepo.text=Use a Central Repository +GlobalSettingsPanel.organizationTextArea.text=Organization information can be tracked in the Central Repository. GlobalSettingsPanel.manageOrganizationButton.text=Manage Organizations -GlobalSettingsPanel.lbCentralRepository.text=A central repository allows you to correlate files and results between cases. +GlobalSettingsPanel.lbCentralRepository.text=A Central Repository allows you to correlate files and results between cases. GlobalSettingsPanel.pnCorrelationProperties.border.title=Correlation Properties GlobalSettingsPanel.organizationPanel.border.title=Organizations GlobalSettingsPanel.casesPanel.border.title=Case Details @@ -74,8 +73,9 @@ ShowCasesDialog.caseDetailsTable.toolTipText=Click column name to sort. Right-cl ShowCasesDialog.title=Case Details 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! +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! +GlobalSettingsPanel.correlationPropertiesTextArea.text=Choose which file and result properties to store in the Central Repository for later correlation.\n ManageCasesDialog.examinerPhoneLabel.text=Examiner Phone: ManageCasesDialog.examinerNameLabel.text=Examiner Name: ManageCasesDialog.examinerEmailLabel.text=Examiner Email: @@ -84,4 +84,4 @@ ManageCasesDialog.orgLabel.text=Organization: ManageCasesDialog.closeButton.text=Close ManageCasesDialog.notesLabel.text=Notes: ManageCasesDialog.dataSourcesLabel.text=Data Sources: -ManageCasesDialog.caseInfoLabel.text=Case Info: +ManageCasesDialog.caseInfoLabel.text=Case Info: \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java index c504f666e48602bda4d2487e497247913e7773e4..826e66ecbc0d65e822eb4a524d863239af817035 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java @@ -102,7 +102,7 @@ public boolean accept(File pathname) { @Override public String getDescription() { - return "Directories and central repository databases"; + return "Directories and Central Repository databases"; } }); cbDatabaseType.setSelectedItem(selectedPlatform); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form index c3a8f678d608f20eafcd2a4f7d0ebe84def20e36..0a8c7dcc64e9ad384e2b9b1dff56d7c78c295681 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form @@ -57,7 +57,7 @@ <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> - <Component id="lbCentralRepository" pref="1022" max="32767" attributes="0"/> + <Component id="lbCentralRepository" max="32767" attributes="0"/> <Group type="102" attributes="0"> <Group type="103" groupAlignment="0" attributes="0"> <Component id="pnDatabaseConfiguration" alignment="0" max="32767" attributes="0"/> @@ -67,7 +67,7 @@ <Group type="102" attributes="0"> <Component id="cbUseCentralRepo" min="-2" pref="162" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/> - <Component id="ingestRunningWarningLabel" max="32767" attributes="0"/> + <Component id="ingestRunningWarningLabel" pref="844" max="32767" attributes="0"/> </Group> <Group type="102" alignment="0" attributes="0"> <EmptySpace max="-2" attributes="0"/> @@ -89,13 +89,13 @@ </Group> <EmptySpace type="unrelated" max="-2" attributes="0"/> <Component id="pnDatabaseConfiguration" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> + <EmptySpace min="0" pref="0" max="-2" attributes="0"/> <Component id="pnCorrelationProperties" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> + <EmptySpace min="0" pref="0" max="-2" attributes="0"/> <Component id="organizationPanel" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> + <EmptySpace min="0" pref="0" max="-2" attributes="0"/> <Component id="casesPanel" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> + <EmptySpace min="0" pref="0" max="-2" attributes="0"/> <Component id="tbOops" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> </Group> @@ -242,14 +242,14 @@ <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> - <EmptySpace max="-2" attributes="0"/> + <Group type="102" alignment="0" attributes="0"> + <EmptySpace min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Component id="correlationPropertiesScrollPane" max="32767" attributes="0"/> - <Group type="102" alignment="0" attributes="0"> + <Group type="102" attributes="0"> <Component id="bnManageTypes" min="-2" max="-2" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/> </Group> + <Component id="correlationPropertiesScrollPane" max="32767" attributes="0"/> </Group> <EmptySpace max="-2" attributes="0"/> </Group> @@ -258,8 +258,7 @@ <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="1" attributes="0"> - <EmptySpace min="-2" pref="7" max="-2" attributes="0"/> - <Component id="correlationPropertiesScrollPane" min="-2" max="-2" attributes="0"/> + <Component id="correlationPropertiesScrollPane" pref="32" max="32767" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="bnManageTypes" min="-2" max="-2" attributes="0"/> <EmptySpace min="-2" pref="8" max="-2" attributes="0"/> @@ -301,7 +300,7 @@ <Font name="Tahoma" size="11" style="0"/> </Property> <Property name="lineWrap" type="boolean" value="true"/> - <Property name="rows" type="int" value="2"/> + <Property name="rows" type="int" value="1"/> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="GlobalSettingsPanel.correlationPropertiesTextArea.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> </Property> @@ -334,7 +333,7 @@ <Group type="102" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Component id="organizationScrollPane" pref="992" max="32767" attributes="0"/> + <Component id="organizationScrollPane" max="32767" attributes="0"/> <Group type="102" attributes="0"> <Component id="manageOrganizationButton" min="-2" max="-2" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/> diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java index e24fe70ea8d551f7699fe142decbc96b6356a56e..a4d6ef5cc0e1dc06f455cd4abbed75890a7d2b13 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java @@ -222,7 +222,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { correlationPropertiesTextArea.setColumns(20); correlationPropertiesTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N correlationPropertiesTextArea.setLineWrap(true); - correlationPropertiesTextArea.setRows(2); + correlationPropertiesTextArea.setRows(1); correlationPropertiesTextArea.setText(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.correlationPropertiesTextArea.text")); // NOI18N correlationPropertiesTextArea.setToolTipText(""); correlationPropertiesTextArea.setWrapStyleWord(true); @@ -236,17 +236,16 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addGroup(pnCorrelationPropertiesLayout.createSequentialGroup() .addContainerGap() .addGroup(pnCorrelationPropertiesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(correlationPropertiesScrollPane) .addGroup(pnCorrelationPropertiesLayout.createSequentialGroup() .addComponent(bnManageTypes) - .addGap(0, 0, Short.MAX_VALUE))) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(correlationPropertiesScrollPane)) .addContainerGap()) ); pnCorrelationPropertiesLayout.setVerticalGroup( pnCorrelationPropertiesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnCorrelationPropertiesLayout.createSequentialGroup() - .addGap(7, 7, 7) - .addComponent(correlationPropertiesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(correlationPropertiesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 32, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(bnManageTypes) .addGap(8, 8, 8)) @@ -281,7 +280,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addGroup(organizationPanelLayout.createSequentialGroup() .addContainerGap() .addGroup(organizationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(organizationScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 992, Short.MAX_VALUE) + .addComponent(organizationScrollPane) .addGroup(organizationPanelLayout.createSequentialGroup() .addComponent(manageOrganizationButton) .addGap(0, 0, Short.MAX_VALUE))) @@ -356,7 +355,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lbCentralRepository, javax.swing.GroupLayout.DEFAULT_SIZE, 1022, Short.MAX_VALUE) + .addComponent(lbCentralRepository, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) @@ -366,7 +365,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addGroup(jPanel1Layout.createSequentialGroup() .addComponent(cbUseCentralRepo, javax.swing.GroupLayout.PREFERRED_SIZE, 162, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(ingestRunningWarningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(ingestRunningWarningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 844, Short.MAX_VALUE)) .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, 974, javax.swing.GroupLayout.PREFERRED_SIZE))) @@ -382,13 +381,13 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addComponent(ingestRunningWarningLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGap(0, 0, 0) .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGap(0, 0, 0) .addComponent(organizationPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGap(0, 0, 0) .addComponent(casesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGap(0, 0, 0) .addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) ); diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java index 506084f0c3f251d8ffada69c1244f38575b9cfe6..dd73b6f6ca762b4ce1b91bfe9c1bc9af2f76b255 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java @@ -74,7 +74,7 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer private final Outline outline; private ExplorerManager explorerManager; - private NSDictionary rootDict; + private NSObject rootDict; /** * Creates new form PListViewer @@ -415,22 +415,35 @@ private PropKeyValue parseProperty(final String key, final NSObject value) { } /** - * Parses given binary stream and extracts Plist key/value + * Parses given binary stream and extracts Plist key/value. * - * @param plistbytes + * @param plistbytes The byte array containing the Plist data. * * @return list of PropKeyValue */ private List<PropKeyValue> parsePList(final byte[] plistbytes) throws IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException { final List<PropKeyValue> plist = new ArrayList<>(); - rootDict = (NSDictionary) PropertyListParser.parse(plistbytes); + rootDict = PropertyListParser.parse(plistbytes); - final String[] keys = rootDict.allKeys(); - for (final String key : keys) { - final PropKeyValue pkv = parseProperty(key, rootDict.objectForKey(key)); - if (null != pkv) { - plist.add(pkv); + /* + * Parse the data if the root is an NSArray or NSDictionary. Anything + * else is unexpected and will be ignored. + */ + if (rootDict instanceof NSArray) { + for (int i=0; i < ((NSArray)rootDict).count(); i++) { + final PropKeyValue pkv = parseProperty("", ((NSArray)rootDict).objectAtIndex(i)); + if (null != pkv) { + plist.add(pkv); + } + } + } else if (rootDict instanceof NSDictionary) { + final String[] keys = ((NSDictionary)rootDict).allKeys(); + for (final String key : keys) { + final PropKeyValue pkv = parseProperty(key, ((NSDictionary)rootDict).objectForKey(key)); + if (null != pkv) { + plist.add(pkv); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java index 2cccd31428ca5380088be351861d62c3bbc91370..39d3203b13fb9b334c6b0b35aadb4b79b2e40e4f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java @@ -487,7 +487,8 @@ public int isPreferred(Node node) { || (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/coreutils/NetworkUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/NetworkUtils.java index af41bc1980be3bd1a21f9e4ef56191013dcbff1c..78547f8370eead294b88975701f7115cecf517a8 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 <at> sleuthkit <dot> 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 static String getLocalHostName() { } 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 ae9f1a7758346279289fb2b7ad4a245ae460c965..c844d9ce97b9c6e4326e805ff7fe93b88e465bc8 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/TimeZoneUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/TimeZoneUtils.java @@ -21,6 +21,8 @@ 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; @@ -45,7 +47,7 @@ public static String convertToAlphaNumericFormat(String timeZoneId) { 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); @@ -74,7 +76,7 @@ public static String convertToAlphaNumericFormat(String timeZoneId) { public static String createTimeZoneString(TimeZone timeZone) { int offset = timeZone.getRawOffset() / 1000; int hour = offset / 3600; - int minutes = (offset % 3600) / 60; + int minutes = Math.abs((offset % 3600) / 60); return String.format("(GMT%+d:%02d) %s", hour, minutes, timeZone.getID()); //NON-NLS } @@ -83,9 +85,11 @@ public static String createTimeZoneString(TimeZone timeZone) { * Generates a list of time zones. */ public static List<String> createTimeZoneList() { - List<String> timeZoneList = new ArrayList<>(); + /* + * Create a list of time zones. + */ + List<TimeZone> timeZoneList = new ArrayList<>(); - // load and add all timezone String[] ids = SimpleTimeZone.getAvailableIDs(); for (String id : ids) { /* @@ -97,10 +101,36 @@ public static List<String> createTimeZoneList() { * if(hasDaylight){ result = result + second; } * timeZoneComboBox.addItem(item + " (" + result + ")"); */ - timeZoneList.add(createTimeZoneString(TimeZone.getTimeZone(id))); + timeZoneList.add(TimeZone.getTimeZone(id)); } - return timeZoneList; + /* + * Sort the list of time zones first by offset, then by ID. + */ + Collections.sort(timeZoneList, new Comparator<TimeZone>(){ + @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<String> outputList = new ArrayList<>(); + + for (TimeZone timeZone : timeZoneList) { + outputList.add(createTimeZoneString(timeZone)); + } + + return outputList; } /** diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index 3ab5f6cb90d17432e7f36e107ce56f8a491b5c41..e950d0e870ad241784f11948b04befa09e84f8ea 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -156,6 +156,8 @@ static String getIconFilePath(int typeID) { return filePath + "drive_network.png"; //NON-NLS } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_FACE_DETECTED.getTypeID()) { return filePath + "face.png"; //NON-NLS + } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID()) { + return filePath + "network-wifi.png"; //NON-NLS } return filePath + "artifact-icon.png"; //NON-NLS } diff --git a/Core/src/org/sleuthkit/autopsy/images/network-wifi.png b/Core/src/org/sleuthkit/autopsy/images/network-wifi.png new file mode 100644 index 0000000000000000000000000000000000000000..5d7cb76ca165db10f3df4f8b07813eee8b335a7c Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/network-wifi.png differ diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index 39e428d233e0eebc494009aefadd0fc42aad3311..c1aec6c81bae0f828601dd0738a939dfe45ef3c1 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -21,9 +21,6 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -93,7 +90,7 @@ class SevenZipExtractor { private String moduleDirAbsolute; private Blackboard blackboard; - + private ProgressHandle progress; private int numItems; private String currentArchiveName; @@ -164,18 +161,19 @@ boolean isSevenZipExtractionSupported(AbstractFile file) { * * More heuristics to be added here * - * @param archiveFile the AbstractFile for the parent archive which - * which we are checking - * @param inArchive The SevenZip archive currently open for extraction - * - * @param inArchiveItemIndex Index of item inside the SevenZip archive. Each - * file inside an archive is associated with a unique - * integer - * - * @param depthMap a concurrent hashmap which keeps track of the - * depth of all nested archives, key of objectID - * @param escapedFilePath the path to the archiveFileItem which has been - * escaped + * @param archiveFile the AbstractFile for the parent archive which + * which we are checking + * @param inArchive The SevenZip archive currently open for + * extraction + * + * @param inArchiveItemIndex Index of item inside the SevenZip archive. Each + * file inside an archive is associated with a + * unique integer + * + * @param depthMap a concurrent hashmap which keeps track of the + * depth of all nested archives, key of objectID + * @param escapedFilePath the path to the archiveFileItem which has been + * escaped * * @return true if potential zip bomb, false otherwise */ @@ -551,7 +549,7 @@ boolean unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthM numItems = inArchive.getNumberOfItems(); progress.start(numItems); progressStarted = true; - + //setup the archive local root folder final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)); try { @@ -605,7 +603,7 @@ boolean unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthM inArchiveItemIndex, PropID.SIZE); if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && archiveItemSize != null && archiveItemSize > 0) { //if free space is known and file is not empty. String archiveItemPath = (String) inArchive.getProperty( - inArchiveItemIndex, PropID.PATH); + inArchiveItemIndex, PropID.PATH); long newDiskSpace = freeDiskSpace - archiveItemSize; if (newDiskSpace < MIN_FREE_DISK_SPACE) { String msg = NbBundle.getMessage(SevenZipExtractor.class, @@ -677,7 +675,7 @@ boolean unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthM inArchive.extract(extractionIndices, false, archiveCallBack); unpackSuccessful = unpackSuccessful & archiveCallBack.wasSuccessful(); - + archiveDetailsMap = null; // add them to the DB. We wait until the end so that we have the metadata on all of the @@ -795,140 +793,57 @@ private int[] getExtractableFilesFromDetailsMap( .mapToInt(Integer::intValue) .toArray(); } - + /** - * Stream used to unpack the archive to local file + * UnpackStream used by the SevenZipBindings to do archive extraction. A memory + * leak exists in the SevenZip library that will not let go of the streams until + * the entire archive extraction is complete. Instead of creating a new UnpackStream + * for every file in the archive, instead we just rebase our EncodedFileOutputStream pointer + * for every new file. */ - private abstract static class UnpackStream implements ISequentialOutStream { + private final static class UnpackStream implements ISequentialOutStream { - private OutputStream output; + private EncodedFileOutputStream output; private String localAbsPath; - - UnpackStream(String localAbsPath) { + private int bytesWritten; + + UnpackStream(String localAbsPath) throws IOException { + this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1); this.localAbsPath = localAbsPath; - try { - output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Error writing extracted file: " + localAbsPath, ex); //NON-NLS - } - - } - - public abstract long getSize(); - - OutputStream getOutput() { - return output; - } - - String getLocalAbsPath() { - return localAbsPath; - } - - public void close() { - if (output != null) { - try { - output.flush(); - output.close(); - output = null; - } catch (IOException e) { - logger.log(Level.SEVERE, "Error closing unpack stream for file: {0}", localAbsPath); //NON-NLS - } - } - } - } - - /** - * Stream used to unpack the archive of unknown size to local file - */ - private static class UnknownSizeUnpackStream extends UnpackStream { - - private long freeDiskSpace; - private boolean outOfSpace = false; - private long bytesWritten = 0; - - UnknownSizeUnpackStream(String localAbsPath, long freeDiskSpace) { - super(localAbsPath); - this.freeDiskSpace = freeDiskSpace; + this.bytesWritten = 0; + } + + public void setNewOutputStream(String localAbsPath) throws IOException { + this.output.close(); + this.output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1); + this.localAbsPath = localAbsPath; + this.bytesWritten = 0; } - - @Override - public long getSize() { - return this.bytesWritten; + + public int getSize() { + return bytesWritten; } - + @Override public int write(byte[] bytes) throws SevenZipException { try { - // If the content size is unknown, cautiously write to disk. - // Write only if byte array is less than 80% of the current - // free disk space. - if (freeDiskSpace == IngestMonitor.DISK_FREE_SPACE_UNKNOWN || bytes.length < 0.8 * freeDiskSpace) { - getOutput().write(bytes); - // NOTE: this method is called multiple times for a - // single extractSlow() call. Update bytesWritten and - // freeDiskSpace after every write operation. - this.bytesWritten += bytes.length; - this.freeDiskSpace -= bytes.length; - } else { - this.outOfSpace = true; - logger.log(Level.INFO, NbBundle.getMessage( - SevenZipExtractor.class, - "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.noSpace.msg")); - throw new SevenZipException( - NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.noSpace.msg")); - } + output.write(bytes); + this.bytesWritten += bytes.length; } catch (IOException ex) { throw new SevenZipException( - NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg", - getLocalAbsPath()), ex); + NbBundle.getMessage(SevenZipExtractor.class, + "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg", + localAbsPath), ex); } return bytes.length; } - - @Override - public void close() { - if (getOutput() != null) { - try { - getOutput().flush(); - getOutput().close(); - if (this.outOfSpace) { - Files.delete(Paths.get(getLocalAbsPath())); - } - } catch (IOException e) { - logger.log(Level.SEVERE, "Error closing unpack stream for file: {0}", getLocalAbsPath()); //NON-NLS - } - } - } - } - - /** - * Stream used to unpack the archive of known size to local file - */ - private static class KnownSizeUnpackStream extends UnpackStream { - - private long size; - - KnownSizeUnpackStream(String localAbsPath, long size) { - super(localAbsPath); - this.size = size; - } - - @Override - public long getSize() { - return this.size; - } - - @Override - public int write(byte[] bytes) throws SevenZipException { - try { - getOutput().write(bytes); - } catch (IOException ex) { - throw new SevenZipException( - NbBundle.getMessage(SevenZipExtractor.class, "EmbeddedFileExtractorIngestModule.ArchiveExtractor.UnpackStream.write.exception.msg", - getLocalAbsPath()), ex); - } - return bytes.length; + + public void close() throws IOException { + try(EncodedFileOutputStream out = output) { + out.flush(); + } } + } /** @@ -973,9 +888,8 @@ private static class StandardIArchiveExtractCallback private UnpackStream unpackStream = null; private final Map<Integer, InArchiveItemDetails> archiveDetailsMap; private final ProgressHandle progressHandle; - + private int inArchiveItemIndex; - private final long freeDiskSpace; private long createTimeInSeconds; private long modTimeInSeconds; @@ -992,7 +906,6 @@ public StandardIArchiveExtractCallback(ISevenZipInArchive inArchive, String password, long freeDiskSpace) { this.inArchive = inArchive; - this.freeDiskSpace = freeDiskSpace; this.progressHandle = progressHandle; this.archiveFile = archiveFile; this.archiveDetailsMap = archiveDetailsMap; @@ -1000,19 +913,21 @@ public StandardIArchiveExtractCallback(ISevenZipInArchive inArchive, } /** - * Get stream is called by the internal framework as it traverses - * the archive structure. The ISequentialOutStream is where the - * archive file contents will be expanded and written to the local disk. - * + * Get stream is called by the internal framework as it traverses the + * archive structure. The ISequentialOutStream is where the archive file + * contents will be expanded and written to the local disk. + * * Skips folders, as there is nothing to extract. - * - * @param inArchiveItemIndex current location of the - * @param mode Will always be EXTRACT + * + * @param inArchiveItemIndex current location of the + * @param mode Will always be EXTRACT + * * @return - * @throws SevenZipException + * + * @throws SevenZipException */ @Override - public ISequentialOutStream getStream(int inArchiveItemIndex, + public ISequentialOutStream getStream(int inArchiveItemIndex, ExtractAskMode mode) throws SevenZipException { this.inArchiveItemIndex = inArchiveItemIndex; @@ -1023,28 +938,36 @@ public ISequentialOutStream getStream(int inArchiveItemIndex, return null; } - final Long archiveItemSize = (Long) inArchive.getProperty( - inArchiveItemIndex, PropID.SIZE); final String localAbsPath = archiveDetailsMap.get( inArchiveItemIndex).getLocalAbsPath(); - - if (archiveItemSize != null) { - unpackStream = new SevenZipExtractor.KnownSizeUnpackStream( - localAbsPath, archiveItemSize); - } else { - unpackStream = new SevenZipExtractor.UnknownSizeUnpackStream( - localAbsPath, freeDiskSpace); + + //If the Unpackstream has been allocated, then set the Outputstream + //to another file rather than creating a new unpack stream. The 7Zip + //binding has a memory leak, so creating new unpack streams will not be + //dereferenced. As a fix, we create one UnpackStream, and mutate its state, + //so that there only exists one 8192 byte buffer in memory per archive. + try { + if (unpackStream != null) { + unpackStream.setNewOutputStream(localAbsPath); + } else { + unpackStream = new UnpackStream(localAbsPath); + } + } catch (IOException ex) { + logger.log(Level.WARNING, String.format("Error opening or setting new stream " //NON-NLS + + "for archive file at %s", localAbsPath), ex.getMessage()); //NON-NLS + return null; } return unpackStream; } /** - * Retrieves the file metadata from the archive before extraction. + * Retrieves the file metadata from the archive before extraction. * Called after getStream. - * + * * @param mode Will always be EXTRACT. - * @throws SevenZipException + * + * @throws SevenZipException */ @Override public void prepareOperation(ExtractAskMode mode) throws SevenZipException { @@ -1061,18 +984,18 @@ public void prepareOperation(ExtractAskMode mode) throws SevenZipException { : writeTime.getTime() / 1000; accessTimeInSeconds = accessTime == null ? 0L : accessTime.getTime() / 1000; - + progressHandle.progress(archiveFile.getName() + ": " + (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH), inArchiveItemIndex); - + } /** * Updates the unpackedNode data in the tree after the archive has been - * expanded to local disk. + * expanded to local disk. * - * @param result - ExtractOperationResult + * @param result - ExtractOperationResult * * @throws SevenZipException */ @@ -1089,7 +1012,7 @@ public void setOperationResult(ExtractOperationResult result) throws SevenZipExc localRelPath); return; } - + final String localAbsPath = archiveDetailsMap.get( inArchiveItemIndex).getLocalAbsPath(); if (result != ExtractOperationResult.OK) { @@ -1103,7 +1026,11 @@ public void setOperationResult(ExtractOperationResult result) throws SevenZipExc !(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER), 0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds, localRelPath); - unpackStream.close(); + try { + unpackStream.close(); + } catch (IOException e) { + logger.log(Level.WARNING, "Error closing unpack stream for file: {0}", localAbsPath); //NON-NLS + } } @Override @@ -1214,9 +1141,9 @@ private UnpackedNode addNode(UnpackedNode parent, List<String> tokenPath) { */ List<AbstractFile> getRootFileObjects() { List<AbstractFile> ret = new ArrayList<>(); - for (UnpackedNode child : rootNode.getChildren()) { + rootNode.getChildren().forEach((child) -> { ret.add(child.getFile()); - } + }); return ret; } @@ -1228,17 +1155,17 @@ List<AbstractFile> getRootFileObjects() { */ List<AbstractFile> getAllFileObjects() { List<AbstractFile> ret = new ArrayList<>(); - for (UnpackedNode child : rootNode.getChildren()) { + rootNode.getChildren().forEach((child) -> { getAllFileObjectsRec(ret, child); - } + }); return ret; } private void getAllFileObjectsRec(List<AbstractFile> list, UnpackedNode parent) { list.add(parent.getFile()); - for (UnpackedNode child : parent.getChildren()) { + parent.getChildren().forEach((child) -> { getAllFileObjectsRec(list, child); - } + }); } /** diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties index c461fc193f4c039a7ce27b5898d4a9bce229391e..11ace044fbccbadffc7e88a1290855d467570a61 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 ee3ba1c08912e5a9312cb95a64f630b2b366732d..23749b8cba9b6acb94e7bd3992973f923ca9a2ce 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java @@ -229,7 +229,9 @@ public ProcessResult process(AbstractFile file) { 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/FilesSetRulePanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java index c67d83080615cc076e9a13439ac810c373e223cf..7f8285c4a1126d1ea408872d05a4d042b0a7c89c 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java @@ -471,7 +471,7 @@ FilesSet.Rule.FileNameCondition getFileNameCondition() throws IllegalStateExcept 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 { @@ -556,7 +556,7 @@ FilesSet.Rule.ParentPathCondition getPathCondition() throws IllegalStateExceptio 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 diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index 81c4c9ac728f5db1c49a6088d02fc2bcfe4cbcec..13a507865836cd09144e3da1368023fe59591bb9 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -271,6 +271,9 @@ private String useDataTypeIcon(String dataType) { case TSK_ACCOUNT: in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/accounts.png"); //NON-NLS break; + case TSK_WIFI_NETWORK: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/network-wifi.png"); //NON-NLS + break; default: logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java index e62c58343c05f8360db46c0960068fa08c707d87..2275b63d5f3b5a83114bb8ddcc7c2526eff903e3 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.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 @@ public void run() { 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 cfdf6cf9736582da7bbd5f0b677b84694cf16b45..7f80201f26d9d93d622486af516bd713d816621f 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.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 @@ void reprocessJob(AutoIngestJob job) throws AutoIngestMonitorException { * @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<AutoIngestJob> 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/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index a0300e72c5e6c517712e82a63cd3ac19a233190b..bc78be5fd7f9c34bfde0a8634e7e323d91952bdf 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -233,19 +233,19 @@ && isDataSourcesTableStale() 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<GroupViewState> viewStateProperty() { @@ -254,7 +254,8 @@ public ReadOnlyObjectProperty<GroupViewState> viewStateProperty() { /** * Should the "forward" button on the history be enabled? - * @return + * + * @return */ public ReadOnlyBooleanProperty getCanAdvance() { return historyManager.getCanAdvance(); @@ -262,19 +263,19 @@ public ReadOnlyBooleanProperty getCanAdvance() { /** * 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) { @@ -283,7 +284,8 @@ public void advance(GroupViewState newState) { /** * Display the next group in the "forward" history stack - * @return + * + * @return */ public GroupViewState advance() { return historyManager.advance(); @@ -291,7 +293,8 @@ public GroupViewState advance() { /** * Display the previous group in the "back" history stack - * @return + * + * @return */ public GroupViewState retreat() { return historyManager.retreat(); @@ -500,10 +503,6 @@ public DrawableFile getFileFromID(Long fileID) throws TskCoreException { return drawableDB.getFileFromID(fileID); } - public ReadOnlyDoubleProperty regroupProgress() { - return groupManager.regroupProgress(); - } - public HashSetManager getHashSetManager() { return hashSetManager; } @@ -763,16 +762,17 @@ public void run() { // Cycle through all of the files returned and call processFile on each //do in transaction drawableDbTransaction = taskDB.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. + + /* 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; @@ -787,7 +787,7 @@ public void run() { 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) { @@ -807,12 +807,12 @@ public void run() { caseDbTransaction.commit(); caseDbTransaction = null; } - + // pass true so that groupmanager is notified of the changes taskDB.commitTransaction(drawableDbTransaction, true); drawableDbTransaction = null; - } catch (TskCoreException | InterruptedException 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()); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java index 88a7d38d70555ff54ac907f1cea0160fcd39a83b..8386999c69e917e477d411476fa35c67df82dc00 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java @@ -157,14 +157,14 @@ public void propertyChange(PropertyChangeEvent evt) { 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) { @@ -211,8 +211,8 @@ else if (FileTypeUtils.getAllSupportedExtensions().contains(file.getNameExtensio } } 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) { @@ -291,13 +291,13 @@ public void propertyChange(PropertyChangeEvent evt) { 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); } 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 bdb54c448fa8c77482eabfc471b8c4802c0a1035..5469afe4d3f90330d412d3d83ee6f360ab215ca6 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 static String getBlankGroupName() { DrawableGroup(GroupKey<?> groupKey, Set<Long> 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 @@ synchronized void addFile(Long f) { 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 f25142b406e4b3867933a43bbc8bd57127aed64e..bfd0fab496ea32a2fc1709ac117e30aaf1ec7a0c 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.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; @@ -278,10 +279,10 @@ public ListenableFuture<?> markGroupSeen(DrawableGroup group, boolean seen) { } /** - * 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 + * 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()) { @@ -505,6 +506,10 @@ public ReadOnlyDoubleProperty regroupProgress() { return regrouper.progressProperty(); } + public ReadOnlyStringProperty regroupMessage() { + return regrouper.messageProperty(); + } + @Subscribe synchronized public void handleTagAdded(ContentTagAddedEvent evt) { GroupKey<?> newGroupKey = null; @@ -619,7 +624,7 @@ synchronized public void handleFileUpdate(Collection<Long> updatedFileIDs) { * 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) { @@ -730,12 +735,7 @@ synchronized public void setCollaborativeMode(Boolean newValue) { */ @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<AttrValType extends Comparable<AttrValType>> extends LoggedTask<Void> { private final DataSource dataSource; @@ -743,16 +743,14 @@ class ReGroupTask<AttrValType extends Comparable<AttrValType>> extends LoggedTas private final GroupSortBy sortBy; private final SortOrder sortOrder; - private final ProgressHandle groupProgress; - ReGroupTask(DataSource dataSource, DrawableAttribute<AttrValType> 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 @@ -761,7 +759,8 @@ protected Void call() throws Exception { if (isCancelled()) { return null; } - groupProgress.start(); + + updateProgress(-1, 1); analyzedGroups.clear(); unSeenGroups.clear(); @@ -769,7 +768,7 @@ protected Void call() throws Exception { // Get the list of group keys Multimap<DataSource, AttrValType> 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<DataSource, AttrValType> valForDataSource : valsByDataSource.entries()) { @@ -777,9 +776,8 @@ protected Void call() throws Exception { 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); } @@ -808,8 +806,8 @@ protected Void call() throws Exception { } } } finally { - groupProgress.finish(); updateProgress(1, 1); + updateMessage(""); } return null; } @@ -827,12 +825,9 @@ protected void done() { } /** - * 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 b02e2e249634b56770113652d3855930554d80b2..c0ee4488b1224a05bb03389113617a246e25068b 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 @@ <?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.StackPane?> -<fx:root id="AnchorPane" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" type="javafx.scene.layout.AnchorPane" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1"> +<fx:root id="AnchorPane" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" type="javafx.scene.layout.AnchorPane" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1"> <children> <BorderPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <right> <HBox alignment="CENTER_RIGHT" prefHeight="-1.0" prefWidth="-1.0" spacing="5.0" BorderPane.alignment="CENTER_RIGHT"> <children> + <Label fx:id="staleLabel" text="Some data may be out of date. Enable listening to ingest to update."> + <graphic> + <ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@../images/information.png" /> + </image> + </ImageView> + </graphic> + </Label> <StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" HBox.hgrow="NEVER"> <children> <ProgressBar id="progBar" fx:id="fileTaskProgresBar" focusTraversable="false" maxHeight="-1.0" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-1.0" prefHeight="24.0" prefWidth="-1.0" progress="0.0" visible="true" /> @@ -31,37 +40,27 @@ <Insets /> </HBox.margin> </StackPane> - <StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" HBox.hgrow="NEVER"> - <children> - <ProgressBar fx:id="bgTaskProgressBar" maxHeight="-1.0" maxWidth="-1.0" minHeight="-Infinity" minWidth="-1.0" prefHeight="24.0" prefWidth="-1.0" progress="0.0" StackPane.alignment="CENTER" /> - <Label fx:id="bgTaskLabel" alignment="CENTER" cache="false" contentDisplay="CENTER" disable="false" focusTraversable="false" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" text="" StackPane.alignment="CENTER"> - <StackPane.margin> - <Insets left="3.0" right="3.0" /> - </StackPane.margin> - <padding> - <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" /> - </padding></Label> - </children> -<HBox.margin> -<Insets right="5.0" /> -</HBox.margin> - </StackPane> </children> <BorderPane.margin> <Insets left="10.0" /> </BorderPane.margin> </HBox> </right> -<left><Label fx:id="staleLabel" text="Some data may be out of date. Enable listening to ingest to update." BorderPane.alignment="CENTER"> -<graphic><ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> -<image> -<Image url="@../images/information.png" /> -</image></ImageView> -</graphic> -<BorderPane.margin> -<Insets bottom="5.0" left="5.0" right="10.0" top="5.0" /> -</BorderPane.margin></Label> -</left> + <left> + <StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" BorderPane.alignment="CENTER"> + <children> + <ProgressBar fx:id="regroupProgressBar" maxHeight="-1.0" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-1.0" prefHeight="24.0" prefWidth="500.0" progress="0.0" StackPane.alignment="CENTER_LEFT" /> + <Label fx:id="regroupLabel" cache="false" contentDisplay="CENTER" disable="false" focusTraversable="false" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="500.0" text="" textOverrun="CENTER_ELLIPSIS" StackPane.alignment="CENTER_LEFT"> + <StackPane.margin> + <Insets left="3.0" right="3.0" /> + </StackPane.margin> + <padding> + <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" /> + </padding> + </Label> + </children> + </StackPane> + </left> </BorderPane> </children> </fx:root> diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.java index 2355d6d2a0710206b752aab222faf05c2556f6c0..d7224c4a581dd17374b10d71b4c2637ea2125d2b 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.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 @@ void initialize() { 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 StatusBar(ImageGalleryController controller) { } catch (IOException exception) { throw new RuntimeException(exception); } - } } diff --git a/NEWS.txt b/NEWS.txt index f93db10f9c48f27314cb88b3c595c563881f80ae..6130ca50dafb6ac67859b53824cebdb78138c2ca 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -1,8 +1,8 @@ ----------------- VERSION 4.9.0 -------------- +---------------- 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 +- 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 -------------- diff --git a/RecentActivity/nbproject/project.xml b/RecentActivity/nbproject/project.xml index 7b7ae183476b262231ecd0e83f83c130f1233834..020377beec80b4bd4599e3a9288c71abd1216f1a 100644 --- a/RecentActivity/nbproject/project.xml +++ b/RecentActivity/nbproject/project.xml @@ -64,9 +64,7 @@ </run-dependency> </dependency> </module-dependencies> - <public-packages> - <package>org.sleuthkit.autopsy.recentactivity</package> - </public-packages> + <public-packages/> <class-path-extension> <runtime-relative-path>ext/gson-2.1.jar</runtime-relative-path> <binary-origin>release/modules/ext/gson-2.1.jar</binary-origin> diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java index 0f9a98cd88c12037dfed55b4e826edbe97c267ad..69381d8a139695dd176ea0f0a28c3fc726e2e657 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java @@ -39,6 +39,7 @@ import java.io.IOException; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.NetworkUtils; import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.AbstractFile; @@ -163,7 +164,7 @@ private void getHistory() { NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), - (Util.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS + (NetworkUtils.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY, historyFile, bbattributes); if (bbart != null) { @@ -286,7 +287,7 @@ private void getBookmark() { } else { date = Long.valueOf(0); } - String domain = Util.extractDomain(url); + String domain = NetworkUtils.extractDomain(url); try { BlackboardArtifact bbart = bookmarkFile.newArtifact(ARTIFACT_TYPE.TSK_WEB_BOOKMARK); Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); @@ -496,7 +497,7 @@ private void getDownload() { //bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "Recent Activity", "Last Visited", time)); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), time)); - String domain = Util.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : ""); //NON-NLS + String domain = NetworkUtils.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : ""); //NON-NLS bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), domain)); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, @@ -590,7 +591,7 @@ private void getLogin() { NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), - (Util.extractDomain((result.get("origin_url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS + (NetworkUtils.extractDomain((result.get("origin_url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, NbBundle.getMessage(this.getClass(), "Chrome.parentModuleName"), ((result.get("username_value").toString() != null) ? result.get("username_value").toString().replaceAll("'", "''") : ""))); //NON-NLS diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java index 90a0a4d7fa62aeebec90b662dc340cd4423bb3c9..29844b08272dce0d698f2a926023b07d50e5da59 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java @@ -26,6 +26,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.ExecUtil; +import org.sleuthkit.autopsy.coreutils.NetworkUtils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -609,6 +610,6 @@ private String extractDomain(String url) { return null; } - return Util.extractDomain(url); + return NetworkUtils.extractDomain(url); } } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index 70607f361cd9cb266904ed2fd882704224844397..6612feb60cdc8eb432579e057a3e7b03f30126ca 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -374,7 +374,9 @@ private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFil // Add all "usb" dataType nodes to collection of BlackboardArtifacts // that we will submit in a ModuleDataEvent for additional processing. Collection<BlackboardArtifact> usbBBartifacts = new ArrayList<>(); - + // Add all "ssid" dataType nodes to collection of BlackboardArtifacts + // that we will submit in a ModuleDataEvent for additional processing. + Collection<BlackboardArtifact> wifiBBartifacts = new ArrayList<>(); for (int i = 0; i < len; i++) { Element tempnode = (Element) children.item(i); @@ -734,6 +736,7 @@ private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFil bbart.addAttributes(bbattributes); // index the artifact for keyword search this.indexArtifact(bbart); + wifiBBartifacts.add(bbart); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding SSID artifact to blackboard."); //NON-NLS } @@ -756,6 +759,9 @@ private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFil if (!usbBBartifacts.isEmpty()) { IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(moduleName, BlackboardArtifact.ARTIFACT_TYPE.TSK_DEVICE_ATTACHED, usbBBartifacts)); } + if (!wifiBBartifacts.isEmpty()){ + IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(moduleName, BlackboardArtifact.ARTIFACT_TYPE.TSK_WIFI_NETWORK, wifiBBartifacts)); + } return true; } catch (FileNotFoundException ex) { logger.log(Level.SEVERE, "Error finding the registry file."); //NON-NLS diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java index d1874d40108ff187955c48a4d47c0fdc94e534c5..6cb51795f306575bf60b0b8c207285d171740677 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java @@ -35,6 +35,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.services.FileManager; 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.autopsy.ingest.IngestServices; @@ -669,6 +670,6 @@ private String extractDomain(String url) { return null; } - return Util.extractDomain(url); + return NetworkUtils.extractDomain(url); } } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java index 9bff0673940404f79243256a3ff7ddb813edb81e..fd9630cebd5d52cee4b84b04de2ec097f22cd79e 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java @@ -84,83 +84,6 @@ public static String readFile(String path) throws IOException { } } - /** - * - * @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; - } - - /** - * - * @param value - * @return empty string if no domain name was found - */ - public static String extractDomain(String value) { - if (value == null) { - return ""; - - } - String result = ""; - // String domainPattern = "(\\w+)\\.(AC|AD|AE|AERO|AF|AG|AI|AL|AM|AN|AO|AQ|AR|ARPA|AS|ASIA|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BIZ|BJ|BM|BN|BO|BR|BS|BT|BV|BW|BY|BZ|CA|CAT|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|COM|COOP|CR|CU|CV|CW|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EDU|EE|EG|ER|ES|ET|EU|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GOV|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|INFO|INT|IO|IQ|IR|IS|IT|JE|JM|JO|JOBS|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MG|MH|MIL|MK|ML|MM|MN|MO|MOBI|MP|MQ|MR|MS|MT|MU|MUSEUM|MV|MW|MX|MY|MZ|NA|NAME|NC|NE|NET|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|ORG|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PRO|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|ST|SU|SV|SX|SY|SZ|TC|TD|TEL|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TP|TR|TRAVEL|TT|TV|TW|TZ|UA|UG|UK|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|XXX|YE|YT|ZA|ZM|ZW(co\\.[a-z].))"; - // Pattern p = Pattern.compile(domainPattern,Pattern.CASE_INSENSITIVE); - // Matcher m = p.matcher(value); - // while (m.find()) { - // result = value.substring(m.start(0),m.end(0)); - // } - - try { - URL url = new URL(value); - result = url.getHost(); - } catch (MalformedURLException ex) { - //do not log if not a valid URL, and handle later - //Logger.getLogger(Util.class.getName()).log(Level.SEVERE, null, ex); - } - - //was not a valid URL, try a less picky method - if (result == null || result.trim().isEmpty()) { - return getBaseDomain(value); - } - return result; - } - public static String getFileName(String value) { String filename = ""; String filematch = "^([a-zA-Z]\\:)(\\\\[^\\\\/:*?<>\"|]*(?<!\\[ \\]))*(\\.[a-zA-Z]{2,6})$"; //NON-NLS diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 91571da9bf06d2c7255119e69dae7749312ebe8e..202947f0332cf57e29745ecbf1052c347d437c8d 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Sat, 13 Oct 2018 21:02:18 -0400 +#Tue, 13 Nov 2018 17:30:09 -0500 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 @@ -8,4 +8,4 @@ SplashRunningTextBounds=0,289,538,18 SplashRunningTextColor=0x0 SplashRunningTextFontSize=19 -currentVersion=Autopsy 4.9.0 +currentVersion=Autopsy 4.9.1 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 90fb6cf2768571767ca85c2a7077b22ed51f9ba8..11be8888476d9cffbc5b31d4e61bbc1112f499c7 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Sat, 13 Oct 2018 21:02:18 -0400 -CTL_MainWindow_Title=Autopsy 4.9.0 -CTL_MainWindow_Title_No_Project=Autopsy 4.9.0 +#Tue, 13 Nov 2018 17:30:09 -0500 +CTL_MainWindow_Title=Autopsy 4.9.1 +CTL_MainWindow_Title_No_Project=Autopsy 4.9.1 diff --git a/release/update_autopsy_version.pl b/release/update_autopsy_version.pl deleted file mode 100755 index 0a115c8dafb7220c423f600dffca6af9b6952a41..0000000000000000000000000000000000000000 --- a/release/update_autopsy_version.pl +++ /dev/null @@ -1,268 +0,0 @@ -#!/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; - $_ = <STDIN>; - 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 (<CONF_IN>) { - 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 (<CONF_IN>) { - 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 (<CONF_IN>) { - 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 deleted file mode 100755 index e630e4890bb6c0666fea434a4c84fd6f8dbb6454..0000000000000000000000000000000000000000 --- a/release/update_sleuthkit_version.pl +++ /dev/null @@ -1,199 +0,0 @@ -#!/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 (<CONF_IN>) { - if (/name="TSK_VERSION" value=/) { - print CONF_OUT " <property name=\"TSK_VERSION\" value=\"${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); - -} - - - -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 (<CONF_IN>) { - 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 (<CONF_IN>) { - if (/<runtime-relative-path>ext\/sleuthkit-postgresql/) { - print CONF_OUT " <runtime-relative-path>ext/sleuthkit-postgresql-${VER}.jar</runtime-relative-path>\n"; - $found++; - } - elsif (/<binary-origin>release\/modules\/ext\/sleuthkit-postgresql/) { - print CONF_OUT " <binary-origin>release/modules/ext/sleuthkit-postgresql-${VER}.jar</binary-origin>\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