diff --git a/Core/manifest.mf b/Core/manifest.mf index 494ba39afc..00681f3e7f 100644 --- a/Core/manifest.mf +++ b/Core/manifest.mf @@ -2,7 +2,7 @@ Manifest-Version: 1.0 OpenIDE-Module: org.sleuthkit.autopsy.core/10 OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml -OpenIDE-Module-Implementation-Version: 30 +OpenIDE-Module-Implementation-Version: 31 OpenIDE-Module-Requires: org.openide.windows.WindowManager AutoUpdate-Show-In-Client: true AutoUpdate-Essential-Module: true diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index c7ceb01a4a..3fd1ed05d9 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -126,5 +126,5 @@ nbm.homepage=http://www.sleuthkit.org/ nbm.module.author=Brian Carrier nbm.needs.restart=true source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar -spec.version.base=10.18 +spec.version.base=10.19 diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 4aa97960f3..64d9509e59 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -251,7 +251,7 @@ 3 - 1.3 + 1.4 diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java index 3d610890da..a64809a07d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java @@ -50,7 +50,7 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.casemodule.CaseCloseAction") @ActionRegistration(displayName = "#CTL_CaseCloseAct", lazy = false) @ActionReferences(value = { - @ActionReference(path = "Toolbars/Case", position = 106)}) + @ActionReference(path = "Toolbars/Case", position = 107)}) public final class CaseCloseAction extends CallableSystemAction implements Presenter.Toolbar { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index 881e60236e..1429a60312 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -464,7 +464,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi // correlate on blackboard artifact attributes if they exist and supported BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node); if (bbArtifact != null && CentralRepository.isEnabled()) { - ret.addAll(CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbArtifact)); + ret.addAll(CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbArtifact)); } // we can correlate based on the MD5 if it is enabled diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java old mode 100644 new mode 100755 index 14724fafb7..5cbc3307a0 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java @@ -19,7 +19,9 @@ package org.sleuthkit.autopsy.centralrepository.datamodel; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.logging.Level; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; @@ -32,6 +34,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.CommunicationsUtils; import org.sleuthkit.datamodel.HashUtility; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -59,25 +62,60 @@ public class CorrelationAttributeUtil { return Bundle.CorrelationAttributeUtil_emailaddresses_text(); } + // Defines which artifact types act as the sources for CR data. + // Most notably, does not include KEYWORD HIT, CALLLOGS, MESSAGES, CONTACTS + // TSK_INTERESTING_ARTIFACT_HIT (See JIRA-6129 for more details on the + // interesting artifact hit). + + // IMPORTANT: This set should be updated for new artifacts types that need to + // be inserted into the CR. + private static final Set SOURCE_TYPES_FOR_CR_INSERT = new HashSet() {{ + add(ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID()); + add(ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID()); + add(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()); + add(ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()); + add(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()); + add(ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID()); + add(ARTIFACT_TYPE.TSK_WIFI_NETWORK_ADAPTER.getTypeID()); + add(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING.getTypeID()); + add(ARTIFACT_TYPE.TSK_BLUETOOTH_ADAPTER.getTypeID()); + add(ARTIFACT_TYPE.TSK_DEVICE_INFO.getTypeID()); + add(ARTIFACT_TYPE.TSK_SIM_ATTACHED.getTypeID()); + add(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()); + add(ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()); + }}; + /** * Makes zero to many correlation attribute instances from the attributes of - * an artifact. - * - * IMPORTANT: The correlation attribute instances are NOT added to the - * central repository by this method. - * - * @param artifact An artifact. + * artifacts that have correlatable data. The intention of this method is to + * use the results to save to the CR, not to correlate with them. If you + * want to correlate, please use makeCorrAttrsForCorrelation. An artifact that can + * have correlatable data != An artifact that should be the source of data + * in the CR, so results may be un-necessarily incomplete. + * + * @param artifact An artifact. * * @return A list, possibly empty, of correlation attribute instances for - * the artifact. + * the artifact. */ - public static List makeCorrAttrsFromArtifact(BlackboardArtifact artifact) { - return makeCorrAttrsFromArtifact(artifact, true ); + public static List makeCorrAttrsToSave(BlackboardArtifact artifact) { + if(SOURCE_TYPES_FOR_CR_INSERT.contains(artifact.getArtifactTypeID())) { + // Restrict the correlation attributes to use for saving. + // The artifacts which are suitable for saving are a subset of the + // artifacts that are suitable for correlating. + return makeCorrAttrsForCorrelation(artifact); + } + // Return an empty collection. + return new ArrayList<>(); } - + /** * Makes zero to many correlation attribute instances from the attributes of - * an artifact. + * artifacts that have correlatable data. The intention of this method is to + * use the results to correlate with, not to save. If you + * want to save, please use makeCorrAttrsToSave. An artifact that can + * have correlatable data != An artifact that should be the source of data + * in the CR, so results may be too lenient. * * IMPORTANT: The correlation attribute instances are NOT added to the * central repository by this method. @@ -91,26 +129,22 @@ public class CorrelationAttributeUtil { * checking is easy to forget, while catching exceptions is enforced. * * @param artifact An artifact. - * @param resolveSourceArtifact A flag to indicate whether to resolve the - * source artifact, if the given artifact is - * of type TSK_INTERESTING_ARTIFACT_HIT. * * @return A list, possibly empty, of correlation attribute instances for * the artifact. */ - public static List makeCorrAttrsFromArtifact(BlackboardArtifact artifact, boolean resolveSourceArtifact) { + public static List makeCorrAttrsForCorrelation(BlackboardArtifact artifact) { List correlationAttrs = new ArrayList<>(); - - // If the artifact is of type TSK_INTERESTING_ARTIFACT_HIT, and the caller - // has not indicated to resolve the source artifact, then return an empty list. - if ((artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) && (resolveSourceArtifact == false) ) { - return correlationAttrs; - } try { BlackboardArtifact sourceArtifact = getCorrAttrSourceArtifact(artifact); if (sourceArtifact != null) { int artifactTypeID = sourceArtifact.getArtifactTypeID(); - if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID() + if (artifactTypeID == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { + BlackboardAttribute setNameAttr = sourceArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)); + if (setNameAttr != null && CorrelationAttributeUtil.getEmailAddressAttrDisplayName().equals(setNameAttr.getValueString())) { + makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, CorrelationAttributeInstance.EMAIL_TYPE_ID); + } + } else if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID() || artifactTypeID == ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID() || artifactTypeID == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() || artifactTypeID == ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) { @@ -143,6 +177,11 @@ public class CorrelationAttributeUtil { } else if (artifactTypeID == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) { makeCorrAttrFromAcctArtifact(correlationAttrs, sourceArtifact); + + } else if (artifactTypeID == ARTIFACT_TYPE.TSK_CONTACT.getTypeID() + || artifactTypeID == ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() + || artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) { + makeCorrAttrsFromCommunicationArtifacts(correlationAttrs, sourceArtifact); } } } catch (CentralRepoException ex) { @@ -158,6 +197,47 @@ public class CorrelationAttributeUtil { return correlationAttrs; } + /** + * Makes a correlation attribute instance from a phone number attribute of an + * artifact. + * + * @param corrAttrInstances Correlation attributes will be added to this. + * @param artifact An artifact with a phone number attribute. + * + * @throws TskCoreException If there is an error querying the case + * database. + * @throws CentralRepoException If there is an error querying the central + * repository. + */ + private static void makeCorrAttrsFromCommunicationArtifacts(List corrAttrInstances, BlackboardArtifact artifact) throws TskCoreException, CentralRepoException { + CorrelationAttributeInstance corrAttr = null; + + /* + * Extract the phone number from the artifact attribute. + */ + String value = null; + if (null != artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER))) { + value = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)).getValueString(); + } else if (null != artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM))) { + value = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)).getValueString(); + } else if (null != artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO))) { + value = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)).getValueString(); + } + + /* + * Normalize the phone number. + */ + if (value != null) { + if(CommunicationsUtils.isValidPhoneNumber(value)) { + value = CommunicationsUtils.normalizePhoneNum(value); + corrAttr = makeCorrAttr(artifact, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.PHONE_TYPE_ID), value); + if(corrAttr != null) { + corrAttrInstances.add(corrAttr); + } + } + } + } + /** * Gets the associated artifact of a "meta-artifact" such as an interesting * artifact hit artifact. @@ -186,33 +266,29 @@ public class CorrelationAttributeUtil { /** * Makes a correlation attribute instance for an account artifact. - * + * * Also creates an account in the CR DB if it doesn't exist. * * IMPORTANT: The correlation attribute instance is NOT added to the central * repository by this method. * - * TODO (Jira-6088): The methods in this low-level, utility class should - * throw exceptions instead of logging them. The reason for this is that the - * clients of the utility class, not the utility class itself, should be in - * charge of error handling policy, per the Autopsy Coding Standard. Note - * that clients of several of these methods currently cannot determine - * whether receiving a null return value is an error or not, plus null - * checking is easy to forget, while catching exceptions is enforced. - * * @param corrAttrInstances A list of correlation attribute instances. * @param acctArtifact An account artifact. * * @return The correlation attribute instance. */ private static void makeCorrAttrFromAcctArtifact(List corrAttrInstances, BlackboardArtifact acctArtifact) throws TskCoreException, CentralRepoException { - + // Get the account type from the artifact BlackboardAttribute accountTypeAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE)); String accountTypeStr = accountTypeAttribute.getValueString(); + // @@TODO Vik-6136: CR currently does not know of custom account types. + // Ensure there is a predefined account type for this account. + Account.Type predefinedAccountType = Account.Type.PREDEFINED_ACCOUNT_TYPES.stream().filter(type -> type.getTypeName().equalsIgnoreCase(accountTypeStr)).findAny().orElse(null); + // do not create any correlation attribute instance for a Device account - if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false) { + if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false && predefinedAccountType != null) { // Get the corresponding CentralRepoAccountType from the database. CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr); @@ -441,10 +517,10 @@ public class CorrelationAttributeUtil { file.getId()); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Error querying case database (%s)", file), ex); // NON-NLS + logger.log(Level.SEVERE, String.format("Error querying case database (%s)", file), ex); // NON-NLS return null; } catch (CentralRepoException ex) { - logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", file), ex); // NON-NLS + logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", file), ex); // NON-NLS return null; } catch (CorrelationAttributeNormalizationException ex) { logger.log(Level.WARNING, String.format("Error creating correlation attribute instance (%s)", file), ex); // NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java index 1df0e10dc6..8f648a9945 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java @@ -296,7 +296,7 @@ final class CaseEventListener implements PropertyChangeListener { return; } - List convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbArtifact); + List convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbArtifact); for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) { eamArtifact.setComment(comment); try { @@ -369,7 +369,7 @@ final class CaseEventListener implements PropertyChangeListener { if (!hasTagWithConflictingKnownStatus) { //Get the correlation atttributes that correspond to the current BlackboardArtifactTag if their status should be changed //with the initial set of correlation attributes this should be a single correlation attribute - List convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbTag.getArtifact()); + List convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbTag.getArtifact()); for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) { CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus()); } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index b2bff50c98..843e476499 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -455,11 +455,8 @@ public class IngestEventsListener { List eamArtifacts = new ArrayList<>(); for (BlackboardArtifact bbArtifact : bbArtifacts) { - // If the incoming artifact is of type TSK_INTERESTING_ARTIFACT_HIT, - // do not resolve to the source artifact, as correlation attributes - // for the source artifact would have already been created, - // when the event for that source artifact was received. - List convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbArtifact, false); + // makeCorrAttrToSave will filter out artifacts which should not be sources of CR data. + List convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsToSave(bbArtifact); for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) { try { // Only do something with this artifact if it's unique within the job diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties index 0b30e2584f..a9f4326b26 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties @@ -84,3 +84,6 @@ ManageCasesDialog.closeButton.text=Close ManageCasesDialog.notesLabel.text=Notes: ManageCasesDialog.dataSourcesLabel.text=Data Sources: ManageCasesDialog.caseInfoLabel.text=Case Info: +GlobalSettingsPanel.bnTestConfigure.text=Test +GlobalSettingsPanel.testStatusLabel.toolTipText= +GlobalSettingsPanel.testStatusLabel.text= diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED index 395330b4db..0fc6762cc3 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties-MERGED @@ -12,15 +12,15 @@ EamDbSettingsDialog.fcDatabasePath.title=Select location for central_repository. EamDbSettingsDialog.lbDatabaseType.text=Database Type : EamDbSettingsDialog.lbSingleUserSqLite.text=SQLite should only be used by one examiner at a time. EamDbSettingsDialog.okButton.connectionErrorMsg.text=Failed to connect to central repository database. -EamDbSettingsDialog.okButton.corruptDatabaseExists.message=Database exists but is not the right format. Manually delete it or choose a different path (if applicable). -EamDbSettingsDialog.okButton.corruptDatabaseExists.title=Error Loading Database -EamDbSettingsDialog.okButton.createDbDialog.message=Database does not exist, would you like to create it? -EamDbSettingsDialog.okButton.createDbDialog.title=Database Does Not Exist -EamDbSettingsDialog.okButton.createDbError.title=Unable to Create Database -EamDbSettingsDialog.okButton.createPostgresDbError.message=Unable to create Postgres Database, please ensure address, port, and login credentials are correct for Postgres server and try again. -EamDbSettingsDialog.okButton.createSQLiteDbError.message=Unable to create SQLite Database, please ensure location exists and you have write permissions and try again. -EamDbSettingsDialog.okButton.databaseConnectionFailed.message=Unable to connect to database. Please check your settings and try again. -EamDbSettingsDialog.okButton.databaseConnectionFailed.title=Database Connection Failed +EamDbSettingsDialog.okButton.corruptDatabaseExists.message=Central Repository Database exists but is not the right format. Manually delete it or choose a different path (if applicable). +EamDbSettingsDialog.okButton.corruptDatabaseExists.title=Error Loading Central Repository Database +EamDbSettingsDialog.okButton.createDbDialog.message=Central Repository Database does not exist, would you like to create it? +EamDbSettingsDialog.okButton.createDbDialog.title=Central Repository Database Does Not Exist +EamDbSettingsDialog.okButton.createDbError.title=Unable to Create Central Repository Database +EamDbSettingsDialog.okButton.createPostgresDbError.message=Unable to create Postgres Central Repository Database, please ensure address, port, and login credentials are correct for Postgres server and try again. +EamDbSettingsDialog.okButton.createSQLiteDbError.message=Unable to create SQLite Central Repository Database, please ensure location exists and you have write permissions and try again. +EamDbSettingsDialog.okButton.databaseConnectionFailed.message=Unable to connect to Central Repository Database. Please check your settings and try again. +EamDbSettingsDialog.okButton.databaseConnectionFailed.title=Central Repository Database Connection Failed EamDbSettingsDialog.okButton.errorMsg.text=Please restart Autopsy to begin using the new database platform. EamDbSettingsDialog.okButton.errorTitle.text=Restart Required. EamDbSettingsDialog.textPrompt.dbName=Database Name @@ -33,12 +33,16 @@ EamDbSettingsDialog.validation.finished=Click OK to save your database settings EamDbSettingsDialog.validation.incompleteFields=Fill in all values for the selected database. EamOptionsController.moduleErr=Error processing value changes. EamOptionsController.moduleErr.msg=Value change processing failed. +GlobalSettingsPanel.askForCentralRepoDbChoice.customPostgrestChoice.text=Configure PostgreSQL +GlobalSettingsPanel.askForCentralRepoDbChoice.disableChoice.text=Disable Central Repository +GlobalSettingsPanel.askForCentralRepoDbChoice.sqliteChoice.text=Use SQLite GlobalSettingsPanel.onMultiUserChange.disabledMu.description=The Central Repository will be reconfigured to use a local SQLite database. GlobalSettingsPanel.onMultiUserChange.disabledMu.description2=Press Configure PostgreSQL to change to a PostgreSQL database. GlobalSettingsPanel.onMultiUserChange.disabledMu.title=Central Repository Change Necessary GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL database? GlobalSettingsPanel.onMultiUserChange.enable.description2=The Central Repository stores hash values and accounts from past cases. GlobalSettingsPanel.onMultiUserChange.enable.title=Use with Central Repository? +GlobalSettingsPanel.testCurrentConfiguration.dbDoesNotExist.message=Database does not exist. GlobalSettingsPanel.validationErrMsg.ingestRunning=You cannot change settings while ingest is running. GlobalSettingsPanel.validationerrMsg.mustConfigure=Configure the database to enable this module. ManageCasesDialog.title.text=Manage Cases @@ -147,3 +151,6 @@ ManageCasesDialog.closeButton.text=Close ManageCasesDialog.notesLabel.text=Notes: ManageCasesDialog.dataSourcesLabel.text=Data Sources: ManageCasesDialog.caseInfoLabel.text=Case Info: +GlobalSettingsPanel.bnTestConfigure.text=Test +GlobalSettingsPanel.testStatusLabel.toolTipText= +GlobalSettingsPanel.testStatusLabel.text= diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle_ja.properties index 96e0634149..e1c3de35ad 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle_ja.properties @@ -143,3 +143,5 @@ ManageCasesDialog.closeButton.text=\u9589\u3058\u308b ManageCasesDialog.notesLabel.text=\u5099\u8003: ManageCasesDialog.dataSourcesLabel.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9: ManageCasesDialog.caseInfoLabel.text=\u30b1\u30fc\u30b9\u60c5\u5831: +GlobalSettingsPanel.bnTestConfigure.text=\u69cb\u6210 +GlobalSettingsPanel.testStatusLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u5b9f\u884c\u4e2d\u306f\u30bb\u30f3\u30c8\u30e9\u30eb\u30fb\u30ec\u30dd\u30b8\u30c8\u30ea\u30fc\u8a2d\u5b9a\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093! diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java index 8c3a308e4e..fb78f049b5 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/EamDbSettingsDialog.java @@ -87,7 +87,6 @@ public class EamDbSettingsDialog extends JDialog { private final Collection textBoxes; private final TextBoxChangedListener textBoxChangedListener; private final CentralRepoDbManager manager = new CentralRepoDbManager(); - private final boolean isMultiUserSelectable = CentralRepoDbManager.isPostgresMultiuserAllowed(); private final DbChoiceRenderer DB_CHOICE_RENDERER = new DbChoiceRenderer(); public EamDbSettingsDialog() { @@ -95,7 +94,7 @@ public class EamDbSettingsDialog extends JDialog { } private boolean isDbChoiceSelectable(CentralRepoDbChoice item) { - return (item != CentralRepoDbChoice.POSTGRESQL_MULTIUSER || isMultiUserSelectable); + return (item != CentralRepoDbChoice.POSTGRESQL_MULTIUSER || manager.isPostgresMultiuserAllowed()); } @@ -159,15 +158,15 @@ public class EamDbSettingsDialog extends JDialog { * @param dialog If non-null value, validates settings and updates 'okay' button enabled state. * @return Whether or not the ultimate status after prompts is okay to continue. */ - @NbBundle.Messages({"EamDbSettingsDialog.okButton.corruptDatabaseExists.title=Error Loading Database", - "EamDbSettingsDialog.okButton.corruptDatabaseExists.message=Database exists but is not the right format. Manually delete it or choose a different path (if applicable).", - "EamDbSettingsDialog.okButton.createDbDialog.title=Database Does Not Exist", - "EamDbSettingsDialog.okButton.createDbDialog.message=Database does not exist, would you like to create it?", - "EamDbSettingsDialog.okButton.databaseConnectionFailed.title=Database Connection Failed", - "EamDbSettingsDialog.okButton.databaseConnectionFailed.message=Unable to connect to database. Please check your settings and try again.", - "EamDbSettingsDialog.okButton.createSQLiteDbError.message=Unable to create SQLite Database, please ensure location exists and you have write permissions and try again.", - "EamDbSettingsDialog.okButton.createPostgresDbError.message=Unable to create Postgres Database, please ensure address, port, and login credentials are correct for Postgres server and try again.", - "EamDbSettingsDialog.okButton.createDbError.title=Unable to Create Database"}) + @NbBundle.Messages({"EamDbSettingsDialog.okButton.corruptDatabaseExists.title=Error Loading Central Repository Database", + "EamDbSettingsDialog.okButton.corruptDatabaseExists.message=Central Repository Database exists but is not the right format. Manually delete it or choose a different path (if applicable).", + "EamDbSettingsDialog.okButton.createDbDialog.title=Central Repository Database Does Not Exist", + "EamDbSettingsDialog.okButton.createDbDialog.message=Central Repository Database does not exist, would you like to create it?", + "EamDbSettingsDialog.okButton.databaseConnectionFailed.title=Central Repository Database Connection Failed", + "EamDbSettingsDialog.okButton.databaseConnectionFailed.message=Unable to connect to Central Repository Database. Please check your settings and try again.", + "EamDbSettingsDialog.okButton.createSQLiteDbError.message=Unable to create SQLite Central Repository Database, please ensure location exists and you have write permissions and try again.", + "EamDbSettingsDialog.okButton.createPostgresDbError.message=Unable to create Postgres Central Repository Database, please ensure address, port, and login credentials are correct for Postgres server and try again.", + "EamDbSettingsDialog.okButton.createDbError.title=Unable to Create Central Repository Database"}) private static boolean promptTestStatusWarnings(CentralRepoDbManager manager, EamDbSettingsDialog dialog) { if (manager.getStatus() == DatabaseTestResult.CONNECTION_FAILED) { JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), @@ -181,31 +180,43 @@ public class EamDbSettingsDialog extends JDialog { Bundle.EamDbSettingsDialog_okButton_corruptDatabaseExists_title(), JOptionPane.WARNING_MESSAGE); } else if (manager.getStatus() == DatabaseTestResult.DB_DOES_NOT_EXIST) { - //database doesn't exist. do you want to create? - if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(), - Bundle.EamDbSettingsDialog_okButton_createDbDialog_message(), - Bundle.EamDbSettingsDialog_okButton_createDbDialog_title(), - JOptionPane.YES_NO_OPTION)) { - onUserPromptCreateDb(manager, dialog); - } + promptCreateDatabase(manager, dialog); } return (manager.getStatus() == DatabaseTestResult.TESTED_OK); } - - /** - * When a new database needs to be created on user selecting cr, this code will be ran when user selects create cr. - * @param manager The manager handling the database creation. - * @param dialog The dialog that prompted database creation. + + + /** + * This method prompts the user whether or not they would like to create a database in the instance that + * it doesn't exist. + * @param manager The manager to use when setting up the database. + * @param dialog If non-null value, validates settings and updates 'okay' + * button enabled state. + * + * @return Whether or not the ultimate status after prompts is okay. */ - private static void onUserPromptCreateDb(CentralRepoDbManager manager, EamDbSettingsDialog dialog) { - try { - manager.createDb(); - } catch (CentralRepoException e) { - onPromptStatusError(manager); + public static boolean promptCreateDatabase(CentralRepoDbManager manager, EamDbSettingsDialog dialog) { + //database doesn't exist. do you want to create? + if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(), + Bundle.EamDbSettingsDialog_okButton_createDbDialog_message(), + Bundle.EamDbSettingsDialog_okButton_createDbDialog_title(), + JOptionPane.YES_NO_OPTION)) { + try { + manager.createDb(); + + } catch (CentralRepoException e) { + onPromptStatusError(manager); + return false; + } + + if (dialog != null) { + dialog.valid(); + } + return true; } - if (dialog != null) - dialog.valid(); + + return manager.testStatus() == DatabaseTestResult.TESTED_OK; } @@ -604,8 +615,8 @@ public class EamDbSettingsDialog extends JDialog { "EamDbSettingsDialog.okButton.errorMsg.text=Please restart Autopsy to begin using the new database platform.", "EamDbSettingsDialog.okButton.connectionErrorMsg.text=Failed to connect to central repository database."}) private void bnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOkActionPerformed - testStatusAndCreate(this, manager, this); - dispose(); + if (testStatusAndCreate(this, manager, this)) + dispose(); }//GEN-LAST:event_bnOkActionPerformed @@ -659,6 +670,7 @@ public class EamDbSettingsDialog extends JDialog { return true; } + /** * This method returns if changes to the central repository configuration were * successfully applied. @@ -887,7 +899,6 @@ public class EamDbSettingsDialog extends JDialog { @Override public void changedUpdate(DocumentEvent e) { - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); manager.clearStatus(); updateFullDbPath(); valid(); @@ -895,7 +906,6 @@ public class EamDbSettingsDialog extends JDialog { @Override public void insertUpdate(DocumentEvent e) { - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); manager.clearStatus(); updateFullDbPath(); valid(); @@ -903,7 +913,6 @@ public class EamDbSettingsDialog extends JDialog { @Override public void removeUpdate(DocumentEvent e) { - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); manager.clearStatus(); updateFullDbPath(); valid(); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form index e5e9405b8b..158a83b7d2 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form @@ -61,7 +61,7 @@ - + @@ -137,10 +137,6 @@ - - - - @@ -149,11 +145,22 @@ - + - + + + + + + + + + + + + @@ -177,7 +184,13 @@ - + + + + + + + @@ -221,6 +234,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -256,7 +303,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java index 3dca79e99a..7cb1e566dd 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.centralrepository.optionspanel; -import java.awt.Cursor; import java.awt.EventQueue; import org.sleuthkit.autopsy.coreutils.Logger; import java.beans.PropertyChangeEvent; @@ -43,7 +42,13 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.PostgresCentralRepoSettings; import org.sleuthkit.autopsy.centralrepository.datamodel.SqliteCentralRepoSettings; import java.awt.Component; +import java.beans.PropertyChangeSupport; import java.util.logging.Level; +import javax.swing.ImageIcon; +import org.openide.util.ImageUtilities; +import org.sleuthkit.autopsy.centralrepository.datamodel.DatabaseTestResult; + + /** * Main settings panel for the Central Repository @@ -54,25 +59,57 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(GlobalSettingsPanel.class.getName()); private static final Set INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED); - private final IngestJobEventPropertyChangeListener ingestJobEventListener; + // this allows property change events to be fired at a static level but listened to by instances + private static final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(GlobalSettingsPanel.class); + + // tracks the last known instance property change listener so that only one GlobalSettingsPanel is listening for events + private static PropertyChangeListener lastRegistered = null; + + private final IngestJobEventPropertyChangeListener ingestJobEventListener; + + private final ImageIcon goodIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/good.png", false)); + private final ImageIcon badIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/bad.png", false)); + + /** * Creates new form EamOptionsPanel */ public GlobalSettingsPanel() { ingestJobEventListener = new IngestJobEventPropertyChangeListener(); - - // listen for change events in currently saved choice - CentralRepoDbManager.addPropertyChangeListener((PropertyChangeEvent evt) -> ingestStateUpdated(Case.isCaseOpen())); initComponents(); customizeComponents(); + setupSettingsChangeListeners(); addIngestJobEventsListener(); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> { //disable when case is open, enable when case is closed ingestStateUpdated(evt.getNewValue() != null); }); } + + + /** + * Sets up this instance's listener for the GlobalSettingsPanel's changes. + */ + private void setupSettingsChangeListeners() { + // listen for change events in currently saved choice + if (lastRegistered != null) { + CentralRepoDbManager.removePropertyChangeListener(lastRegistered); + GlobalSettingsPanel.propertyChangeSupport.removePropertyChangeListener(lastRegistered); + } + + lastRegistered = this::onSettingsChange; + CentralRepoDbManager.addPropertyChangeListener(lastRegistered); + GlobalSettingsPanel.propertyChangeSupport.addPropertyChangeListener(lastRegistered); + } + + + private void onSettingsChange(PropertyChangeEvent evt) { + ingestStateUpdated(Case.isCaseOpen()); + clearStatus(); + } + private void customizeComponents() { setName(NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.pnCorrelationProperties.border.title")); } @@ -121,24 +158,23 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i public static void onMultiUserChange(Component parent, boolean muPreviouslySelected, boolean muCurrentlySelected) { boolean crEnabled = CentralRepoDbUtil.allowUseOfCentralRepository(); boolean crMultiUser = CentralRepoDbManager.getSavedDbChoice() == CentralRepoDbChoice.POSTGRESQL_MULTIUSER; - boolean crDisabledDueToFailure = CentralRepoDbManager.isDisabledDueToFailure(); if (!muPreviouslySelected && muCurrentlySelected) { SwingUtilities.invokeLater(() -> { if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(parent, "" + "
" - + "

" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.enable.description") + "

" - + "

" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.enable.description2") + "

" + + "

" + Bundle.GlobalSettingsPanel_onMultiUserChange_enable_description() + "

" + + "

" + Bundle.GlobalSettingsPanel_onMultiUserChange_enable_description2() + "

" + "
" + "", - NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.enable.title"), + Bundle.GlobalSettingsPanel_onMultiUserChange_enable_title(), JOptionPane.YES_NO_OPTION)) { // setup database for CR CentralRepoDbUtil.setUseCentralRepo(true); CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.POSTGRESQL_MULTIUSER); - handleDbChange(parent); + checkStatusAndCreateDb(parent); } }); } // moving from selected to not selected && 'PostgreSQL using multi-user settings' is selected @@ -148,12 +184,23 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i }); } // changing multi-user settings connection && 'PostgreSQL using multi-user settings' is selected && // central repo either enabled or was disabled due to error - else if (muPreviouslySelected && muCurrentlySelected && crMultiUser && (crEnabled || crDisabledDueToFailure)) { - // test databse for CR change - CentralRepoDbUtil.setUseCentralRepo(true); - handleDbChange(parent); + else if (muPreviouslySelected && muCurrentlySelected && crEnabled && crMultiUser) { + GlobalSettingsPanel.propertyChangeSupport.firePropertyChange("multiuserSettingsChanged", null, null); + checkStatusAndCreateDb(parent); } } + + + /** + * Checks the status of current connectivity for CR and reports any issues. Will also prompt user to create + * database if cr database is absent. + * @param parent the parent component to which the dialogs will be associated. + */ + private static void checkStatusAndCreateDb(Component parent) { + SwingUtilities.invokeLater(() -> { + EamDbSettingsDialog.testStatusAndCreate(parent, new CentralRepoDbManager()); + }); + } /** * This method is called when a user must select a new database other than @@ -165,28 +212,27 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i @NbBundle.Messages({ "GlobalSettingsPanel.onMultiUserChange.disabledMu.title=Central Repository Change Necessary", "GlobalSettingsPanel.onMultiUserChange.disabledMu.description=The Central Repository will be reconfigured to use a local SQLite database.", - "GlobalSettingsPanel.onMultiUserChange.disabledMu.description2=Press Configure PostgreSQL to change to a PostgreSQL database." + "GlobalSettingsPanel.onMultiUserChange.disabledMu.description2=Press Configure PostgreSQL to change to a PostgreSQL database.", + "GlobalSettingsPanel.askForCentralRepoDbChoice.sqliteChoice.text=Use SQLite", + "GlobalSettingsPanel.askForCentralRepoDbChoice.customPostgrestChoice.text=Configure PostgreSQL", + "GlobalSettingsPanel.askForCentralRepoDbChoice.disableChoice.text=Disable Central Repository" }) private static void askForCentralRepoDbChoice(Component parent) { - // disable central repository until user makes choice - CentralRepoDbUtil.setUseCentralRepo(false); - CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.DISABLED, false); - Object[] options = { - "Use SQLite", - "Configure PostgreSQL", - "Disable Central Repository" + Bundle.GlobalSettingsPanel_askForCentralRepoDbChoice_sqliteChoice_text(), + Bundle.GlobalSettingsPanel_askForCentralRepoDbChoice_customPostgrestChoice_text(), + Bundle.GlobalSettingsPanel_askForCentralRepoDbChoice_disableChoice_text() }; int result = JOptionPane.showOptionDialog( parent, "" + "
" - + "

" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.disabledMu.description") + "

" - + "

" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.disabledMu.description2") + "

" + + "

" + Bundle.GlobalSettingsPanel_onMultiUserChange_disabledMu_description() + "

" + + "

" + Bundle.GlobalSettingsPanel_onMultiUserChange_disabledMu_description2() + "

" + "
" + "", - NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.disabledMu.title"), + Bundle.GlobalSettingsPanel_onMultiUserChange_disabledMu_title(), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, @@ -200,13 +246,55 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i invokeCrChoice(parent, CentralRepoDbChoice.POSTGRESQL_CUSTOM); } } + + @NbBundle.Messages({ + "GlobalSettingsPanel.testCurrentConfiguration.dbDoesNotExist.message=Database does not exist.", + }) + private boolean testCurrentConfiguration() { + if (CentralRepoDbManager.getSavedDbChoice() == null || + CentralRepoDbManager.getSavedDbChoice() == CentralRepoDbChoice.DISABLED || + !CentralRepoDbUtil.allowUseOfCentralRepository()) + return false; + + CentralRepoDbManager manager = new CentralRepoDbManager(); + DatabaseTestResult testResult = manager.testStatus(); + + // if database doesn't exist, prompt user to create database + if (testResult == DatabaseTestResult.DB_DOES_NOT_EXIST) { + boolean success = EamDbSettingsDialog.promptCreateDatabase(manager, null); + if (success) + testResult = DatabaseTestResult.TESTED_OK; + } + + // display to the user the status + switch (testResult) { + case TESTED_OK: return showStatusOkay(); + case DB_DOES_NOT_EXIST: return showStatusFail(Bundle.GlobalSettingsPanel_testCurrentConfiguration_dbDoesNotExist_message()); + case SCHEMA_INVALID: return showStatusFail(Bundle.EamDbSettingsDialog_okButton_corruptDatabaseExists_message()); + case CONNECTION_FAILED: + default: + return showStatusFail(Bundle.EamDbSettingsDialog_okButton_databaseConnectionFailed_message()); + } + } - private static void handleDbChange(Component parent) { - SwingUtilities.invokeLater(() -> { - if (!EamDbSettingsDialog.testStatusAndCreate(parent, new CentralRepoDbManager())) { - CentralRepoDbManager.disableDueToFailure(); - } - }); + private boolean showStatusOkay() { + return setStatus(goodIcon, " "); + } + + private boolean showStatusFail(String message) { + return setStatus(badIcon, message); + } + + private void clearStatus() { + setStatus(null, " "); + } + + private boolean setStatus(ImageIcon icon, String text) { + synchronized (testStatusLabel) { + testStatusLabel.setIcon(icon); + testStatusLabel.setText(text); + return true; + } } /** @@ -230,6 +318,8 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i lbDbPlatformValue = new javax.swing.JLabel(); lbDbNameValue = new javax.swing.JLabel(); lbDbLocationValue = new javax.swing.JLabel(); + bnTestConfigure = new javax.swing.JButton(); + testStatusLabel = new javax.swing.JLabel(); pnCorrelationProperties = new javax.swing.JPanel(); bnManageTypes = new javax.swing.JButton(); correlationPropertiesScrollPane = new javax.swing.JScrollPane(); @@ -278,6 +368,20 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i } }); + org.openide.awt.Mnemonics.setLocalizedText(bnTestConfigure, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.bnTestConfigure.text")); // NOI18N + bnTestConfigure.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnTestConfigureActionPerformed(evt); + } + }); + + testStatusLabel.setFont(testStatusLabel.getFont().deriveFont(testStatusLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + testStatusLabel.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(testStatusLabel, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.testStatusLabel.text")); // NOI18N + testStatusLabel.setToolTipText(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.testStatusLabel.toolTipText")); // NOI18N + testStatusLabel.setMaximumSize(new java.awt.Dimension(387, 16)); + testStatusLabel.setPreferredSize(new java.awt.Dimension(387, 16)); + javax.swing.GroupLayout pnDatabaseConfigurationLayout = new javax.swing.GroupLayout(pnDatabaseConfiguration); pnDatabaseConfiguration.setLayout(pnDatabaseConfigurationLayout); pnDatabaseConfigurationLayout.setHorizontalGroup( @@ -285,9 +389,6 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .addGroup(pnDatabaseConfigurationLayout.createSequentialGroup() .addContainerGap() .addGroup(pnDatabaseConfigurationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(pnDatabaseConfigurationLayout.createSequentialGroup() - .addComponent(bnDbConfigure) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGroup(pnDatabaseConfigurationLayout.createSequentialGroup() .addGroup(pnDatabaseConfigurationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(lbDbPlatformTypeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) @@ -295,9 +396,18 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .addComponent(lbDbLocationLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(pnDatabaseConfigurationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lbDbNameValue, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 936, Short.MAX_VALUE) + .addComponent(lbDbNameValue, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lbDbPlatformValue, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(lbDbLocationValue, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, pnDatabaseConfigurationLayout.createSequentialGroup() + .addComponent(lbDbLocationValue, javax.swing.GroupLayout.DEFAULT_SIZE, 255, Short.MAX_VALUE) + .addGap(681, 681, 681)))) + .addGroup(pnDatabaseConfigurationLayout.createSequentialGroup() + .addComponent(bnDbConfigure) + .addGap(18, 18, 18) + .addComponent(bnTestConfigure) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testStatusLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 675, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) ); pnDatabaseConfigurationLayout.setVerticalGroup( pnDatabaseConfigurationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -315,7 +425,11 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .addComponent(lbDbLocationLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lbDbLocationValue, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(bnDbConfigure) + .addGroup(pnDatabaseConfigurationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(testStatusLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(pnDatabaseConfigurationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(bnDbConfigure) + .addComponent(bnTestConfigure))) .addGap(8, 8, 8)) ); @@ -359,7 +473,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i pnCorrelationPropertiesLayout.setVerticalGroup( pnCorrelationPropertiesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnCorrelationPropertiesLayout.createSequentialGroup() - .addComponent(correlationPropertiesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 28, Short.MAX_VALUE) + .addComponent(correlationPropertiesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 24, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(bnManageTypes) .addGap(8, 8, 8)) @@ -472,7 +586,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .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) - .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.DEFAULT_SIZE, 1010, Short.MAX_VALUE) + .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.DEFAULT_SIZE, 1016, Short.MAX_VALUE) .addComponent(organizationPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(casesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(jPanel1Layout.createSequentialGroup() @@ -532,7 +646,6 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i boolean changed = invokeCrChoice(this, null); if (changed) { load(); // reload db settings content and update buttons - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); } }//GEN-LAST:event_bnDbConfigureActionPerformed @@ -549,20 +662,14 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i private void cbUseCentralRepoActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbUseCentralRepoActionPerformed //if saved setting is disabled checkbox should be disabled already store(); - - // if moving to using CR, multi-user mode is disabled and selection is multiuser settings, set to disabled - if (cbUseCentralRepo.isSelected() - && !CentralRepoDbManager.isPostgresMultiuserAllowed() - && CentralRepoDbManager.getSavedDbChoice() == CentralRepoDbChoice.POSTGRESQL_MULTIUSER) { - - CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.DISABLED); - } - load(); this.ingestStateUpdated(Case.isCaseOpen()); - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); }//GEN-LAST:event_cbUseCentralRepoActionPerformed + private void bnTestConfigureActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestConfigureActionPerformed + testCurrentConfiguration(); + }//GEN-LAST:event_bnTestConfigureActionPerformed + @Override @Messages({"GlobalSettingsPanel.validationerrMsg.mustConfigure=Configure the database to enable this module."}) public void load() { @@ -691,6 +798,8 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i enableDatabaseConfigureButton(cbUseCentralRepo.isSelected() && !caseIsOpen); } + + /** * Enable the Configure button @@ -702,8 +811,10 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i private void enableDatabaseConfigureButton(Boolean enable) { boolean ingestRunning = IngestManager.getInstance().isIngestRunning(); ingestRunningWarningLabel.setVisible(ingestRunning); + pnDatabaseConfiguration.setEnabled(enable && !ingestRunning); bnDbConfigure.setEnabled(enable && !ingestRunning); + bnTestConfigure.setEnabled(enable && !ingestRunning); lbDbLocationLabel.setEnabled(enable && !ingestRunning); lbDbLocationValue.setEnabled(enable && !ingestRunning); lbDbNameLabel.setEnabled(enable && !ingestRunning); @@ -738,6 +849,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton bnDbConfigure; private javax.swing.JButton bnManageTypes; + private javax.swing.JButton bnTestConfigure; private javax.swing.JPanel casesPanel; private javax.swing.JScrollPane casesScrollPane; private javax.swing.JTextArea casesTextArea; @@ -762,5 +874,6 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i private javax.swing.JPanel pnDatabaseConfiguration; private javax.swing.JButton showCasesButton; private javax.swing.JTextField tbOops; + private javax.swing.JLabel testStatusLabel; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java index f36f8ab520..5eb521f3d5 100644 --- a/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonpropertiessearch/InterCasePanel.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.commonpropertiessearch; import org.sleuthkit.autopsy.guiutils.DataSourceComboBoxModel; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -119,6 +120,13 @@ public final class InterCasePanel extends javax.swing.JPanel { this.correlationTypeFilters = new HashMap<>(); try { List types = CentralRepository.getInstance().getDefinedCorrelationTypes(); + Collections.sort(types, new Comparator() { + //The types should be sorted so that the File type is the first item in the combo box. + @Override + public int compare(CorrelationAttributeInstance.Type type1, CorrelationAttributeInstance.Type type2) { + return Integer.compare(type1.getId(), type2.getId()); + } + }); for (CorrelationAttributeInstance.Type type : types) { correlationTypeFilters.put(type.getDisplayName(), type); this.correlationTypeComboBox.addItem(type.getDisplayName()); @@ -295,7 +303,7 @@ public final class InterCasePanel extends javax.swing.JPanel { private void correlationTypeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_correlationTypeComboBoxActionPerformed - boolean enableFileTypesFilter = this.correlationTypeComboBox.getSelectedItem().equals("Files"); + boolean enableFileTypesFilter = this.correlationTypeComboBox.getSelectedItem().equals("Files"); categoriesLabel.setEnabled(enableFileTypesFilter); allFileCategoriesRadioButton.setEnabled(enableFileTypesFilter); selectedFileCategoriesButton.setEnabled(enableFileTypesFilter); @@ -329,8 +337,8 @@ public final class InterCasePanel extends javax.swing.JPanel { // End of variables declaration//GEN-END:variables /** - * Get the map of cases which was used to populate the combo box on - * this panel. + * Get the map of cases which was used to populate the combo box on this + * panel. * * @return an unmodifiable copy of the map of cases */ @@ -339,8 +347,8 @@ public final class InterCasePanel extends javax.swing.JPanel { } /** - * Set the datamodel for the combo box which displays the cases in - * the central repository + * Set the datamodel for the combo box which displays the cases in the + * central repository * * @param dataSourceComboBoxModel the DataSourceComboBoxModel to use */ diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageViewer.java index 5e90d83c98..38e245aec3 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageViewer.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageViewer.java @@ -247,6 +247,8 @@ public class MessageViewer extends JPanel implements RelationshipsViewer { */ private void showMessagesPane() { switchCard("messages"); + Outline outline = rootTablePane.getOutlineView().getOutline(); + outline.clearSelection(); } /** diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.java index 3c967db3fd..c5c92b11c0 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/AnnotationsContentViewer.java @@ -198,7 +198,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data startSection(html, "Central Repository Comments"); List instancesList = new ArrayList<>(); if (artifact != null) { - instancesList.addAll(CorrelationAttributeUtil.makeCorrAttrsFromArtifact(artifact)); + instancesList.addAll(CorrelationAttributeUtil.makeCorrAttrsForCorrelation(artifact)); } try { List artifactTypes = CentralRepository.getInstance().getDefinedCorrelationTypes(); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 15ee654347..4571bcc3df 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -77,9 +77,9 @@ DataResultViewerThumbnail.switchPage.done.errMsg=Error making thumbnails: {0} AboutWindowPanel.actVerboseLogging.text=Activate verbose logging OptionsCategory_Name_Multi_User_Settings=Multi-User OptionsCategory_Keywords_Multi_User_Options=Multi-User Settings -MultiUserSettingsPanel.lbSolrSettings.text=Solr Settings +MultiUserSettingsPanel.lbSolrSettings.text=Solr Server Settings MultiUserSettingsPanel.cbEnableMultiUser.text=Enable multi-user cases -MultiUserSettingsPanel.lbDatabaseSettings.text=Database Settings +MultiUserSettingsPanel.lbDatabaseSettings.text=Database Server Settings MultiUserSettingsPanel.validationErrMsg.incomplete=Fill in all values MultiUserSettingsPanel.nonWindowsOs.msg=Multi-user cases are only available on Windows platforms MultiUserSettingsPanel.validationErrMsg.invalidDatabasePort=Invalid database port number @@ -107,7 +107,7 @@ MultiUserSettingsPanel.tbSolrHostname.toolTipText=Hostname or IP Address MultiUserSettingsPanel.tbSolrPort.toolTipText=Port Number MultiUserSettingsPanel.lbTestMessageService.text= MultiUserSettingsPanel.bnTestMessageService.text=Test -MultiUserSettingsPanel.lbMessageServiceSettings.text=ActiveMQ Message Service Settings +MultiUserSettingsPanel.lbMessageServiceSettings.text=ActiveMQ Message Server Settings MultiUserSettingsPanel.tbMsgPort.toolTipText=Port Number MultiUserSettingsPanel.tbMsgPort.text= MultiUserSettingsPanel.tbMsgUsername.toolTipText=User Name (optional) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java index 864f04f28a..5671ca6051 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java @@ -97,7 +97,7 @@ class GetSCOTask implements Runnable { logger.log(Level.WARNING, "Unable to get correlation type or value to determine value for O column for artifact", ex); } } else { - List listOfPossibleAttributes = CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbArtifact); + List listOfPossibleAttributes = CorrelationAttributeUtil.makeCorrAttrsForCorrelation(bbArtifact); if (listOfPossibleAttributes.size() > 1) { //Don't display anything if there is more than 1 correlation property for an artifact but let the user know description = Bundle.GetSCOTask_occurrences_multipleProperties(); diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java index 5832625b73..b25f83d195 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java @@ -23,6 +23,10 @@ import com.google.common.cache.CacheBuilder; import com.google.common.io.Files; import java.awt.Image; import java.awt.image.BufferedImage; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.nio.file.Paths; @@ -79,6 +83,9 @@ import org.sleuthkit.autopsy.textextractors.TextExtractor; import org.sleuthkit.autopsy.textextractors.TextExtractorFactory; import org.sleuthkit.autopsy.textsummarizer.TextSummarizer; import org.sleuthkit.autopsy.textsummarizer.TextSummary; +import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException; +import org.sleuthkit.autopsy.texttranslation.TextTranslationService; +import org.sleuthkit.autopsy.texttranslation.TranslationException; /** * Main class to perform the file search. @@ -304,9 +311,113 @@ class FileSearch { } catch (TskCoreException ex) { logger.log(Level.WARNING, "Error getting children for file: " + file.getId(), ex); } - image = image == null ? image : image.getScaledInstance(ImageUtils.ICON_SIZE_MEDIUM, ImageUtils.ICON_SIZE_MEDIUM, + image = image == null ? image : image.getScaledInstance(ImageUtils.ICON_SIZE_MEDIUM, ImageUtils.ICON_SIZE_MEDIUM, Image.SCALE_SMOOTH); - return new TextSummary(getFirstLines(file), image, countOfImages); + String summaryText = null; + if (file.getMd5Hash() != null) { + try { + summaryText = getSavedSummary(Paths.get(Case.getCurrentCaseThrows().getCacheDirectory(), "summaries", file.getMd5Hash() + "-default-" + PREVIEW_SIZE + "-translated.txt").toString()); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to retrieve saved summary. No case is open.", ex); + } + } + if (StringUtils.isBlank(summaryText)) { + String firstLines = getFirstLines(file); + String translatedFirstLines = getTranslatedVersion(firstLines); + if (!StringUtils.isBlank(translatedFirstLines)) { + summaryText = translatedFirstLines; + if (file.getMd5Hash() != null) { + try { + saveSummary(summaryText, Paths.get(Case.getCurrentCaseThrows().getCacheDirectory(), "summaries", file.getMd5Hash() + "-default-" + PREVIEW_SIZE + "-translated.txt").toString()); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Unable to save translated summary. No case is open.", ex); + } + } + } else { + summaryText = firstLines; + } + } + return new TextSummary(summaryText, image, countOfImages); + } + + /** + * Provide an English version of the specified String if it is not English, + * translation is enabled, and it can be translated. + * + * @param documentString The String to provide an English version of. + * + * @return The English version of the provided String, or null if no + * translation occurred. + */ + private static String getTranslatedVersion(String documentString) { + try { + TextTranslationService translatorInstance = TextTranslationService.getInstance(); + if (translatorInstance.hasProvider()) { + String translatedResult = translatorInstance.translate(documentString); + if (translatedResult.isEmpty() == false) { + return translatedResult; + } + } + } catch (NoServiceProviderException | TranslationException ex) { + logger.log(Level.INFO, "Error translating string for summary", ex); + } + return null; + } + + /** + * Find and load a saved summary from the case folder for the specified + * file. + * + * @param summarySavePath The full path for the saved summary file. + * + * @return The summary found given the specified path, null if no summary + * was found. + */ + private static String getSavedSummary(String summarySavePath) { + if (summarySavePath == null) { + return null; + } + File savedFile = new File(summarySavePath); + if (savedFile.exists()) { + try (BufferedReader bReader = new BufferedReader(new FileReader(savedFile))) { + // pass the path to the file as a parameter + StringBuilder sBuilder = new StringBuilder(); + String sCurrentLine = bReader.readLine(); + while (sCurrentLine != null) { + sBuilder.append(sCurrentLine).append('\n'); + sCurrentLine = bReader.readLine(); + } + return sBuilder.toString(); + } catch (IOException ingored) { + //summary file may not exist or may be incomplete in which case return null so a summary can be generated + return null; //no saved summary was able to be found + } + } else { + try { //if the file didn't exist make sure the parent directories exist before we move on to creating a summary + Files.createParentDirs(savedFile); + } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to create summaries directory in case folder for file at: " + summarySavePath, ex); + } + return null; //no saved summary was able to be found + } + + } + + /** + * Save a summary at the specified location. + * + * @param summary The text of the summary being saved. + * @param summarySavePath The full path for the saved summary file. + */ + private static void saveSummary(String summary, String summarySavePath) { + if (summarySavePath == null) { + return; //can't save a summary if we don't have a path + } + try (FileWriter myWriter = new FileWriter(summarySavePath)) { + myWriter.write(summary); + } catch (IOException ex) { + logger.log(Level.WARNING, "Unable to save summary at: " + summarySavePath, ex); + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java index 0462e77842..00ef520934 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -310,7 +310,7 @@ final public class MapPanel extends javax.swing.JPanel { void setZoom(int zoom) { zoomChanging = true; mapViewer.setZoom(zoom); - zoomSlider.setValue((zoomSlider.getMaximum() + zoomSlider.getMinimum()) - zoom); + zoomSlider.setValue(zoom); zoomChanging = false; } @@ -572,6 +572,7 @@ final public class MapPanel extends javax.swing.JPanel { zoomSlider.setOrientation(javax.swing.JSlider.VERTICAL); zoomSlider.setPaintTicks(true); zoomSlider.setSnapToTicks(true); + zoomSlider.setInverted(true); zoomSlider.setMinimumSize(new java.awt.Dimension(35, 100)); zoomSlider.setOpaque(false); zoomSlider.setPreferredSize(new java.awt.Dimension(35, 190)); diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java index 67de1e55ba..13a23b30e3 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2019-2020 Basis Technology Corp. * contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,9 +25,9 @@ import java.util.Map; import org.openide.util.NbBundle.Messages; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil; -import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList.GeoWaypoint; -import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList; +import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil; +import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil.InvalidJsonException; +import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoints; /** * A Route represents a TSK_GPS_ROUTE artifact which has a start and end point @@ -42,8 +42,6 @@ public class Route extends GeoPath { // This list is not expected to change after construction so the // constructor will take care of creating an unmodifiable List private final List propertiesList; - - private static final TskGeoWaypointsUtil attributeUtil = new TskGeoWaypointsUtil(); /** * Construct a route for the given artifact. @@ -119,9 +117,13 @@ public class Route extends GeoPath { } if (attribute != null) { - GeoWaypointList waypoints = attributeUtil.fromAttribute(attribute); - - for(GeoWaypoint waypoint: waypoints) { + GeoWaypoints waypoints; + try { + waypoints = BlackboardJsonAttrUtil.fromAttribute(attribute, GeoWaypoints.class); + } catch (InvalidJsonException ex) { + throw new GeoLocationDataException(String.format("Unable to parse waypoints in TSK_GEO_WAYPOINTS attribute (artifact object ID =%d)", artifact.getId()), ex); + } + for (GeoWaypoints.Waypoint waypoint : waypoints) { addToPath(new Waypoint(artifact, label, null, waypoint.getLatitude(), waypoint.getLongitude(), waypoint.getAltitude(), null, attributeMap, this)); } } else { diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Track.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Track.java index 213bd6b5f5..68da242b8a 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Track.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Track.java @@ -1,5 +1,4 @@ /* - * * Autopsy Forensic Browser * * Copyright 2020 Basis Technology Corp. @@ -26,26 +25,24 @@ import java.util.Map; import org.openide.util.NbBundle.Messages; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil; -import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList; -import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList.GeoTrackPoint; +import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil; +import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil.InvalidJsonException; +import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints; /** * A GPS track with which wraps the TSK_GPS_TRACK artifact. */ -public final class Track extends GeoPath{ +public final class Track extends GeoPath { private final Long startTimestamp; private final Long endTimeStamp; - - private static final TskGeoTrackpointsUtil attributeUtil = new TskGeoTrackpointsUtil(); /** * Construct a new Track for the given artifact. - * + * * @param artifact - * - * @throws GeoLocationDataException + * + * @throws GeoLocationDataException */ public Track(BlackboardArtifact artifact) throws GeoLocationDataException { this(artifact, Waypoint.getAttributesFromArtifactAsMap(artifact)); @@ -53,49 +50,49 @@ public final class Track extends GeoPath{ /** * Construct a Track for the given artifact and attributeMap. - * - * @param artifact TSK_GPD_TRACK artifact - * @param attributeMap Map of the artifact attributes - * - * @throws GeoLocationDataException + * + * @param artifact TSK_GPD_TRACK artifact + * @param attributeMap Map of the artifact attributes + * + * @throws GeoLocationDataException */ private Track(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { super(artifact, getTrackName(attributeMap)); - GeoTrackPointList points = getPointsList(attributeMap); + GeoTrackPoints points = getPointsList(attributeMap); buildPath(points, artifact); startTimestamp = points.getStartTime(); endTimeStamp = points.getEndTime(); } - + /** * Returns the start time of this track. - * - * @return Earliest time, or null if none was available. - * (seconds from java epoch) + * + * @return Earliest time, or null if none was available. (seconds from java + * epoch) */ public Long getStartTime() { return startTimestamp; } - + /** * Returns the end time of this track. - * - * @return Earliest timestamp, or null if none was available. - * (seconds from java epoch) + * + * @return Earliest timestamp, or null if none was available. (seconds from + * java epoch) */ public Long getEndTime() { return endTimeStamp; } /** - * Return the name of the track from the attributeMap. - * Track name is stored in the attribute TSK_NAME - * + * Return the name of the track from the attributeMap. Track name is stored + * in the attribute TSK_NAME + * * @param attributeMap - - * @return Track name or empty string if none was available. + * + * @return Track name or empty string if none was available. */ private static String getTrackName(Map attributeMap) { BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); @@ -105,38 +102,41 @@ public final class Track extends GeoPath{ /** * Create the list of TrackWaypoints from the GeoTrackPoint list. - * - * @param points List of GeoTrackPoints - * + * + * @param points GeoTrackPoints object. * @param artifact The artifact to which these points belong - * - * @throws GeoLocationDataException + * + * @throws GeoLocationDataException */ @Messages({ "# {0} - track name", "GEOTrack_point_label_header=Trackpoint for track: {0}" }) - private void buildPath(GeoTrackPointList points, BlackboardArtifact artifact) - throws GeoLocationDataException { - for(GeoTrackPoint point: points) { + private void buildPath(GeoTrackPoints points, BlackboardArtifact artifact) throws GeoLocationDataException { + for (GeoTrackPoints.TrackPoint point : points) { addToPath(new TrackWaypoint(artifact, Bundle.GEOTrack_point_label_header(getLabel()), point)); } } /** - * Returns the list of GeoTrackPoints from the attributeMap. Creates the + * Returns the list of GeoTrackPoints from the attributeMap. Creates the * GeoTrackPoint list from the TSK_GEO_TRACKPOINTS attribute. - * + * * @param attributeMap Map of artifact attributes. - * + * * @return GeoTrackPoint list empty list if the attribute was not found. + * + * @throws GeoLocationDataException */ - private GeoTrackPointList getPointsList(Map attributeMap) { + private GeoTrackPoints getPointsList(Map attributeMap) throws GeoLocationDataException { BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS); if (attribute != null) { - return attributeUtil.fromAttribute(attribute); + try { + return BlackboardJsonAttrUtil.fromAttribute(attribute, GeoTrackPoints.class); + } catch (InvalidJsonException ex) { + throw new GeoLocationDataException("Unable to parse track points in TSK_GEO_TRACKPOINTS attribute", ex); + } } - return null; } @@ -149,16 +149,16 @@ public final class Track extends GeoPath{ /** * Construct a TrackWaypoint. - * - * @param artifact the artifact to which this waypoint belongs - * + * + * @param artifact the artifact to which this waypoint belongs + * * @param pointLabel the label for the waypoint - * - * @param point GeoTrackPoint - * - * @throws GeoLocationDataException + * + * @param point GeoTrackPoint + * + * @throws GeoLocationDataException */ - TrackWaypoint(BlackboardArtifact artifact, String pointLabel, GeoTrackPoint point) throws GeoLocationDataException { + TrackWaypoint(BlackboardArtifact artifact, String pointLabel, GeoTrackPoints.TrackPoint point) throws GeoLocationDataException { super(artifact, pointLabel, point.getTimeStamp(), point.getLatitude(), @@ -172,10 +172,10 @@ public final class Track extends GeoPath{ } /** - * Overloaded to return a property list that is generated from - * the GeoTrackPoint instead of an artifact. - * - * @return unmodifiable list of Waypoint.Property + * Overloaded to return a property list that is generated from the + * GeoTrackPoint instead of an artifact. + * + * @return unmodifiable list of Waypoint.Property */ @Override public List getOtherProperties() { @@ -184,16 +184,16 @@ public final class Track extends GeoPath{ /** * Create a propertyList specific to GeoTrackPoints. - * + * * @param point GeoTrackPoint to get values from. - * + * * @return A list of Waypoint.properies. */ @Messages({ "Track_distanceTraveled_displayName=Distance traveled", "Track_distanceFromHome_displayName=Distance from home point" }) - private List createPropertyList(GeoTrackPoint point) { + private List createPropertyList(GeoTrackPoints.TrackPoint point) { List list = new ArrayList<>(); Long timestamp = point.getTimeStamp(); diff --git a/Core/src/org/sleuthkit/autopsy/modules/drones/DATExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/drones/DATExtractor.java index 97cfbd40be..3966317d15 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/drones/DATExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/drones/DATExtractor.java @@ -41,11 +41,11 @@ import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList.GeoTrackPoint; import org.sleuthkit.datamodel.blackboardutils.GeoArtifactsHelper; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.Blackboard.BlackboardException; -import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList; +import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints; +import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints.TrackPoint; /** * Extract drone position data from DJI Phantom drones. @@ -111,7 +111,7 @@ final class DATExtractor extends DroneExtractor { } // Process the csv file - GeoTrackPointList trackPoints = processCSVFile(context, DATFile, csvFilePath); + GeoTrackPoints trackPoints = processCSVFile(context, DATFile, csvFilePath); if (trackPoints != null && !trackPoints.isEmpty()) { (new GeoArtifactsHelper(getSleuthkitCase(), getName(), "DatCon", DATFile)).addTrack(DATFile.getName(), trackPoints, null); @@ -188,8 +188,8 @@ final class DATExtractor extends DroneExtractor { * * @throws DroneIngestException */ - private GeoTrackPointList processCSVFile(IngestJobContext context, AbstractFile DATFile, String csvFilePath) throws DroneIngestException { - GeoTrackPointList trackPoints = new GeoTrackPointList(); + private GeoTrackPoints processCSVFile(IngestJobContext context, AbstractFile DATFile, String csvFilePath) throws DroneIngestException { + GeoTrackPoints trackPoints = new GeoTrackPoints(); try (BufferedReader reader = new BufferedReader(new FileReader(new File(csvFilePath)))) { // First read in the header line and process String line = reader.readLine(); @@ -201,7 +201,7 @@ final class DATExtractor extends DroneExtractor { } String[] values = line.split(","); //NON-NLS - GeoTrackPoint point = createTrackPoint(headerMap, values); + TrackPoint point = createTrackPoint(headerMap, values); if (point != null) { trackPoints.addPoint(point); } @@ -246,7 +246,7 @@ final class DATExtractor extends DroneExtractor { * * @throws DroneIngestException */ - private GeoTrackPoint createTrackPoint(Map columnLookup, String[] values) throws DroneIngestException { + private TrackPoint createTrackPoint(Map columnLookup, String[] values) throws DroneIngestException { Double latitude = getDoubleValue(columnLookup.get(HEADER_LAT), values); Double longitude = getDoubleValue(columnLookup.get(HEADER_LONG), values); @@ -256,7 +256,7 @@ final class DATExtractor extends DroneExtractor { return null; } - return new GeoTrackPoint(latitude, + return new TrackPoint(latitude, longitude, getDoubleValue(columnLookup.get(HEADER_ALTITUDE), values), null, diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java index 7cb6c72ca2..0f1f97ba97 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupSettingsPanel.java @@ -52,6 +52,7 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.CentralRepoHashS import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb.KnownFilesType; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; +import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.SetEvt; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; /** @@ -94,6 +95,17 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan } } }); + + HashDbManager.getInstance().addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String propName = evt.getPropertyName(); + if(propName.equals(SetEvt.DB_ADDED.toString()) || + propName.equals(SetEvt.DB_DELETED.toString())) { + hashSetTableModel.refreshModel(); + } + } + }); } @NbBundle.Messages({"HashLookupSettingsPanel.Title=Global Hash Lookup Settings"}) diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java index 5b3d661665..236263200a 100644 --- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java +++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java @@ -59,7 +59,7 @@ import org.sleuthkit.autopsy.report.ReportModule; @ActionReferences(value = { @ActionReference(path = "Menu/Tools", position = 301, separatorAfter = 399) , - @ActionReference(path = "Toolbars/Case", position = 107)}) + @ActionReference(path = "Toolbars/Case", position = 106)}) public final class ReportWizardAction extends CallableSystemAction implements Presenter.Toolbar, ActionListener { private static final Logger logger = Logger.getLogger(ReportWizardAction.class.getName()); diff --git a/CoreLibs/manifest.mf b/CoreLibs/manifest.mf index 1f7fcc545e..95267126df 100644 --- a/CoreLibs/manifest.mf +++ b/CoreLibs/manifest.mf @@ -1,8 +1,8 @@ Manifest-Version: 1.0 OpenIDE-Module: org.sleuthkit.autopsy.corelibs/3 -OpenIDE-Module-Implementation-Version: 6 +OpenIDE-Module-Implementation-Version: 7 OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/corelibs/Bundle.properties -OpenIDE-Module-Specification-Version: 1.3 +OpenIDE-Module-Specification-Version: 1.4 AutoUpdate-Show-In-Client: true AutoUpdate-Essential-Module: true diff --git a/Experimental/nbproject/project.xml b/Experimental/nbproject/project.xml index 90fbce3d76..166c73f0a5 100644 --- a/Experimental/nbproject/project.xml +++ b/Experimental/nbproject/project.xml @@ -135,7 +135,7 @@ 10 - 10.18 + 10.19 @@ -144,7 +144,7 @@ 3 - 1.3 + 1.4 diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/MultiUserTestTool.java b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/MultiUserTestTool.java index 8504bfd7c0..c622d452b5 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/MultiUserTestTool.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/MultiUserTestTool.java @@ -57,6 +57,7 @@ import org.sleuthkit.autopsy.ingest.IngestJobStartResult; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestModuleError; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; +import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.datamodel.AbstractFile; /** @@ -83,6 +84,7 @@ class MultiUserTestTool { "MultiUserTestTool.unableCreatFile=Unable to create a file in case output directory", "MultiUserTestTool.unableAddFileAsDataSource=Unable to add test file as data source to case", "MultiUserTestTool.unableToReadTestFileFromDatabase=Unable to read test file info from case database", + "MultiUserTestTool.unableToInitializeFilTypeDetector=Unable to initialize File Type Detector", "MultiUserTestTool.unableToUpdateKWSIndex=Unable to write to Keyword Search index", "MultiUserTestTool.unableToRunIngest=Unable to run ingest on test data source", "MultiUserTestTool.unexpectedError=Unexpected error while performing Multi User test", @@ -187,6 +189,16 @@ class MultiUserTestTool { } AbstractFile file = listOfFiles.get(0); + + // Set MIME type of the test file (required to test indexing) + FileTypeDetector fileTypeDetector = null; + try { + fileTypeDetector = new FileTypeDetector(); + } catch (FileTypeDetector.FileTypeDetectorInitException ex) { + return Bundle.MultiUserTestTool_unableToInitializeFilTypeDetector() + ". " + ex.getMessage(); + } + String mimeType = fileTypeDetector.getMIMEType(file); + file.setMIMEType(mimeType); // write to KWS index KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); diff --git a/ImageGallery/nbproject/project.xml b/ImageGallery/nbproject/project.xml index a7157df475..4ef4c1fde1 100644 --- a/ImageGallery/nbproject/project.xml +++ b/ImageGallery/nbproject/project.xml @@ -127,7 +127,7 @@ 10 - 10.18 + 10.19 @@ -136,7 +136,7 @@ 3 - 1.3 + 1.4 diff --git a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py index 6aeb84d5be..3d202a963b 100644 --- a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py +++ b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py @@ -37,12 +37,10 @@ from org.sleuthkit.datamodel import BlackboardArtifact from org.sleuthkit.datamodel import BlackboardAttribute from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel.blackboardutils import GeoArtifactsHelper -from org.sleuthkit.datamodel.blackboardutils.attributes import TskGeoWaypointsUtil -from org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil import GeoWaypointList -from org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList import GeoWaypoint -from org.sleuthkit.datamodel.blackboardutils.attributes import TskGeoTrackpointsUtil -from org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil import GeoTrackPointList -from org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList import GeoTrackPoint +from org.sleuthkit.datamodel.blackboardutils.attributes import GeoWaypoints +from org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoints import Waypoint +from org.sleuthkit.datamodel.blackboardutils.attributes import GeoTrackPoints +from org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints import TrackPoint from org.sleuthkit.autopsy.datamodel import ContentUtils from org.sleuthkit.autopsy.ingest import IngestModule from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException @@ -166,7 +164,7 @@ class GPXParserDataSourceIngestModule(DataSourceIngestModule): if self.writeDebugMsgs: self.log(Level.INFO, "Processing tracks from " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") for track in gpx.tracks: for segment in track.segments: - geoPointList = TskGeoTrackpointsUtil.GeoTrackPointList() + geoPointList = GeoTrackPoints() for point in segment.points: elevation = 0 @@ -180,7 +178,7 @@ class GPXParserDataSourceIngestModule(DataSourceIngestModule): except Exception as e: self.log(Level.WARNING, "Error getting track timestamp from " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + str(e)) - geoPointList.addPoint(GeoTrackPoint(point.latitude, point.longitude, elevation, None, 0, 0, 0, timeStamp)) + geoPointList.addPoint(TrackPoint(point.latitude, point.longitude, elevation, None, 0, 0, 0, timeStamp)) try: geoArtifactHelper.addTrack("Track", geoPointList, None) @@ -213,13 +211,13 @@ class GPXParserDataSourceIngestModule(DataSourceIngestModule): if self.writeDebugMsgs: self.log(Level.INFO, "Processing routes from " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") for route in gpx.routes: - geoWaypointList = TskGeoWaypointsUtil.GeoWaypointList() + geoWaypoints = GeoWaypoints() for point in route.points: - geoWaypointList.addPoint(GeoWaypoint(point.latitude, point.longitude, point.elevation, point.name)) + geoWaypoints.addPoint(Waypoint(point.latitude, point.longitude, point.elevation, point.name)) try: - geoArtifactHelper.addRoute(None, None, geoWaypointList, None) + geoArtifactHelper.addRoute(None, None, geoWaypoints, None) except Blackboard.BlackboardException as e: self.log("Error posting GPS route artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) except TskCoreException as e: diff --git a/InternalPythonModules/android/calllog.py b/InternalPythonModules/android/calllog.py index 5b3faf0697..13775e80f7 100644 --- a/InternalPythonModules/android/calllog.py +++ b/InternalPythonModules/android/calllog.py @@ -103,10 +103,7 @@ class CallLogAnalyzer(general.AndroidComponentAnalyzer): calleeId = None timeStamp = resultSet.getLong("date") / 1000 - number = resultSet.getString("number") - if not general.isValidPhoneNumer(number): - number = None duration = resultSet.getLong("duration") # duration of call is in seconds name = resultSet.getString("name") # name of person dialed or called. None if unregistered diff --git a/InternalPythonModules/android/general.py b/InternalPythonModules/android/general.py index daa789a57c..4048c05c0f 100644 --- a/InternalPythonModules/android/general.py +++ b/InternalPythonModules/android/general.py @@ -45,19 +45,15 @@ def appendAttachmentList(msgBody, attachmentsList): """ Checks if the given string might be a phone number. """ -def isValidPhoneNumer(data): - try: - return CommunicationsUtils.normalizePhoneNum(data) is not None - except TskCoreException as ex: - return False +def isValidPhoneNumber(data): + return CommunicationsUtils.isValidPhoneNumber(data) + """ Checks if the given string is a valid email address. """ def isValidEmailAddress(data): - try: - return CommunicationsUtils.normalizeEmailAddress(data) is not None - except TskCoreException as ex: - return False + return CommunicationsUtils.isValidEmailAddress(data) + diff --git a/InternalPythonModules/android/googlemaplocation.py b/InternalPythonModules/android/googlemaplocation.py index 8e393dcee2..2c33146b21 100644 --- a/InternalPythonModules/android/googlemaplocation.py +++ b/InternalPythonModules/android/googlemaplocation.py @@ -43,8 +43,8 @@ from org.sleuthkit.datamodel import Content from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel.Blackboard import BlackboardException from org.sleuthkit.datamodel.blackboardutils import GeoArtifactsHelper -from org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil import GeoWaypointList -from org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList import GeoWaypoint +from org.sleuthkit.datamodel.blackboardutils.attributes import GeoWaypoints +from org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoints import Waypoint import traceback import general @@ -117,9 +117,9 @@ class GoogleMapLocationAnalyzer(general.AndroidComponentAnalyzer): source_lat = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lat")) source_lng = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lng")) - waypointlist = GeoWaypointList() - waypointlist.addPoint(GeoWaypoint(source_lat, source_lng, None, None)) - waypointlist.addPoint(GeoWaypoint(dest_lat, dest_lng, None, dest_address)) + waypointlist = GeoWaypoints() + waypointlist.addPoint(Waypoint(source_lat, source_lng, None, None)) + waypointlist.addPoint(Waypoint(dest_lat, dest_lng, None, dest_address)) artifactHelper.addRoute(dest_title, time, waypointlist, None) diff --git a/InternalPythonModules/android/oruxmaps.py b/InternalPythonModules/android/oruxmaps.py index 7a939e1972..283eab38b2 100644 --- a/InternalPythonModules/android/oruxmaps.py +++ b/InternalPythonModules/android/oruxmaps.py @@ -45,7 +45,8 @@ from org.sleuthkit.datamodel import Content from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel.Blackboard import BlackboardException from org.sleuthkit.datamodel.blackboardutils import GeoArtifactsHelper -from org.sleuthkit.datamodel.blackboardutils.attributes import TskGeoTrackpointsUtil +from org.sleuthkit.datamodel.blackboardutils.attributes import GeoTrackPoints +from org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints import TrackPoint import traceback import general @@ -139,14 +140,14 @@ class OruxMapsAnalyzer(general.AndroidComponentAnalyzer): trackpointsQueryString = "SELECT trkptlat, trkptlon, trkptalt, trkpttime FROM trackpoints WHERE trkptseg = " + segmentId trackpointsResultSet = oruxMapsTrackpointsDb.runQuery(trackpointsQueryString) if trackpointsResultSet is not None: - geoPointList = TskGeoTrackpointsUtil.GeoTrackPointList() + geoPointList = GeoTrackPoints() while trackpointsResultSet.next(): latitude = trackpointsResultSet.getDouble("trkptlat") longitude = trackpointsResultSet.getDouble("trkptlon") altitude = trackpointsResultSet.getDouble("trkptalt") time = trackpointsResultSet.getLong("trkpttime") / 1000 # milliseconds since unix epoch - geoPointList.addPoint(TskGeoTrackpointsUtil.GeoTrackPointList.GeoTrackPoint(latitude, longitude, altitude, segmentName, 0, 0, 0, time)) + geoPointList.addPoint(TrackPoint(latitude, longitude, altitude, segmentName, 0, 0, 0, time)) try: geoartifact = geoArtifactHelper.addTrack(segmentName, geoPointList, None) diff --git a/InternalPythonModules/android/textnow.py b/InternalPythonModules/android/textnow.py index e05763cae9..005e1191dd 100644 --- a/InternalPythonModules/android/textnow.py +++ b/InternalPythonModules/android/textnow.py @@ -286,7 +286,7 @@ class TextNowContactsParser(TskContactsParser): def get_phone(self): number = self.result_set.getString("number") - return (number if general.isValidPhoneNumer(number) else None) + return (number if general.isValidPhoneNumber(number) else None) def get_email(self): # occasionally the 'number' column may have an email address instead diff --git a/InternalPythonModules/android/whatsapp.py b/InternalPythonModules/android/whatsapp.py index 5346545450..c67502d22b 100644 --- a/InternalPythonModules/android/whatsapp.py +++ b/InternalPythonModules/android/whatsapp.py @@ -435,7 +435,7 @@ class WhatsAppContactsParser(TskContactsParser): def get_phone(self): number = self.result_set.getString("number") - return (number if general.isValidPhoneNumer(number) else None) + return (number if general.isValidPhoneNumber(number) else None) def get_email(self): # occasionally the 'number' column may have an email address instead diff --git a/KeywordSearch/nbproject/project.xml b/KeywordSearch/nbproject/project.xml index 2de991d105..10e1e76bfe 100644 --- a/KeywordSearch/nbproject/project.xml +++ b/KeywordSearch/nbproject/project.xml @@ -119,7 +119,7 @@ 10 - 10.18 + 10.19 @@ -128,7 +128,7 @@ 3 - 1.3 + 1.4 diff --git a/NEWS.txt b/NEWS.txt index ee294d7fea..068408b413 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -1,3 +1,32 @@ +---------------- VERSION 4.15.0 -------------- +New UI Features: +- Added Document view to File Discovery. +- Expanded Context Content Viewer to show if an app accessed a file. +- Added translation feature to Message Content Viewer. +- Added waypoint type filter to the Geolocation viewer. +- Added zoom feature to Indexed Text Content Viewer. + +New Ingest Modules Features: +- New GPX ingest module. +- New Drone ingest module for DJI drones based on DatCon. +- Create artifacts for files opened by Adobe Reader, Windows Media Player, Office Docs (Most Recently Used (MRU) and TrustRecords), 7Zip MRU, WinRAR MRU, Applets, Microsoft Management Console (MMC) via RegRipper. + +New Central Repository Features: +- Central Repository stores account IDs that were previously seen. +- Central Repository is enabled by default to store past hashes. Feature to flag previously seen files is disabled by default. + +Other New Features: +- Multi-user cases can be created via command line + +Bug fixes: +- Prevent entire application from crashing when gstreamer crashes on videos. +- Improve Geolocation viewer with large data sets. +- Fix error with non-sector aligned reads on local disks. +- Times from Recycle Bin files are now in timeline. +- Validate timeline events and ignore events too far in the future. +- Moved some database queries off of UI thread. +- Remove hard coded sizes from UI that cause issues with other languages. + ---------------- VERSION 4.14.0 -------------- Specialized UIs: - New File Discovery UI that allows you to search and filter for certain types of files. diff --git a/RecentActivity/nbproject/project.xml b/RecentActivity/nbproject/project.xml index 89394f1b1f..f6465f00a5 100644 --- a/RecentActivity/nbproject/project.xml +++ b/RecentActivity/nbproject/project.xml @@ -60,7 +60,7 @@ 10 - 10.18 + 10.19 @@ -69,7 +69,7 @@ 3 - 1.3 + 1.4 diff --git a/Testing/nbproject/project.xml b/Testing/nbproject/project.xml index def60a06ac..2ebb94a835 100644 --- a/Testing/nbproject/project.xml +++ b/Testing/nbproject/project.xml @@ -47,7 +47,7 @@ 10 - 10.18 + 10.19 diff --git a/docs/doxygen-user/Doxyfile b/docs/doxygen-user/Doxyfile index 6eb71ae8f8..0f985042a3 100644 --- a/docs/doxygen-user/Doxyfile +++ b/docs/doxygen-user/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy User Documentation" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.14.0 +PROJECT_NUMBER = 4.15.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -1025,7 +1025,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = 4.14.0 +HTML_OUTPUT = 4.15.0 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/docs/doxygen-user/images/serviceinstall.PNG b/docs/doxygen-user/images/serviceinstall.PNG index 3a5ea81b47..b20f07d9c8 100644 Binary files a/docs/doxygen-user/images/serviceinstall.PNG and b/docs/doxygen-user/images/serviceinstall.PNG differ diff --git a/docs/doxygen-user/images/wherejava.PNG b/docs/doxygen-user/images/wherejava.PNG index 93bc8af523..ee93d1717e 100644 Binary files a/docs/doxygen-user/images/wherejava.PNG and b/docs/doxygen-user/images/wherejava.PNG differ diff --git a/docs/doxygen-user/multi-user/installActiveMQ.dox b/docs/doxygen-user/multi-user/installActiveMQ.dox index 4d7ab37fe5..07a6f1dcfd 100644 --- a/docs/doxygen-user/multi-user/installActiveMQ.dox +++ b/docs/doxygen-user/multi-user/installActiveMQ.dox @@ -8,14 +8,14 @@ ActiveMQ is a messaging service that allows the Autopsy clients to communicate w \section install_activemq_prereq Prerequisites You will need: -- 64-bit version of the Java Runtime Environment (JRE) from http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html. +- 64-bit version of the Java 8 Runtime Environment (JRE) from https://github.com/ojdkbuild/ojdkbuild ( Link to installer) - Download ActiveMQ from: http://activemq.apache.org/download.html . Autopsy has been tested with ActiveMQ version 5.14.0. \section install_activemq_install Installation \subsection install_activemq_install_java JRE Installation -Install the Java JRE if needed. You can test this by running _where java_ from the command line. If you see output like the yellow results below, you have a JRE. +Install the Java JRE if needed. You can test this by running _where java_ from the command line. If you see output similar to the results below, you have a JRE.

\image html wherejava.PNG

diff --git a/docs/doxygen-user/multi-user/installSolr.dox b/docs/doxygen-user/multi-user/installSolr.dox index 3d55f29efc..1c3970e344 100644 --- a/docs/doxygen-user/multi-user/installSolr.dox +++ b/docs/doxygen-user/multi-user/installSolr.dox @@ -13,7 +13,7 @@ Solr's embedded ZooKeeper is also used as a coordination service for Autopsy. We use Bitnami Solr, which packages Solr as a Windows service. You will need: -- A 64-bit version of the Java Runtime Environment (JRE) from http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html. +- A 64-bit version of the Java 8 Runtime Environment (JRE) from https://github.com/ojdkbuild/ojdkbuild. ( Link to installer) - The Apache Solr 4.10.3-0 installation package. This is no longer available from its original source, but you can find it on our site: https://sourceforge.net/projects/autopsy/files/CollaborativeServices/Solr. -- NOTE: We tested Solr 6 at one point, but ran into stability problems when loading and unloading cores. For now, you need to use Solr 4. - An installed version of Autopsy so that you can copy files from it. You can install Autopsy on one of the planned client systems. You do not need to install it on the Solr server. @@ -24,7 +24,11 @@ You will need: \section install_solr_install Installation \subsection install_solr_install_java JRE Installation -1. JREs are normally installed under "C:\Program Files\Java\jre(version)", so check there to see if you have one installed already. If not, get the installer as listed in the above Prerequisites section and install it with the default settings. +1. Install the Java JRE if needed. You can test this by running _where java_ from the command line. If you see output similar to the results below, you have a JRE. +

+\image html wherejava.PNG +

+If you need the JRE, install it with the default settings. \subsection install_solr_install_solr Solr Installation @@ -52,7 +56,7 @@ The following steps will configure Solr to run using an account that will have a + ++JvmOptions=-Dbootstrap_confdir="C:\Bitnami\solr-4.10.3-0\apache-solr\solr\configsets\AutopsyConfig\conf" + ++JvmOptions=-DzkRun
- - Replace the path to JavaHome with the path to your 64-bit version of the JRE. If you do not know the path, the correct JavaHome path can be obtained by running the command "where java" from the Windows command line. An example is shown below. The text in yellow is what we are interested in. Do not include the "bin" folder in the path you place into the JavaHome variable. A correct example of the final result will look something like this: –-JavaHome="C:\Program Files\Java\jre1.8.0_111" + - Replace the path to JavaHome with the path to your 64-bit version of the JRE. If you do not know the path, the correct JavaHome path can be obtained by running the command "where java" from the Windows command line. An example is shown below. The text in yellow is what we are interested in. Do not include the "bin" folder in the path you place into the JavaHome variable. A correct example of the final result will look something like this: –-JavaHome="C:\Program Files\ojdkbuild\java-1.8.0-openjdk-1.8.0.222-1"

A portion of an updated _serviceinstall.bat_ is shown below, with the changes marked in yellow.

diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 6490edacd6..993ee8ded2 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.14.0 +PROJECT_NUMBER = 4.15.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears a the top of each page and should give viewer a @@ -1066,7 +1066,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = api-docs/4.14.0/ +HTML_OUTPUT = api-docs/4.15.0/ # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/docs/doxygen/modReport.dox b/docs/doxygen/modReport.dox index 563a151c43..90e40fb552 100644 --- a/docs/doxygen/modReport.dox +++ b/docs/doxygen/modReport.dox @@ -5,7 +5,7 @@ Report modules allow Autopsy users to create different report types. Autopsy co All custom report modules will be general report modules. General report modules have a single method to generate the report. This method gives the module freedom to find and process any data it so chooses. General modules also have the ability to provide a configuration panel, allowing the user to choose from various displayed settings. The report module may then use the user's selection to generate a more specific report. -General modules are also given the responsibility of updating their report's progress bar and processing label in the UI. A progress panel is given to every general report module. It contains basic API to start, stop, and add to the progress bar, as well as update the processing label. The module is also expected to check the progress bar's status occasionally to see if the user has manually canceled the report. +General modules are also given the responsibility of updating their report's progress bar and processing label in the UI. A progress panel is given to every general report module. It defines a basic API to start, stop, and increment the progress bar and to update the processing label. The module is also expected to check the progress bar's status occasionally to see if the user has manually canceled the report. \section report_create_module Creating a Report Module To create a report module, start off by creating a new Java or Python (Jython) class and implementing (Java) or inheriting (Jython) from org.sleuthkit.autopsy.report.GeneralReportModule. You'll need to override multiple methods including the following: @@ -17,11 +17,11 @@ To create a report module, start off by creating a new Java or Python (Jython) c If your report module requires configuration, you'll need to override: - org.sleuthkit.autopsy.report.GeneralReportModule.getConfigurationPanel() -For general report modules, Autopsy will simply call the generateReport(String reportPath, ReportProgressPanel progressPanel) method and leave it up to the module to aquire and report data in its desired format. The only requirements are that the module saves to the given report path and updates the org.sleuthkit.autopsy.report.ReportProgressPanel as the report progresses. +For general report modules, Autopsy will simply call the generateReport(String reportPath, ReportProgressPanel progressPanel) method and leave it up to the module to acquire and report data in its desired format. The only requirements are that the module saves to the given report path and updates the org.sleuthkit.autopsy.report.ReportProgressPanel as the report progresses. When updating the progress panel, it is recommended to update it as infrequently as possible, while still keeping the user informed. If your report processes 100,000 files and you chose to update the UI each time a file is reviewed, the UI would freeze when trying to process all your requests. This would cause problems to not only your reporting module, but to other modules running in parallel. A safer approach would be to update the UI every 1,000 files, or when a certain "category" of the files being processed has changed. For example, the HTML report module increments the progress bar and changes the processing label every time a new Blackboard Artifact Type is being processed. -Autopsy will also display the panel returned by getConfigurationPanel() in the generate report wizard. This panel can be used to allow the user custom controls over the report. To make this panel, use NetBeans to make a new JPanel class and use its layout interface to put the UI widgets in the places that you want them. Then, your getConfigurationPanel() method should create an instance of that class and return it. +Autopsy will also display the panel returned by getConfigurationPanel() in the generate report wizard. This panel can be used to allow the user to customize the report. To make this panel, use NetBeans to make a new JPanel class and use its layout interface to put the UI widgets in the places that you want them. Then, your getConfigurationPanel() method should create an instance of that class and return it. Typically a general report module should interact with both the Blackboard API in the org.sleuthkit.datamodel.SleuthkitCase class, in addition to an API (possibly external/thirdparty) to convert Blackboard Artifacts to the desired reporting format. @@ -32,7 +32,7 @@ You should call org.sleuthkit.autopsy.casemodule.Case.addReport() with the path \subsection report_create_module_indexing Indexing Reports -After you have called org.sleuthkit.autopsy.casemodule.Case.addReport() and created a report, you can pass it to org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService.index() so that it is indexed and can then be found by a user. This is most commonly used when an Ingest Module runs a 3rd party tool and the output of that tool is added back into Autopsy as a report. Here is some example code: +After you have called org.sleuthkit.autopsy.casemodule.Case.addReport() and created a report, you can pass it to org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService.index() so that the report text is indexed for keyword search. This is most commonly used when an Ingest Module runs a 3rd party tool and the output of that tool is added back into Autopsy as a report. Here is some example code: \code{.java} KeywordSearchService searchService = Lookup.getDefault().lookup(KeywordSearchService.class); diff --git a/nbproject/project.properties b/nbproject/project.properties index 1f61a9c967..7cb61fc13a 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -4,10 +4,10 @@ app.title=Autopsy ### lowercase version of above app.name=${branding.token} ### if left unset, version will default to today's date -app.version=4.14.0 +app.version=4.15.0 ### build.type must be one of: DEVELOPMENT, RELEASE -#build.type=RELEASE -build.type=DEVELOPMENT +build.type=RELEASE +#build.type=DEVELOPMENT project.org.netbeans.progress=org-netbeans-api-progress project.org.sleuthkit.autopsy.experimental=Experimental diff --git a/thunderbirdparser/nbproject/project.xml b/thunderbirdparser/nbproject/project.xml index 4eebb3d2f4..29782066ac 100644 --- a/thunderbirdparser/nbproject/project.xml +++ b/thunderbirdparser/nbproject/project.xml @@ -36,7 +36,7 @@ 10 - 10.18 + 10.19