mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge pull request #7795 from eugene7646/indexed_text_tab_8505
Indexed text tab (8505)
This commit is contained in:
commit
f820a39c65
@ -47,6 +47,9 @@ class AddLocalFilesTask implements Runnable {
|
||||
private final List<String> localFilePaths;
|
||||
private final DataSourceProcessorProgressMonitor progress;
|
||||
private final DataSourceProcessorCallback callback;
|
||||
private final boolean createTimestamp;
|
||||
private final boolean accessTimestamp;
|
||||
private final boolean modifiedTimestamp;
|
||||
|
||||
/**
|
||||
* Constructs a runnable that adds a set of local/logical files and/or
|
||||
@ -67,15 +70,22 @@ class AddLocalFilesTask implements Runnable {
|
||||
* @param localFilePaths A list of localFilePaths of local/logical
|
||||
* files and/or directories.
|
||||
* @param host The host for this data source (may be null).
|
||||
* @param createTime Boolean value to add the time the file was locally created
|
||||
* @param accessTime Boolean value to add the time the file was last accessed
|
||||
* @param modifiedTime Boolean value to add the time the file was locally modified
|
||||
* @param progressMonitor Progress monitor to report progress
|
||||
* during processing.
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
AddLocalFilesTask(String deviceId, String rootVirtualDirectoryName, List<String> localFilePaths, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
AddLocalFilesTask(String deviceId, String rootVirtualDirectoryName, List<String> localFilePaths, Host host, boolean createTimestamp,
|
||||
boolean accessTimestamp, boolean modifiedTimestamp, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
this.deviceId = deviceId;
|
||||
this.rootVirtualDirectoryName = rootVirtualDirectoryName;
|
||||
this.localFilePaths = localFilePaths;
|
||||
this.host = host;
|
||||
this.createTimestamp = createTimestamp;
|
||||
this.accessTimestamp = accessTimestamp;
|
||||
this.modifiedTimestamp = modifiedTimestamp;
|
||||
this.callback = callback;
|
||||
this.progress = progressMonitor;
|
||||
}
|
||||
@ -92,7 +102,8 @@ class AddLocalFilesTask implements Runnable {
|
||||
try {
|
||||
progress.setIndeterminate(true);
|
||||
FileManager fileManager = Case.getCurrentCaseThrows().getServices().getFileManager();
|
||||
LocalFilesDataSource newDataSource = fileManager.addLocalFilesDataSource(deviceId, rootVirtualDirectoryName, "", host, localFilePaths, new ProgressUpdater());
|
||||
LocalFilesDataSource newDataSource = fileManager.addLocalFilesDataSource(deviceId, rootVirtualDirectoryName, "", host, localFilePaths, createTimestamp,
|
||||
accessTimestamp, modifiedTimestamp, new ProgressUpdater());
|
||||
newDataSources.add(newDataSource);
|
||||
} catch (TskDataException | TskCoreException | NoCurrentCaseException ex) {
|
||||
errors.add(ex.getMessage());
|
||||
|
@ -263,3 +263,8 @@ AddImageWizardSelectHostVisual.specifyNewHostTextField.text=
|
||||
AddImageWizardSelectHostVisual.specifyNewHostRadio.text=Specify new host name
|
||||
AddImageWizardSelectHostVisual.generateNewRadio.text=Generate new host name based on data source name
|
||||
AddImageWizardSelectHostVisual.validationMessage.text=\
|
||||
LocalFilesPanel.createTimeCheckBox.text=Creation Time - Often changed when a file is copied
|
||||
LocalFilesPanel.modifiedTimeCheckBox.text=\ Modified Time - Often not changed when a file is copied
|
||||
LocalFilesPanel.jLabel2.text=NOTE: Time stamps may have changed when the files were copied to the current location.
|
||||
LocalFilesPanel.timestampToIncludeLabel.text=Timestamps To Include:
|
||||
LocalFilesPanel.accessTimeCheckBox.text=Access Time - Can be changed when the file is opened
|
||||
|
@ -247,10 +247,15 @@ AddImageWizardIngestConfigPanel.dsProcDone.errs.text=*Errors encountered in addi
|
||||
AddImageWizardIngestConfigVisual.getName.text=Configure Ingest
|
||||
AddImageWizardIterator.stepXofN=Step {0} of {1}
|
||||
AddLocalFilesTask.localFileAdd.progress.text=Adding: {0}/{1}
|
||||
Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open!
|
||||
Case.getCurCase.exception.noneOpen=Cannot get the current case; there is no case open\!
|
||||
Case.open.msgDlg.updated.msg=Updated case database schema.\nA backup copy of the database with the following path has been made:\n {0}
|
||||
Case.open.msgDlg.updated.title=Case Database Schema Update
|
||||
Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \nthis case are missing. Would you like to search for them now?\nPreviously, the image was located at:\n{0}\nPlease note that you will still be able to browse directories and generate reports\nif you choose No, but you will not be able to view file content or run the ingest process.
|
||||
Case.checkImgExist.confDlg.doesntExist.msg=One of the images associated with \n\
|
||||
this case are missing. Would you like to search for them now?\n\
|
||||
Previously, the image was located at:\n\
|
||||
{0}\n\
|
||||
Please note that you will still be able to browse directories and generate reports\n\
|
||||
if you choose No, but you will not be able to view file content or run the ingest process.
|
||||
Case.checkImgExist.confDlg.doesntExist.title=Missing Image
|
||||
Case.addImg.exception.msg=Error adding image to the case
|
||||
Case.updateCaseName.exception.msg=Error while trying to update the case name.
|
||||
@ -269,9 +274,12 @@ Case.GetCaseTypeGivenPath.Failure=Unable to get case type
|
||||
Case.metaDataFileCorrupt.exception.msg=The case metadata file (.aut) is corrupted.
|
||||
Case.deleteReports.deleteFromDiskException.log.msg=Unable to delete the report from the disk.
|
||||
Case.deleteReports.deleteFromDiskException.msg=Unable to delete the report {0} from the disk.\nYou may manually delete it from {1}
|
||||
CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \nCase Name: {0}\nCase Directory: {1}
|
||||
CaseDeleteAction.closeConfMsg.text=Are you sure want to close and delete this case? \n\
|
||||
Case Name: {0}\n\
|
||||
Case Directory: {1}
|
||||
CaseDeleteAction.closeConfMsg.title=Warning: Closing the Current Case
|
||||
CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\nClose the folder and file and try again or you can delete the case manually.
|
||||
CaseDeleteAction.msgDlg.fileInUse.msg=The delete action cannot be fully completed because the folder or file in it is open by another program.\n\n\
|
||||
Close the folder and file and try again or you can delete the case manually.
|
||||
CaseDeleteAction.msgDlg.fileInUse.title=Error: Folder In Use
|
||||
CaseDeleteAction.msgDlg.caseDelete.msg=Case {0} has been deleted.
|
||||
CaseOpenAction.autFilter.title={0} Case File ( {1})
|
||||
@ -303,7 +311,8 @@ NewCaseWizardAction.databaseProblem1.text=Cannot open database. Cancelling case
|
||||
NewCaseWizardAction.databaseProblem2.text=Error
|
||||
NewCaseWizardPanel1.validate.errMsg.invalidSymbols=The Case Name cannot contain any of the following symbols: \\ / : * ? " < > |
|
||||
NewCaseWizardPanel1.validate.errMsg.dirExists=Case directory ''{0}'' already exists.
|
||||
NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\nDo you want to create that directory?
|
||||
NewCaseWizardPanel1.validate.confMsg.createDir.msg=The base directory "{0}" does not exist. \n\n\
|
||||
Do you want to create that directory?
|
||||
NewCaseWizardPanel1.validate.confMsg.createDir.title=Create directory
|
||||
NewCaseWizardPanel1.validate.errMsg.cantCreateParDir.msg=Error: Could not create case parent directory {0}
|
||||
NewCaseWizardPanel1.validate.errMsg.prevCreateBaseDir.msg=Prevented from creating base directory {0}
|
||||
@ -360,8 +369,8 @@ UnpackageWorker.doInBackground.previouslySeenCase=Case has been previously opene
|
||||
UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases
|
||||
UpdateRecentCases.menuItem.empty=-Empty-
|
||||
AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel
|
||||
NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on "C:" drive
|
||||
NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on "C:" drive. Case folder is created on the target system
|
||||
NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive
|
||||
NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system
|
||||
NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive.
|
||||
NewCaseVisualPanel1.uncPath.error=Error: UNC paths are not allowed for Single-User cases
|
||||
CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source
|
||||
@ -369,7 +378,7 @@ CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1}
|
||||
MissingImageDialog.lbWarning.text=
|
||||
MissingImageDialog.lbWarning.toolTipText=
|
||||
NewCaseVisualPanel1.caseParentDirWarningLabel.text=
|
||||
NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-User\t\t
|
||||
NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-User
|
||||
NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-User
|
||||
NewCaseVisualPanel1.caseTypeLabel.text=Case Type:
|
||||
SingleUserCaseConverter.BadDatabaseFileName=Database file does not exist!
|
||||
@ -481,3 +490,8 @@ AddImageWizardSelectHostVisual.specifyNewHostTextField.text=
|
||||
AddImageWizardSelectHostVisual.specifyNewHostRadio.text=Specify new host name
|
||||
AddImageWizardSelectHostVisual.generateNewRadio.text=Generate new host name based on data source name
|
||||
AddImageWizardSelectHostVisual.validationMessage.text=\
|
||||
LocalFilesPanel.createTimeCheckBox.text=Creation Time - Often changed when a file is copied
|
||||
LocalFilesPanel.modifiedTimeCheckBox.text=\ Modified Time - Often not changed when a file is copied
|
||||
LocalFilesPanel.jLabel2.text=NOTE: Time stamps may have changed when the files were copied to the current location.
|
||||
LocalFilesPanel.timestampToIncludeLabel.text=Timestamps To Include:
|
||||
LocalFilesPanel.accessTimeCheckBox.text=Access Time - Can be changed when the file is opened
|
||||
|
@ -176,6 +176,9 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
public void run(Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
|
||||
localFilePaths = configPanel.getContentPaths();
|
||||
boolean createTimestamp = configPanel.getCreateTimestamp();
|
||||
boolean modifiedTimestamp = configPanel.getModifiedTimestamp();
|
||||
boolean accessTimestamp = configPanel.getAccessTimestamp();
|
||||
if (configPanel.subTypeIsLogicalEvidencePanel()) {
|
||||
try {
|
||||
//if the L01 option was chosen
|
||||
@ -191,7 +194,8 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
return;
|
||||
}
|
||||
}
|
||||
run(UUID.randomUUID().toString(), configPanel.getFileSetName(), localFilePaths, host, progressMonitor, callback);
|
||||
run(UUID.randomUUID().toString(), configPanel.getFileSetName(), localFilePaths, host, createTimestamp,
|
||||
accessTimestamp, modifiedTimestamp, progressMonitor, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -330,7 +334,40 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
public void run(String deviceId, String rootVirtualDirectoryName, List<String> localFilePaths, Host host, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
new Thread(new AddLocalFilesTask(deviceId, rootVirtualDirectoryName, localFilePaths, host, progressMonitor, callback)).start();
|
||||
new Thread(new AddLocalFilesTask(deviceId, rootVirtualDirectoryName, localFilePaths, host, false, false, false,progressMonitor, callback)).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the given settings instead of those provided by the
|
||||
* selection and configuration panel. Returns as soon as the background task
|
||||
* is started and uses the callback object to signal task completion and
|
||||
* return results.
|
||||
*
|
||||
* @param deviceId An ASCII-printable identifier for the
|
||||
* device associated with the data source
|
||||
* that is intended to be unique across
|
||||
* multiple cases (e.g., a UUID).
|
||||
* @param rootVirtualDirectoryName The name to give to the virtual directory
|
||||
* that will serve as the root for the
|
||||
* local/logical files and/or directories
|
||||
* that compose the data source. Pass the
|
||||
* empty string to get a default name of the
|
||||
* form: LogicalFileSet[N]
|
||||
* @param localFilePaths A list of local/logical file and/or
|
||||
* directory localFilePaths.
|
||||
* @param createTime Boolean value to add the time the file was locally created
|
||||
* @param accessTime Boolean value to add the time the file was last accessed
|
||||
* @param modifiedTime Boolean value to add the time the file was locally modified
|
||||
* @param host The host for this data source.
|
||||
* @param progressMonitor Progress monitor for reporting progress
|
||||
* during processing.
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
public void run(String deviceId, String rootVirtualDirectoryName, List<String> localFilePaths, Host host, boolean createTimestamp, boolean accessTimestamp,
|
||||
boolean modifiedTimestamp, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
new Thread(new AddLocalFilesTask(deviceId, rootVirtualDirectoryName, localFilePaths, host, createTimestamp, accessTimestamp, modifiedTimestamp,
|
||||
progressMonitor, callback)).start();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -356,7 +393,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat
|
||||
* during processing.
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
public void run(String deviceId, String rootVirtualDirectoryName, List<String> localFilePaths, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
public void run(String deviceId, String rootVirtualDirectoryName, List<String> localFilePaths, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
run(deviceId, rootVirtualDirectoryName, localFilePaths, null, progressMonitor, callback);
|
||||
}
|
||||
|
||||
|
@ -32,12 +32,24 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jPanel1" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="jPanel1" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jPanel1" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="jPanel1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
@ -60,18 +72,34 @@
|
||||
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="displayNameLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="changeNameButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="errorLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="timestampToIncludeLabel" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="displayNameLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="changeNameButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="modifiedTimeCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="accessTimeCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="createTimeCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jLabel2" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
@ -91,9 +119,17 @@
|
||||
<Component id="changeNameButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="displayNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="13" max="-2" attributes="0"/>
|
||||
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="timestampToIncludeLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="modifiedTimeCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="createTimeCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="accessTimeCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jLabel2" min="-2" pref="10" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="17" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -170,16 +206,6 @@
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="errorLabel">
|
||||
<Properties>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LocalFilesPanel.errorLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="changeNameButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
@ -197,7 +223,61 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="timestampToIncludeLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LocalFilesPanel.timestampToIncludeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="createTimeCheckBox">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LocalFilesPanel.createTimeCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="createTimeCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="accessTimeCheckBox">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LocalFilesPanel.accessTimeCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="accessTimeCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="modifiedTimeCheckBox">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LocalFilesPanel.modifiedTimeCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="modifiedTimeCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="jLabel2">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LocalFilesPanel.jLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="errorLabel">
|
||||
<Properties>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LocalFilesPanel.errorLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -75,9 +75,14 @@ final class LocalFilesPanel extends javax.swing.JPanel {
|
||||
clearButton = new javax.swing.JButton();
|
||||
selectedPathsScrollPane = new javax.swing.JScrollPane();
|
||||
selectedPaths = new javax.swing.JTextArea();
|
||||
errorLabel = new javax.swing.JLabel();
|
||||
changeNameButton = new javax.swing.JButton();
|
||||
displayNameLabel = new javax.swing.JLabel();
|
||||
timestampToIncludeLabel = new javax.swing.JLabel();
|
||||
createTimeCheckBox = new javax.swing.JCheckBox();
|
||||
accessTimeCheckBox = new javax.swing.JCheckBox();
|
||||
modifiedTimeCheckBox = new javax.swing.JCheckBox();
|
||||
jLabel2 = new javax.swing.JLabel();
|
||||
errorLabel = new javax.swing.JLabel();
|
||||
|
||||
localFileChooser.setApproveButtonText(org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.localFileChooser.approveButtonText")); // NOI18N
|
||||
localFileChooser.setApproveButtonToolTipText(org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.localFileChooser.approveButtonToolTipText")); // NOI18N
|
||||
@ -115,9 +120,6 @@ final class LocalFilesPanel extends javax.swing.JPanel {
|
||||
selectedPaths.setToolTipText(org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.selectedPaths.toolTipText")); // NOI18N
|
||||
selectedPathsScrollPane.setViewportView(selectedPaths);
|
||||
|
||||
errorLabel.setForeground(new java.awt.Color(255, 0, 0));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.errorLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(changeNameButton, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.changeNameButton.text")); // NOI18N
|
||||
changeNameButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
@ -127,6 +129,31 @@ final class LocalFilesPanel extends javax.swing.JPanel {
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(displayNameLabel, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.displayNameLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(timestampToIncludeLabel, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.timestampToIncludeLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(createTimeCheckBox, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.createTimeCheckBox.text")); // NOI18N
|
||||
createTimeCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
createTimeCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(accessTimeCheckBox, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.accessTimeCheckBox.text")); // NOI18N
|
||||
accessTimeCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
accessTimeCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(modifiedTimeCheckBox, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.modifiedTimeCheckBox.text")); // NOI18N
|
||||
modifiedTimeCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
modifiedTimeCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.jLabel2.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
jPanel1Layout.setHorizontalGroup(
|
||||
@ -142,13 +169,25 @@ final class LocalFilesPanel extends javax.swing.JPanel {
|
||||
.addComponent(clearButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addGap(2, 2, 2))
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(displayNameLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(changeNameButton))
|
||||
.addComponent(errorLabel))
|
||||
.addComponent(timestampToIncludeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addContainerGap())
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(displayNameLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(changeNameButton)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGap(23, 23, 23)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(modifiedTimeCheckBox)
|
||||
.addComponent(accessTimeCheckBox)
|
||||
.addComponent(createTimeCheckBox)))
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(jLabel2)))
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
jPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {clearButton, selectButton});
|
||||
@ -167,20 +206,40 @@ final class LocalFilesPanel extends javax.swing.JPanel {
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(changeNameButton)
|
||||
.addComponent(displayNameLabel))
|
||||
.addGap(13, 13, 13)
|
||||
.addComponent(errorLabel)
|
||||
.addContainerGap())
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(timestampToIncludeLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(modifiedTimeCheckBox)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(createTimeCheckBox)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(accessTimeCheckBox)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(17, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
errorLabel.setForeground(new java.awt.Color(255, 0, 0));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.errorLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addContainerGap())
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(errorLabel)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(errorLabel))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@ -226,6 +285,18 @@ final class LocalFilesPanel extends javax.swing.JPanel {
|
||||
}
|
||||
}//GEN-LAST:event_changeNameButtonActionPerformed
|
||||
|
||||
private void createTimeCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createTimeCheckBoxActionPerformed
|
||||
|
||||
}//GEN-LAST:event_createTimeCheckBoxActionPerformed
|
||||
|
||||
private void accessTimeCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_accessTimeCheckBoxActionPerformed
|
||||
|
||||
}//GEN-LAST:event_accessTimeCheckBoxActionPerformed
|
||||
|
||||
private void modifiedTimeCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_modifiedTimeCheckBoxActionPerformed
|
||||
|
||||
}//GEN-LAST:event_modifiedTimeCheckBoxActionPerformed
|
||||
|
||||
/**
|
||||
* Clear the fields and undo any selection of files.
|
||||
*/
|
||||
@ -260,6 +331,33 @@ final class LocalFilesPanel extends javax.swing.JPanel {
|
||||
}
|
||||
return pathsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the createTimestampcheckbox has been checked or not
|
||||
* @return boolean if box was checked
|
||||
*/
|
||||
|
||||
Boolean getCreateTimestamps() {
|
||||
return createTimeCheckBox.isSelected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the ModifiedTimestampcheckbox has been checked or not
|
||||
* @return boolean if box was checked
|
||||
*/
|
||||
|
||||
Boolean getModifiedTimestamps() {
|
||||
return modifiedTimeCheckBox.isSelected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the accessTimestampcheckbox has been checked or not
|
||||
* @return boolean if box was checked
|
||||
*/
|
||||
|
||||
Boolean getAccessTimestamps() {
|
||||
return accessTimeCheckBox.isSelected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates path to selected data source and displays warning if it is
|
||||
@ -312,14 +410,19 @@ final class LocalFilesPanel extends javax.swing.JPanel {
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JCheckBox accessTimeCheckBox;
|
||||
private javax.swing.JButton changeNameButton;
|
||||
private javax.swing.JButton clearButton;
|
||||
private javax.swing.JCheckBox createTimeCheckBox;
|
||||
private javax.swing.JLabel displayNameLabel;
|
||||
private javax.swing.JLabel errorLabel;
|
||||
private javax.swing.JLabel jLabel2;
|
||||
private javax.swing.JPanel jPanel1;
|
||||
private javax.swing.JFileChooser localFileChooser;
|
||||
private javax.swing.JCheckBox modifiedTimeCheckBox;
|
||||
private javax.swing.JButton selectButton;
|
||||
private javax.swing.JTextArea selectedPaths;
|
||||
private javax.swing.JScrollPane selectedPathsScrollPane;
|
||||
private javax.swing.JLabel timestampToIncludeLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
|
@ -16,16 +16,11 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="dspSubtypeComboBox" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="dspSubtypePanel" alignment="0" min="-2" pref="466" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="dspSubtypeComboBox" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="dspSubtypePanel" alignment="0" min="-2" pref="524" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
@ -33,8 +28,8 @@
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="dspSubtypeComboBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="dspSubtypePanel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="dspSubtypePanel" min="-2" pref="334" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@ -50,12 +45,12 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="466" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="524" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="160" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="334" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
|
@ -114,11 +114,11 @@ final class LogicalFilesDspPanel extends JPanel {
|
||||
dspSubtypePanel.setLayout(dspSubtypePanelLayout);
|
||||
dspSubtypePanelLayout.setHorizontalGroup(
|
||||
dspSubtypePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 466, Short.MAX_VALUE)
|
||||
.addGap(0, 524, Short.MAX_VALUE)
|
||||
);
|
||||
dspSubtypePanelLayout.setVerticalGroup(
|
||||
dspSubtypePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 160, Short.MAX_VALUE)
|
||||
.addGap(0, 334, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
dspSubtypeComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] {Bundle.LogicalFilesDspPanel_subTypeComboBox_localFilesOption_text(), Bundle.LogicalFilesDspPanel_subTypeComboBox_l01FileOption_text()}));
|
||||
@ -135,20 +135,17 @@ final class LogicalFilesDspPanel extends JPanel {
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(dspSubtypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(dspSubtypePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 466, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap()
|
||||
.addComponent(dspSubtypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(dspSubtypePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 524, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(dspSubtypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(dspSubtypePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 0, 0))
|
||||
.addComponent(dspSubtypePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 334, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@ -231,4 +228,32 @@ final class LogicalFilesDspPanel extends JPanel {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the createTimestampcheckbox was selected or not
|
||||
*
|
||||
* @return if box was checked or not
|
||||
*/
|
||||
Boolean getCreateTimestamp() {
|
||||
return localFilesPanel.getCreateTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the modifiedTimestampcheckbox was selected or not
|
||||
*
|
||||
* @return if box was checked or not
|
||||
*/
|
||||
Boolean getModifiedTimestamp() {
|
||||
return localFilesPanel.getModifiedTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the accessTimestampcheckbox was selected or not
|
||||
*
|
||||
* @return if box was checked or not
|
||||
*/
|
||||
Boolean getAccessTimestamp() {
|
||||
return localFilesPanel.getAccessTimestamps();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ package org.sleuthkit.autopsy.casemodule.services;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@ -424,9 +426,8 @@ public class FileManager implements Closeable {
|
||||
* directory that does not exist or cannot be read.
|
||||
*/
|
||||
public LocalFilesDataSource addLocalFilesDataSource(String deviceId, String rootVirtualDirectoryName, String timeZone, List<String> localFilePaths, FileAddProgressUpdater progressUpdater) throws TskCoreException, TskDataException {
|
||||
return addLocalFilesDataSource(deviceId, rootVirtualDirectoryName, timeZone, null, localFilePaths, progressUpdater);
|
||||
return addLocalFilesDataSource(deviceId, rootVirtualDirectoryName, timeZone, null, localFilePaths, false, false, false, progressUpdater);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of local/logical files and/or directories to the case database
|
||||
* as data source.
|
||||
@ -456,8 +457,44 @@ public class FileManager implements Closeable {
|
||||
* @throws TskDataException if any of the local file paths is for a file or
|
||||
* directory that does not exist or cannot be read.
|
||||
*/
|
||||
public LocalFilesDataSource addLocalFilesDataSource(String deviceId, String rootVirtualDirectoryName, String timeZone, Host host, List<String> localFilePaths, FileAddProgressUpdater progressUpdater) throws TskCoreException, TskDataException {
|
||||
return addLocalFilesDataSource(deviceId, rootVirtualDirectoryName, timeZone, host, localFilePaths, false, false, false, progressUpdater);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of local/logical files and/or directories to the case database
|
||||
* as data source.
|
||||
*
|
||||
* @param deviceId An ASCII-printable identifier for the
|
||||
* device associated with the data source
|
||||
* that is intended to be unique across
|
||||
* multiple cases (e.g., a UUID).
|
||||
* @param rootVirtualDirectoryName The name to give to the virtual directory
|
||||
* that will serve as the root for the
|
||||
* local/logical files and/or directories
|
||||
* that compose the data source. Pass the
|
||||
* empty string to get a default name of the
|
||||
* form: LogicalFileSet[N]
|
||||
* @param timeZone The time zone used to process the data
|
||||
* source, may be the empty string.
|
||||
* @param host The host for this data source (may be null).
|
||||
* @param localFilePaths A list of local/logical file and/or
|
||||
* directory localFilePaths.
|
||||
* @param createTime Boolean value to add the time the file was locally created
|
||||
* @param accessTime Boolean value to add the time the file was last accessed
|
||||
* @param modifiedTime Boolean value to add the time the file was locally modified
|
||||
* @param progressUpdater Called after each file/directory is added
|
||||
* to the case database.
|
||||
*
|
||||
* @return A local files data source object.
|
||||
*
|
||||
* @throws TskCoreException If there is a problem completing a database
|
||||
* operation.
|
||||
* @throws TskDataException if any of the local file paths is for a file or
|
||||
* directory that does not exist or cannot be read.
|
||||
*/
|
||||
public LocalFilesDataSource addLocalFilesDataSource(String deviceId, String rootVirtualDirectoryName, String timeZone, Host host,
|
||||
List<String> localFilePaths, FileAddProgressUpdater progressUpdater) throws TskCoreException, TskDataException {
|
||||
List<String> localFilePaths, boolean createTimestamp, boolean accessTimestamp, boolean modifiedTimestamp, FileAddProgressUpdater progressUpdater) throws TskCoreException, TskDataException {
|
||||
List<java.io.File> localFiles = getFilesAndDirectories(localFilePaths);
|
||||
CaseDbTransaction trans = null;
|
||||
try {
|
||||
@ -474,7 +511,7 @@ public class FileManager implements Closeable {
|
||||
LocalFilesDataSource dataSource = caseDb.addLocalFilesDataSource(deviceId, rootDirectoryName, timeZone, host, trans);
|
||||
List<AbstractFile> filesAdded = new ArrayList<>();
|
||||
for (java.io.File localFile : localFiles) {
|
||||
AbstractFile fileAdded = addLocalFile(trans, dataSource, localFile, TskData.EncodingType.NONE, progressUpdater);
|
||||
AbstractFile fileAdded = addLocalFile(trans, dataSource, localFile, createTimestamp, accessTimestamp, modifiedTimestamp, TskData.EncodingType.NONE, progressUpdater);
|
||||
if (null != fileAdded) {
|
||||
filesAdded.add(fileAdded);
|
||||
} else {
|
||||
@ -564,6 +601,9 @@ public class FileManager implements Closeable {
|
||||
* @param parentDirectory The root virtual directory of the data source or
|
||||
* the parent local directory.
|
||||
* @param localFile The local/logical file or directory.
|
||||
* @param createTime Boolean value to add the time the file was locally created
|
||||
* @param accessTime Boolean value to add the time the file was locally modified
|
||||
* @param modifiedTime Boolean value to add the time the file was last accessed
|
||||
* @param encodingType Type of encoding used when storing the file
|
||||
* @param progressUpdater Called after each file/directory is added to the
|
||||
* case database.
|
||||
@ -573,8 +613,8 @@ public class FileManager implements Closeable {
|
||||
* @throws TskCoreException If there is a problem completing a database
|
||||
* operation.
|
||||
*/
|
||||
private AbstractFile addLocalFile(CaseDbTransaction trans, SpecialDirectory parentDirectory, java.io.File localFile,
|
||||
TskData.EncodingType encodingType, FileAddProgressUpdater progressUpdater) throws TskCoreException {
|
||||
private AbstractFile addLocalFile(CaseDbTransaction trans, SpecialDirectory parentDirectory, java.io.File localFile, boolean createTime,
|
||||
boolean accessTime, boolean modifiedTime, TskData.EncodingType encodingType, FileAddProgressUpdater progressUpdater) throws TskCoreException {
|
||||
if (localFile.isDirectory()) {
|
||||
/*
|
||||
* Add the directory as a local directory.
|
||||
@ -588,15 +628,35 @@ public class FileManager implements Closeable {
|
||||
final java.io.File[] childFiles = localFile.listFiles();
|
||||
if (childFiles != null && childFiles.length > 0) {
|
||||
for (java.io.File childFile : childFiles) {
|
||||
addLocalFile(trans, localDirectory, childFile, progressUpdater);
|
||||
addLocalFile(trans, localDirectory, childFile, createTime, accessTime, modifiedTime, encodingType, progressUpdater);
|
||||
}
|
||||
}
|
||||
|
||||
return localDirectory;
|
||||
} else {
|
||||
return caseDb.addLocalFile(localFile.getName(), localFile.getAbsolutePath(), localFile.length(),
|
||||
0, 0, 0, 0,
|
||||
long createTimestamp = 0;
|
||||
long modifiedTimestamp = 0;
|
||||
long accessTimestamp = 0;
|
||||
try {
|
||||
BasicFileAttributes attrs;
|
||||
attrs = Files.readAttributes(localFile.toPath(), BasicFileAttributes.class);
|
||||
if (createTime) {
|
||||
createTimestamp = (attrs.creationTime().toMillis()/1000);
|
||||
}
|
||||
if (modifiedTime) {
|
||||
modifiedTimestamp = (attrs.lastModifiedTime().toMillis()/1000);
|
||||
}
|
||||
if (accessTime) {
|
||||
accessTimestamp = (attrs.lastAccessTime().toMillis()/1000);
|
||||
}
|
||||
return caseDb.addLocalFile(localFile.getName(), localFile.getAbsolutePath(), localFile.length(),
|
||||
0, createTimestamp, accessTimestamp, modifiedTimestamp,
|
||||
localFile.isFile(), encodingType, parentDirectory, trans);
|
||||
} catch (IOException ex) {
|
||||
return caseDb.addLocalFile(localFile.getName(), localFile.getAbsolutePath(), localFile.length(),
|
||||
0, 0, 0, 0,
|
||||
localFile.isFile(), encodingType, parentDirectory, trans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -752,7 +812,7 @@ public class FileManager implements Closeable {
|
||||
*/
|
||||
@Deprecated
|
||||
private AbstractFile addLocalFile(CaseDbTransaction trans, SpecialDirectory parentDirectory, java.io.File localFile, FileAddProgressUpdater progressUpdater) throws TskCoreException {
|
||||
return addLocalFile(trans, parentDirectory, localFile, TskData.EncodingType.NONE, progressUpdater);
|
||||
return addLocalFile(trans, parentDirectory, localFile, false, false, false, TskData.EncodingType.NONE, progressUpdater);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,7 +24,6 @@ import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
|
||||
/**
|
||||
* A DataContentViewer that displays text with the TextViewers available.
|
||||
|
@ -99,7 +99,7 @@ public class TextContentViewerPanel extends javax.swing.JPanel implements DataCo
|
||||
|
||||
/**
|
||||
* Determine the isPreffered score for the content viewer which is
|
||||
* displaying this panel. Score is depenedent on the score of the supported
|
||||
* displaying this panel. Score is dependent on the score of the supported
|
||||
* TextViewers which exist.
|
||||
*
|
||||
* @param node
|
||||
|
@ -292,7 +292,16 @@ public class JLNK {
|
||||
} else if (linkTargetIdList != null && !linkTargetIdList.isEmpty()) {
|
||||
String ret = "";
|
||||
for (String s : linkTargetIdList) {
|
||||
ret += s;
|
||||
if (s.endsWith("\\")) {
|
||||
ret += s;
|
||||
} else {
|
||||
if (ret.endsWith("\\")) {
|
||||
ret +=s;
|
||||
} else {
|
||||
ret += "\\";
|
||||
ret += s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -18,20 +18,24 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.directorytree;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
import com.fasterxml.jackson.databind.SequenceWriter;
|
||||
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
|
||||
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.File;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
@ -57,7 +61,8 @@ import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
|
||||
* Exports CSV version of result nodes to a location selected by the user.
|
||||
*/
|
||||
public final class ExportCSVAction extends AbstractAction {
|
||||
|
||||
// number of rows to sample for different columns
|
||||
private static final int COLUMN_SAMPLING_ROW_NUM = 100;
|
||||
private static final Logger logger = Logger.getLogger(ExportCSVAction.class.getName());
|
||||
private final static String DEFAULT_FILENAME = "Results";
|
||||
private final static List<String> columnsToSkip = Arrays.asList(AbstractFilePropertyType.SCORE.toString(),
|
||||
@ -276,43 +281,64 @@ public final class ExportCSVAction extends AbstractAction {
|
||||
progress.start();
|
||||
progress.switchToIndeterminate();
|
||||
|
||||
try (BufferedWriter br = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8))) {
|
||||
// Write BOM
|
||||
br.write('\ufeff');
|
||||
if (this.isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Set<String> columnHeaderStrs = new HashSet<>();
|
||||
List<CsvSchema.Column> columnHeaders = new ArrayList<>();
|
||||
int remainingRowsToSample = 0;
|
||||
int columnIdx = 0;
|
||||
for (Node nd: nodesToExport) {
|
||||
// sample up to 100 rows
|
||||
if (remainingRowsToSample >= COLUMN_SAMPLING_ROW_NUM) {
|
||||
break;
|
||||
}
|
||||
remainingRowsToSample++;
|
||||
|
||||
// Write the header
|
||||
List<String> headers = new ArrayList<>();
|
||||
PropertySet[] sets = nodesToExport.iterator().next().getPropertySets();
|
||||
for(PropertySet set : sets) {
|
||||
for (Property<?> prop : set.getProperties()) {
|
||||
if ( ! columnsToSkip.contains(prop.getDisplayName())) {
|
||||
headers.add(prop.getDisplayName());
|
||||
for (PropertySet ps: nd.getPropertySets()) {
|
||||
for (Property prop: ps.getProperties()) {
|
||||
if (!columnHeaderStrs.contains(prop.getDisplayName()) && !columnsToSkip.contains(prop.getName())) {
|
||||
columnHeaderStrs.add(prop.getDisplayName());
|
||||
columnHeaders.add(new CsvSchema.Column(columnIdx, prop.getDisplayName()));
|
||||
columnIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
br.write(listToCSV(headers));
|
||||
|
||||
}
|
||||
|
||||
if (this.isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CsvSchema schema = CsvSchema.builder()
|
||||
.addColumns(columnHeaders)
|
||||
.setUseHeader(true)
|
||||
.setNullValue("")
|
||||
.build();
|
||||
|
||||
CsvMapper mapper = new CsvMapper();
|
||||
ObjectWriter writer = mapper.writerFor(Map.class).with(schema);
|
||||
try (SequenceWriter seqWriter = writer.writeValues(outputFile)) {
|
||||
// Write each line
|
||||
Iterator<?> nodeIterator = nodesToExport.iterator();
|
||||
while (nodeIterator.hasNext()) {
|
||||
if (this.isCancelled()) {
|
||||
break;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Map<String, Object> rowMap = new HashMap<>();
|
||||
Node node = (Node)nodeIterator.next();
|
||||
List<String> values = new ArrayList<>();
|
||||
sets = node.getPropertySets();
|
||||
for(PropertySet set : sets) {
|
||||
for(PropertySet set : node.getPropertySets()) {
|
||||
for (Property<?> prop : set.getProperties()) {
|
||||
if ( ! columnsToSkip.contains(prop.getDisplayName())) {
|
||||
values.add(escapeQuotes(prop.getValue().toString()));
|
||||
if (!columnsToSkip.contains(prop.getName())) {
|
||||
rowMap.put(prop.getDisplayName(), prop.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
br.write(listToCSV(values));
|
||||
seqWriter.write(rowMap);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* and HighlightedText are very similar and could probably use some refactoring
|
||||
* to reduce code duplication.
|
||||
*/
|
||||
class AccountsText implements IndexedText {
|
||||
class AccountsText implements ExtractedText {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AccountsText.class.getName());
|
||||
private static final boolean DEBUG = (Version.getBuildType() == Version.Type.DEVELOPMENT);
|
||||
@ -312,7 +312,7 @@ class AccountsText implements IndexedText {
|
||||
return "<html><pre>" + highlightedText + "</pre></html>"; //NON-NLS
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Error getting highlighted text for Solr doc id " + this.solrObjectId + ", chunkID " + this.currentPage, ex); //NON-NLS
|
||||
return Bundle.IndexedText_errorMessage_errorGettingText();
|
||||
return Bundle.ExtractedText_errorMessage_errorGettingText();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ AbstractKeywordSearchPerformer.search.emptyKeywordErrorBody=Keyword list is empt
|
||||
AbstractKeywordSearchPerformer.search.noFilesInIdxMsg=<html>No files are in index yet. <br />If Solr keyword search indexing was enabled, wait for ingest to complete</html>
|
||||
AbstractKeywordSearchPerformer.search.noFilesIdxdMsg=<html>No files were indexed.<br />Re-ingest the image with the Keyword Search Module and Solr indexing enabled. </html>
|
||||
ExtractedContentViewer.toolTip=Displays extracted text from files and keyword-search results. Requires Keyword Search ingest to be run on a file to activate this viewer.
|
||||
ExtractedContentViewer.getTitle=Indexed Text
|
||||
ExtractedContentViewer.getTitle=Extracted Text
|
||||
HighlightedMatchesSource.toString=Search Results
|
||||
Installer.reportPortError=Indexing server port {0} is not available. Check if your security software does not block {1} and consider changing {2} in {3} property file in the application user folder. Then try rebooting your system if another process was causing the conflict.
|
||||
Installer.reportStopPortError=Indexing server stop port {0} is not available. Consider changing {1} in {2} property file in the application user folder.
|
||||
|
@ -15,6 +15,7 @@ ExtractAllTermsReport.error.noOpenCase=No currently open case.
|
||||
ExtractAllTermsReport.export.error=Error During Unique Word Extraction
|
||||
ExtractAllTermsReport.exportComplete=Unique Word Extraction Complete
|
||||
ExtractAllTermsReport.getName.text=Extract Unique Words
|
||||
# {0} - Number of extracted terms
|
||||
ExtractAllTermsReport.numberExtractedTerms=Extracted {0} terms...
|
||||
ExtractAllTermsReport.search.ingestInProgressBody=<html>Keyword Search Ingest is currently running.<br />Not all files have been indexed and unique word extraction might yield incomplete results.<br />Do you want to proceed with unique word extraction anyway?</html>
|
||||
ExtractAllTermsReport.search.noFilesInIdxMsg=No files are in index yet. If Solr keyword search indexing and Solr indexing were enabled, wait for ingest to complete.
|
||||
@ -22,13 +23,15 @@ ExtractAllTermsReport.search.noFilesInIdxMsg2=No files are in index yet. Re-inge
|
||||
ExtractAllTermsReport.search.searchIngestInProgressTitle=Keyword Search Ingest in Progress
|
||||
ExtractAllTermsReport.startExport=Starting Unique Word Extraction
|
||||
ExtractedContentPanel.setMarkup.panelTxt=<span style='font-style:italic'>Loading text... Please wait</span>
|
||||
# {0} - Content name
|
||||
ExtractedContentPanel.SetMarkup.progress.loading=Loading text for {0}
|
||||
ExtractedText.errorMessage.errorGettingText=<span style='font-style:italic'>Error retrieving text.</span>
|
||||
ExtractedText.FileText=File Text
|
||||
ExtractedText.warningMessage.knownFile=<span style='font-style:italic'>This file is a known file (based on MD5 hash) and does not have indexed text.</span>
|
||||
ExtractedText.warningMessage.noTextAvailable=<span style='font-style:italic'>No text available for this file.</span>
|
||||
GlobalEditListPanel.editKeyword.title=Edit Keyword
|
||||
GlobalEditListPanel.warning.text=Boundary characters ^ and $ do not match word boundaries. Consider\nreplacing with an explicit list of boundary characters, such as [ \\.,]
|
||||
GlobalEditListPanel.warning.title=Warning
|
||||
IndexedText.errorMessage.errorGettingText=<span style='font-style:italic'>Error retrieving indexed text.</span>
|
||||
IndexedText.warningMessage.knownFile=<span style='font-style:italic'>This file is a known file (based on MD5 hash) and does not have indexed text.</span>
|
||||
IndexedText.warningMessage.noTextAvailable=<span style='font-style:italic'>No indexed text for this file.</span>
|
||||
KeywordSearchGlobalSearchSettingsPanel.customizeComponents.windowsLimitedOCR=Only process images which are over 100KB in size or extracted from a document. (Beta) (Requires Windows 64-bit)
|
||||
KeywordSearchGlobalSearchSettingsPanel.customizeComponents.windowsOCR=Enable Optical Character Recognition (OCR) (Requires Windows 64-bit)
|
||||
KeywordSearchGlobalSettingsPanel.Title=Global Keyword Search Settings
|
||||
@ -49,7 +52,7 @@ KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found
|
||||
KeywordSearchResultFactory.query.exception.msg=Could not perform the query
|
||||
OpenIDE-Module-Display-Category=Ingest Module
|
||||
|
||||
OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\nThe module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found.
|
||||
OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\n\The module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found.
|
||||
OpenIDE-Module-Name=KeywordSearch
|
||||
OptionsCategory_Name_KeywordSearchOptions=Keyword Search
|
||||
OptionsCategory_Keywords_KeywordSearchOptions=Keyword Search
|
||||
@ -91,7 +94,7 @@ AbstractKeywordSearchPerformer.search.emptyKeywordErrorBody=Keyword list is empt
|
||||
AbstractKeywordSearchPerformer.search.noFilesInIdxMsg=<html>No files are in index yet. <br />If Solr keyword search indexing was enabled, wait for ingest to complete</html>
|
||||
AbstractKeywordSearchPerformer.search.noFilesIdxdMsg=<html>No files were indexed.<br />Re-ingest the image with the Keyword Search Module and Solr indexing enabled. </html>
|
||||
ExtractedContentViewer.toolTip=Displays extracted text from files and keyword-search results. Requires Keyword Search ingest to be run on a file to activate this viewer.
|
||||
ExtractedContentViewer.getTitle=Indexed Text
|
||||
ExtractedContentViewer.getTitle=Extracted Text
|
||||
HighlightedMatchesSource.toString=Search Results
|
||||
Installer.reportPortError=Indexing server port {0} is not available. Check if your security software does not block {1} and consider changing {2} in {3} property file in the application user folder. Then try rebooting your system if another process was causing the conflict.
|
||||
Installer.reportStopPortError=Indexing server stop port {0} is not available. Consider changing {1} in {2} property file in the application user folder.
|
||||
@ -137,8 +140,6 @@ KeywordSearchIngestModule.init.onlyIdxKwSkipMsg=Only indexing will be done and k
|
||||
KeywordSearchIngestModule.doInBackGround.displayName=Periodic Keyword Search
|
||||
KeywordSearchIngestModule.doInBackGround.finalizeMsg=Finalizing
|
||||
KeywordSearchIngestModule.doInBackGround.pendingMsg=(Pending)
|
||||
RawText.FileText=File Text
|
||||
RawText.ResultText=Result Text
|
||||
SearchRunner.doInBackGround.cancelMsg=(Cancelling...)
|
||||
KeywordSearchIngestModule.postIndexSummary.knowFileHeaderLbl=Files with known types
|
||||
KeywordSearchIngestModule.postIndexSummary.fileGenStringsHead=Files with general strings extracted
|
||||
@ -224,6 +225,7 @@ KeywordSearchSettings.properties_options.text={0}_Options
|
||||
KeywordSearchSettings.propertiesNSRL.text={0}_NSRL
|
||||
KeywordSearchSettings.propertiesScripts.text={0}_Scripts
|
||||
NoOpenCoreException.err.noOpenSorlCore.msg=No currently open Solr core.
|
||||
# {0} - colelction name
|
||||
Server.deleteCore.exception.msg=Failed to delete Solr colelction {0}
|
||||
Server.exceptionMessage.unableToBackupCollection=Unable to backup Solr collection
|
||||
Server.exceptionMessage.unableToCreateCollection=Unable to create Solr collection
|
||||
@ -336,6 +338,8 @@ GlobalListsManagementPanel.copyListButton.text=Copy List
|
||||
GlobalListsManagementPanel.renameListButton.text=Edit List Name
|
||||
GlobalEditListPanel.editWordButton.text=Edit Keyword
|
||||
SolrConnectionCheck.Port=Invalid port number.
|
||||
SolrIndexedText.FileText=File Text
|
||||
SolrIndexedText.ResultText=Result Text
|
||||
SolrSearch.checkingForLatestIndex.msg=Looking for text index with latest Solr and schema version
|
||||
SolrSearch.complete.msg=Text index successfully opened
|
||||
SolrSearch.creatingNewIndex.msg=Creating new text index
|
||||
|
@ -565,7 +565,7 @@
|
||||
<Component class="javax.swing.JComboBox" name="sourceComboBox">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="new javax.swing.DefaultComboBoxModel<org.sleuthkit.autopsy.keywordsearch.IndexedText>()" type="code"/>
|
||||
<Connection code="new javax.swing.DefaultComboBoxModel<org.sleuthkit.autopsy.keywordsearch.ExtractedText>()" type="code"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[150, 32767]"/>
|
||||
@ -579,7 +579,7 @@
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new javax.swing.JComboBox<>()"/>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<org.sleuthkit.autopsy.keywordsearch.IndexedText>"/>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<org.sleuthkit.autopsy.keywordsearch.ExtractedText>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
@ -622,4 +622,4 @@
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
</Form>
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2021 Basis Technology Corp.
|
||||
* Copyright 2011-2023 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -396,7 +396,7 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
textSourcePanel.add(jLabel1);
|
||||
textSourcePanel.add(fillerSmall12);
|
||||
|
||||
sourceComboBox.setModel(new javax.swing.DefaultComboBoxModel<org.sleuthkit.autopsy.keywordsearch.IndexedText>());
|
||||
sourceComboBox.setModel(new javax.swing.DefaultComboBoxModel<org.sleuthkit.autopsy.keywordsearch.ExtractedText>());
|
||||
sourceComboBox.setMaximumSize(new java.awt.Dimension(150, 32767));
|
||||
sourceComboBox.setMinimumSize(new java.awt.Dimension(150, 25));
|
||||
sourceComboBox.setPreferredSize(new java.awt.Dimension(150, 25));
|
||||
@ -443,7 +443,7 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
private javax.swing.JLabel pagesLabel;
|
||||
private javax.swing.JPopupMenu rightClickMenu;
|
||||
private javax.swing.JMenuItem selectAllMenuItem;
|
||||
private javax.swing.JComboBox<org.sleuthkit.autopsy.keywordsearch.IndexedText> sourceComboBox;
|
||||
private javax.swing.JComboBox<org.sleuthkit.autopsy.keywordsearch.ExtractedText> sourceComboBox;
|
||||
private javax.swing.JPanel textSourcePanel;
|
||||
private javax.swing.JPanel zoomPanel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
@ -457,10 +457,10 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
* default)
|
||||
*
|
||||
* @param contentName The name of the content to be displayed
|
||||
* @param sources A list of IndexedText that have different 'views' of
|
||||
* the content.
|
||||
* @param sources A list of ExtractedText that have different 'views' of
|
||||
the content.
|
||||
*/
|
||||
final void setSources(String contentName, List<IndexedText> sources) {
|
||||
final void setSources(String contentName, List<ExtractedText> sources) {
|
||||
this.lastKnownAnchor = null;
|
||||
this.contentName = contentName;
|
||||
setPanelText(null, false);
|
||||
@ -480,8 +480,8 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
*
|
||||
* @return currently selected Source
|
||||
*/
|
||||
public IndexedText getSelectedSource() {
|
||||
return (IndexedText) sourceComboBox.getSelectedItem();
|
||||
public ExtractedText getSelectedSource() {
|
||||
return (ExtractedText) sourceComboBox.getSelectedItem();
|
||||
}
|
||||
|
||||
private void setPanelText(String text, boolean detectDirection) {
|
||||
@ -556,7 +556,11 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
* @param total total number of pages to update the display with
|
||||
*/
|
||||
void updateTotalPagesDisplay(int total) {
|
||||
pageTotalLabel.setText(Integer.toString(total));
|
||||
if (total >= 0) {
|
||||
pageTotalLabel.setText(Integer.toString(total));
|
||||
} else {
|
||||
pageTotalLabel.setText("-");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -632,7 +636,7 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
*
|
||||
* @param source the selected source
|
||||
*/
|
||||
void updateControls(IndexedText source) {
|
||||
void updateControls(ExtractedText source) {
|
||||
updatePageControls(source);
|
||||
updateSearchControls(source);
|
||||
}
|
||||
@ -642,7 +646,7 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
*
|
||||
* @param source selected source
|
||||
*/
|
||||
void updatePageControls(IndexedText source) {
|
||||
void updatePageControls(ExtractedText source) {
|
||||
if (source == null) {
|
||||
enableNextPageControl(false);
|
||||
enablePrevPageControl(false);
|
||||
@ -655,13 +659,8 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
int totalPages = source.getNumberPages();
|
||||
updateTotalPagesDisplay(totalPages);
|
||||
|
||||
if (totalPages < 2) {
|
||||
enableNextPageControl(false);
|
||||
enablePrevPageControl(false);
|
||||
} else {
|
||||
enableNextPageControl(source.hasNextPage());
|
||||
enablePrevPageControl(source.hasPreviousPage());
|
||||
}
|
||||
enableNextPageControl(source.hasNextPage());
|
||||
enablePrevPageControl(source.hasPreviousPage());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -669,7 +668,7 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
*
|
||||
* @param source selected source
|
||||
*/
|
||||
void updateSearchControls(IndexedText source) {
|
||||
void updateSearchControls(ExtractedText source) {
|
||||
//setup search controls
|
||||
if (source != null && source.isSearchable()) {
|
||||
updateCurrentMatchDisplay(source.currentItem());
|
||||
@ -689,7 +688,7 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
*
|
||||
* @param source
|
||||
*/
|
||||
private void scrollToCurrentHit(final IndexedText source) {
|
||||
private void scrollToCurrentHit(final ExtractedText source) {
|
||||
if (source == null || !source.isSearchable()) {
|
||||
return;
|
||||
}
|
||||
@ -705,7 +704,7 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
* be invoked from GUI thread only.
|
||||
*/
|
||||
@NbBundle.Messages("ExtractedContentPanel.setMarkup.panelTxt=<span style='font-style:italic'>Loading text... Please wait</span>")
|
||||
private void setMarkup(IndexedText source) {
|
||||
private void setMarkup(ExtractedText source) {
|
||||
setPanelText(Bundle.ExtractedContentPanel_setMarkup_panelTxt(), false);
|
||||
new SetMarkupWorker(contentName, source).execute();
|
||||
}
|
||||
@ -719,11 +718,11 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
|
||||
private final String contentName;
|
||||
|
||||
private final IndexedText source;
|
||||
private final ExtractedText source;
|
||||
|
||||
private ProgressHandle progress;
|
||||
|
||||
SetMarkupWorker(String contentName, IndexedText source) {
|
||||
SetMarkupWorker(String contentName, ExtractedText source) {
|
||||
this.contentName = contentName;
|
||||
this.source = source;
|
||||
}
|
||||
@ -754,7 +753,7 @@ class ExtractedContentPanel extends javax.swing.JPanel implements ResizableTextP
|
||||
}
|
||||
} catch (InterruptedException | CancellationException | ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting marked up text", ex); //NON-NLS
|
||||
setPanelText(Bundle.IndexedText_errorMessage_errorGettingText(), true);
|
||||
setPanelText(Bundle.ExtractedText_errorMessage_errorGettingText(), true);
|
||||
}
|
||||
|
||||
updateControls(source);
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2023 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -24,14 +24,15 @@ import org.openide.util.NbBundle;
|
||||
* Interface to provide HTML text to display in ExtractedContentViewer. There is
|
||||
* a SOLR implementation of this that interfaces with SOLR to highlight the
|
||||
* keyword hits and a version that does not do markup so that you can simply
|
||||
* view the stored text.
|
||||
* view the stored text. There is also an implementation that extracts text from
|
||||
* a file using one os TextExtractors.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"IndexedText.errorMessage.errorGettingText=<span style='font-style:italic'>Error retrieving indexed text.</span>",
|
||||
"IndexedText.warningMessage.knownFile=<span style='font-style:italic'>This file is a known file (based on MD5 hash) and does not have indexed text.</span>",
|
||||
"IndexedText.warningMessage.noTextAvailable=<span style='font-style:italic'>No indexed text for this file.</span>"
|
||||
"ExtractedText.errorMessage.errorGettingText=<span style='font-style:italic'>Error retrieving text.</span>",
|
||||
"ExtractedText.warningMessage.knownFile=<span style='font-style:italic'>This file is a known file (based on MD5 hash) and does not have indexed text.</span>",
|
||||
"ExtractedText.warningMessage.noTextAvailable=<span style='font-style:italic'>No text available for this file.</span>"
|
||||
})
|
||||
interface IndexedText {
|
||||
interface ExtractedText {
|
||||
|
||||
/**
|
||||
* @return text optionally marked up with the subset of HTML that Swing
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Copyright 2011-2023 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -22,10 +22,15 @@ import java.awt.Component;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.tika.mime.MimeTypes;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
@ -35,6 +40,9 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.TextViewer;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.keywordsearch.AdHocSearchChildFactory.AdHocQueryResult;
|
||||
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
||||
import org.sleuthkit.autopsy.textextractors.TextExtractor;
|
||||
import org.sleuthkit.autopsy.textextractors.TextExtractorFactory;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@ -45,6 +53,7 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASS
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Report;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/**
|
||||
* A text viewer that displays the indexed text associated with a file or an
|
||||
@ -60,15 +69,30 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
|
||||
private ExtractedContentPanel panel;
|
||||
private volatile Node currentNode = null;
|
||||
private IndexedText currentSource = null;
|
||||
private ExtractedText currentSource = null;
|
||||
private FileTypeDetector fileTypeDetector = null;
|
||||
|
||||
// cache of last 10 solrHasFullyIndexedContent() requests sent to Solr.
|
||||
private SolrIsFullyIndexedCache solrCache = null;
|
||||
|
||||
/**
|
||||
* Constructs a text viewer that displays the indexed text associated with a
|
||||
* file or an artifact, possibly marked up with HTML to highlight keyword
|
||||
* hits.
|
||||
* hits. If text for the Content has not been fully indexed by Solr then
|
||||
* attempt to extract text using one of text extractors.
|
||||
*/
|
||||
public ExtractedTextViewer() {
|
||||
// This constructor is intentionally empty.
|
||||
try {
|
||||
fileTypeDetector = new FileTypeDetector();
|
||||
} catch (FileTypeDetector.FileTypeDetectorInitException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to initialize FileTypeDetector", ex); //NON-NLS
|
||||
}
|
||||
|
||||
solrCache = new SolrIsFullyIndexedCache();
|
||||
// clear the cache when case opens or closes
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
|
||||
solrCache.clearCache();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,7 +123,7 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
* Assemble a collection of all of the indexed text "sources" for the
|
||||
* node.
|
||||
*/
|
||||
List<IndexedText> sources = new ArrayList<>();
|
||||
List<ExtractedText> sources = new ArrayList<>();
|
||||
Lookup nodeLookup = node.getLookup();
|
||||
|
||||
/**
|
||||
@ -115,7 +139,7 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
* First, get text with highlighted hits if this node is for a search
|
||||
* result.
|
||||
*/
|
||||
IndexedText highlightedHitText = null;
|
||||
ExtractedText highlightedHitText = null;
|
||||
if (adHocQueryResult != null) {
|
||||
/*
|
||||
* The node is an ad hoc search result node.
|
||||
@ -153,10 +177,25 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
* Next, add the "raw" (not highlighted) text, if any, for any file
|
||||
* associated with the node.
|
||||
*/
|
||||
IndexedText rawContentText = null;
|
||||
ExtractedText rawContentText = null;
|
||||
if (file != null) {
|
||||
rawContentText = new RawText(file, file.getId());
|
||||
sources.add(rawContentText);
|
||||
|
||||
// see if Solr has fully indexed this file
|
||||
if (solrHasFullyIndexedContent(file.getId())) {
|
||||
rawContentText = new SolrIndexedText(file, file.getId());
|
||||
sources.add(rawContentText);
|
||||
} else {
|
||||
// Solr does not have fully indexed content.
|
||||
// see if it's a file type for which we can extract text
|
||||
if (ableToExtractTextFromFile(file)) {
|
||||
try {
|
||||
rawContentText = new FileReaderExtractedText(file);
|
||||
sources.add(rawContentText);
|
||||
} catch (TextExtractorFactory.NoTextExtractorFound | TextExtractor.InitReaderException ex) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -164,15 +203,18 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
* associated with the node.
|
||||
*/
|
||||
if (report != null) {
|
||||
rawContentText = new RawText(report, report.getId());
|
||||
sources.add(rawContentText);
|
||||
// see if Solr has fully indexed this file
|
||||
if (solrHasFullyIndexedContent(report.getId())) {
|
||||
rawContentText = new SolrIndexedText(report, report.getId());
|
||||
sources.add(rawContentText);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, add the "raw" (not highlighted) text, if any, for any
|
||||
* artifact associated with the node.
|
||||
*/
|
||||
IndexedText rawArtifactText = null;
|
||||
ExtractedText rawArtifactText = null;
|
||||
try {
|
||||
rawArtifactText = getRawArtifactText(artifact);
|
||||
if (rawArtifactText != null) {
|
||||
@ -192,7 +234,7 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
}
|
||||
|
||||
// Push the text sources into the panel.
|
||||
for (IndexedText source : sources) {
|
||||
for (ExtractedText source : sources) {
|
||||
int currentPage = source.getCurrentPage();
|
||||
if (currentPage == 0 && source.hasNextPage()) {
|
||||
source.nextPage();
|
||||
@ -208,8 +250,8 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
|
||||
}
|
||||
|
||||
static private IndexedText getRawArtifactText(BlackboardArtifact artifact) throws TskCoreException, NoCurrentCaseException {
|
||||
IndexedText rawArtifactText = null;
|
||||
private ExtractedText getRawArtifactText(BlackboardArtifact artifact) throws TskCoreException, NoCurrentCaseException {
|
||||
ExtractedText rawArtifactText = null;
|
||||
if (null != artifact) {
|
||||
/*
|
||||
* For keyword hit artifacts, add the text of the artifact that hit,
|
||||
@ -222,18 +264,21 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
if (attribute != null) {
|
||||
long artifactId = attribute.getValueLong();
|
||||
BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(artifactId);
|
||||
rawArtifactText = new RawText(associatedArtifact, associatedArtifact.getArtifactID());
|
||||
|
||||
if (solrHasFullyIndexedContent(associatedArtifact.getArtifactID())) {
|
||||
rawArtifactText = new SolrIndexedText(associatedArtifact, associatedArtifact.getArtifactID());
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
rawArtifactText = new RawText(artifact, artifact.getArtifactID());
|
||||
if (solrHasFullyIndexedContent(artifact.getArtifactID())) {
|
||||
rawArtifactText = new SolrIndexedText(artifact, artifact.getArtifactID());
|
||||
}
|
||||
}
|
||||
}
|
||||
return rawArtifactText;
|
||||
}
|
||||
|
||||
static private IndexedText getAccountsText(Content content, Lookup nodeLookup) throws TskCoreException {
|
||||
static private ExtractedText getAccountsText(Content content, Lookup nodeLookup) throws TskCoreException {
|
||||
/*
|
||||
* get all the credit card artifacts
|
||||
*/
|
||||
@ -247,7 +292,7 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
}
|
||||
|
||||
private void scrollToCurrentHit() {
|
||||
final IndexedText source = panel.getSelectedSource();
|
||||
final ExtractedText source = panel.getSelectedSource();
|
||||
if (source == null || !source.isSearchable()) {
|
||||
return;
|
||||
}
|
||||
@ -340,8 +385,18 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
* data source instead of a file.
|
||||
*/
|
||||
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
|
||||
if (file != null && solrHasContent(file.getId())) {
|
||||
return true;
|
||||
if (file != null) {
|
||||
|
||||
// see if Solr has fully indexed this file
|
||||
if (solrHasFullyIndexedContent(file.getId())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Solr does not have fully indexed content.
|
||||
// see if it's a file type for which we can extract text
|
||||
if (ableToExtractTextFromFile(file)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -351,7 +406,7 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
* indexed text for the artifact.
|
||||
*/
|
||||
if (artifact != null) {
|
||||
return solrHasContent(artifact.getArtifactID());
|
||||
return solrHasFullyIndexedContent(artifact.getArtifactID());
|
||||
}
|
||||
|
||||
/*
|
||||
@ -361,7 +416,7 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
*/
|
||||
Report report = node.getLookup().lookup(Report.class);
|
||||
if (report != null) {
|
||||
return solrHasContent(report.getId());
|
||||
return solrHasFullyIndexedContent(report.getId());
|
||||
}
|
||||
|
||||
/*
|
||||
@ -381,36 +436,102 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
* panel hasn't been created yet)
|
||||
*
|
||||
* @param contentName The name of the content to be displayed
|
||||
* @param sources A list of IndexedText that have different 'views' of
|
||||
* the content.
|
||||
* @param sources A list of ExtractedText that have different 'views' of
|
||||
the content.
|
||||
*/
|
||||
private void setPanel(String contentName, List<IndexedText> sources) {
|
||||
private void setPanel(String contentName, List<ExtractedText> sources) {
|
||||
if (panel != null) {
|
||||
panel.setSources(contentName, sources);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Solr has extracted content for a given node
|
||||
* Check if Solr has indexed ALL of the content for a given node. Note that
|
||||
* in some situations Solr only indexes parts of a file. This happens when
|
||||
* an in-line KWS finds a KW hit in the file - only the chunks with the KW
|
||||
* hit (+/- 1 chunk) get indexed by Solr. That is not enough for the
|
||||
* purposes of this text viewer as we need to display all of the text in the
|
||||
* file.
|
||||
*
|
||||
* @param objectId
|
||||
*
|
||||
* @return true if Solr has content, else false
|
||||
*/
|
||||
private boolean solrHasContent(Long objectId) {
|
||||
private boolean solrHasFullyIndexedContent(Long objectId) {
|
||||
|
||||
// check if we have cached this decision
|
||||
if (solrCache.containsKey(objectId)) {
|
||||
return solrCache.getCombination(objectId);
|
||||
}
|
||||
|
||||
final Server solrServer = KeywordSearch.getServer();
|
||||
if (solrServer.coreIsOpen() == false) {
|
||||
solrCache.putCombination(objectId, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// verify that all of the chunks in the file have been indexed.
|
||||
try {
|
||||
return solrServer.queryIsIndexed(objectId);
|
||||
boolean isFullyIndexed = solrServer.queryIsFullyIndexed(objectId);
|
||||
solrCache.putCombination(objectId, isFullyIndexed);
|
||||
return isFullyIndexed;
|
||||
} catch (NoOpenCoreException | KeywordSearchModuleException ex) {
|
||||
logger.log(Level.SEVERE, "Error querying Solr server", ex); //NON-NLS
|
||||
solrCache.putCombination(objectId, false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we can extract text for this file type using one of our text extractors.
|
||||
* NOTE: the logic in this method should be similar and based on the
|
||||
* logic of how KeywordSearchIngestModule decides which files to index.
|
||||
*
|
||||
* @param file Abstract File
|
||||
*
|
||||
* @return true if text can be extracted from file, else false
|
||||
*/
|
||||
private boolean ableToExtractTextFromFile(AbstractFile file) {
|
||||
|
||||
TskData.TSK_DB_FILES_TYPE_ENUM fileType = file.getType();
|
||||
|
||||
if (fileType.equals(TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((fileType.equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
|
||||
|| fileType.equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS))
|
||||
|| (fileType.equals(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final long size = file.getSize();
|
||||
if (file.isDir() || size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String mimeType = fileTypeDetector.getMIMEType(file).trim().toLowerCase();
|
||||
|
||||
if (KeywordSearchIngestModule.ARCHIVE_MIME_TYPES.contains(mimeType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (MimeTypes.OCTET_STREAM.equals(mimeType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Often times there is an exception when trying to initiale a reader,
|
||||
// thus making that specific file "unsupported". The only way to identify
|
||||
// this situation is to initialize the reader.
|
||||
try {
|
||||
FileReaderExtractedText tmp = new FileReaderExtractedText(file);
|
||||
} catch (TextExtractorFactory.NoTextExtractorFound | TextExtractor.InitReaderException ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to select the next match found in the text
|
||||
*/
|
||||
@ -418,7 +539,7 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
IndexedText source = panel.getSelectedSource();
|
||||
ExtractedText source = panel.getSelectedSource();
|
||||
if (source == null) {
|
||||
// reset
|
||||
panel.updateControls(null);
|
||||
@ -461,7 +582,7 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
IndexedText source = panel.getSelectedSource();
|
||||
ExtractedText source = panel.getSelectedSource();
|
||||
final boolean hasPreviousItem = source.hasPreviousItem();
|
||||
final boolean hasPreviousPage = source.hasPreviousPage();
|
||||
int indexVal;
|
||||
@ -598,4 +719,39 @@ public class ExtractedTextViewer implements TextViewer {
|
||||
previousPage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class maintains a cache of last 10 solrHasFullyIndexedContent()
|
||||
* requests sent to Solr.
|
||||
*/
|
||||
private class SolrIsFullyIndexedCache {
|
||||
|
||||
private static final int CACHE_SIZE = 10;
|
||||
private final LinkedHashMap<Long, Boolean> cache;
|
||||
|
||||
private SolrIsFullyIndexedCache() {
|
||||
this.cache = new LinkedHashMap<Long, Boolean>(CACHE_SIZE, 0.75f, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Long, Boolean> eldest) {
|
||||
return size() > CACHE_SIZE;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void putCombination(long key, boolean value) {
|
||||
cache.put(key, value);
|
||||
}
|
||||
|
||||
public Boolean getCombination(long key) {
|
||||
return cache.get(key);
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
public boolean containsKey(long key) {
|
||||
return cache.containsKey(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2023 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.keywordsearch;
|
||||
|
||||
import com.google.common.io.CharSource;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.textextractors.TextExtractor;
|
||||
import org.sleuthkit.autopsy.textextractors.TextExtractorFactory;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
|
||||
/**
|
||||
* A "source" for abstractFile viewer that displays "raw" extracted text for a
|
||||
* file. Only supports file types for which there are text extractors. Uses
|
||||
* chunking algorithm used by KeywordSearchIngestModule. The readers used in
|
||||
* chunking don't have ability to go backwards or to fast forward to a specific
|
||||
* offset. Therefore there is no way to scroll pages back, or to determine how
|
||||
* many total pages there are.
|
||||
*/
|
||||
class FileReaderExtractedText implements ExtractedText {
|
||||
|
||||
private int numPages = 0;
|
||||
private int currentPage = 0;
|
||||
private final AbstractFile abstractFile;
|
||||
private Chunker chunker = null;
|
||||
private static final Logger logger = Logger.getLogger(FileReaderExtractedText.class.getName());
|
||||
|
||||
/**
|
||||
* Construct a new ExtractedText object for the given abstract file.
|
||||
*
|
||||
* @param file Abstract file.
|
||||
*/
|
||||
FileReaderExtractedText(AbstractFile file) throws TextExtractorFactory.NoTextExtractorFound, TextExtractor.InitReaderException {
|
||||
this.abstractFile = file;
|
||||
this.numPages = -1; // We don't know how many pages there are until we reach end of the document
|
||||
|
||||
TextExtractor extractor = TextExtractorFactory.getExtractor(abstractFile, null);
|
||||
|
||||
Map<String, String> extractedMetadata = new HashMap<>();
|
||||
Reader sourceReader = getTikaOrTextExtractor(extractor, abstractFile, extractedMetadata);
|
||||
|
||||
//Get a reader for the content of the given source
|
||||
BufferedReader reader = new BufferedReader(sourceReader);
|
||||
this.chunker = new Chunker(reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentPage() {
|
||||
return this.currentPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNextPage() {
|
||||
if (chunker.hasNext()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPreviousPage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextPage() {
|
||||
if (!hasNextPage()) {
|
||||
throw new IllegalStateException(
|
||||
NbBundle.getMessage(this.getClass(), "ExtractedContentViewer.nextPage.exception.msg"));
|
||||
}
|
||||
++currentPage;
|
||||
return currentPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previousPage() {
|
||||
if (!hasPreviousPage()) {
|
||||
throw new IllegalStateException(
|
||||
NbBundle.getMessage(this.getClass(), "ExtractedContentViewer.previousPage.exception.msg"));
|
||||
}
|
||||
--currentPage;
|
||||
return currentPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNextItem() {
|
||||
throw new UnsupportedOperationException(
|
||||
NbBundle.getMessage(this.getClass(), "ExtractedContentViewer.hasNextItem.exception.msg"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPreviousItem() {
|
||||
throw new UnsupportedOperationException(
|
||||
NbBundle.getMessage(this.getClass(), "ExtractedContentViewer.hasPreviousItem.exception.msg"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextItem() {
|
||||
throw new UnsupportedOperationException(
|
||||
NbBundle.getMessage(this.getClass(), "ExtractedContentViewer.nextItem.exception.msg"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previousItem() {
|
||||
throw new UnsupportedOperationException(
|
||||
NbBundle.getMessage(this.getClass(), "ExtractedContentViewer.previousItem.exception.msg"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int currentItem() {
|
||||
throw new UnsupportedOperationException(
|
||||
NbBundle.getMessage(this.getClass(), "ExtractedContentViewer.currentItem.exception.msg"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
try {
|
||||
return getContentText(currentPage);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Couldn't get extracted text", ex); //NON-NLS
|
||||
}
|
||||
return Bundle.ExtractedText_errorMessage_errorGettingText();
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"ExtractedText.FileText=File Text"})
|
||||
@Override
|
||||
public String toString() {
|
||||
return Bundle.ExtractedText_FileText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSearchable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAnchorPrefix() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberHits() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberPages() {
|
||||
return numPages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract text from abstractFile
|
||||
*
|
||||
* @param currentPage currently used page
|
||||
*
|
||||
* @return the extracted text
|
||||
*/
|
||||
private String getContentText(int currentPage) throws TextExtractor.InitReaderException, IOException, Exception {
|
||||
String indexedText;
|
||||
if (chunker.hasNext()) {
|
||||
Chunker.Chunk chunk = chunker.next();
|
||||
chunk.setChunkId(currentPage);
|
||||
|
||||
if (chunker.hasException()) {
|
||||
logger.log(Level.WARNING, "Error chunking content from " + abstractFile.getId() + ": " + abstractFile.getName(), chunker.getException());
|
||||
throw chunker.getException();
|
||||
}
|
||||
|
||||
indexedText = chunk.toString();
|
||||
} else {
|
||||
return Bundle.ExtractedText_errorMessage_errorGettingText();
|
||||
}
|
||||
|
||||
indexedText = EscapeUtil.escapeHtml(indexedText).trim();
|
||||
StringBuilder sb = new StringBuilder(indexedText.length() + 20);
|
||||
sb.append("<pre>").append(indexedText).append("</pre>"); //NON-NLS
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private Reader getTikaOrTextExtractor(TextExtractor extractor, AbstractFile aFile,
|
||||
Map<String, String> extractedMetadata) throws TextExtractor.InitReaderException {
|
||||
|
||||
Reader fileText = extractor.getReader();
|
||||
Reader finalReader;
|
||||
try {
|
||||
Map<String, String> metadata = extractor.getMetadata();
|
||||
if (!metadata.isEmpty()) {
|
||||
// save the metadata map to use after this method is complete.
|
||||
extractedMetadata.putAll(metadata);
|
||||
}
|
||||
CharSource formattedMetadata = KeywordSearchIngestModule.getMetaDataCharSource(metadata);
|
||||
//Append the metadata to end of the file text
|
||||
finalReader = CharSource.concat(new CharSource() {
|
||||
//Wrap fileText reader for concatenation
|
||||
@Override
|
||||
public Reader openStream() throws IOException {
|
||||
return fileText;
|
||||
}
|
||||
}, formattedMetadata).openStream();
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, String.format("Could not format extracted metadata for file %s [id=%d]",
|
||||
aFile.getName(), aFile.getId()), ex);
|
||||
//Just send file text.
|
||||
finalReader = fileText;
|
||||
}
|
||||
//divide into chunks
|
||||
return finalReader;
|
||||
}
|
||||
|
||||
}
|
@ -53,7 +53,7 @@ import org.sleuthkit.datamodel.TskData;
|
||||
* Highlights hits for a given document. Knows about pages and such for the
|
||||
* content viewer.
|
||||
*/
|
||||
class HighlightedText implements IndexedText {
|
||||
class HighlightedText implements ExtractedText {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(HighlightedText.class.getName());
|
||||
|
||||
@ -476,7 +476,7 @@ class HighlightedText implements IndexedText {
|
||||
return "<html><pre>" + highlightedContent + "</pre></html>"; //NON-NLS
|
||||
} catch (TskCoreException | KeywordSearchModuleException | NoOpenCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting highlighted text for Solr doc id " + solrObjectId + ", chunkID " + chunkID + ", highlight query: " + highlightField, ex); //NON-NLS
|
||||
return Bundle.IndexedText_errorMessage_errorGettingText();
|
||||
return Bundle.ExtractedText_errorMessage_errorGettingText();
|
||||
}
|
||||
}
|
||||
|
||||
@ -520,7 +520,7 @@ class HighlightedText implements IndexedText {
|
||||
*/
|
||||
static String attemptManualHighlighting(SolrDocumentList solrDocumentList, String highlightField, Collection<String> keywords) {
|
||||
if (solrDocumentList.isEmpty()) {
|
||||
return Bundle.IndexedText_errorMessage_errorGettingText();
|
||||
return Bundle.ExtractedText_errorMessage_errorGettingText();
|
||||
}
|
||||
|
||||
// It doesn't make sense for there to be more than a single document in
|
||||
|
@ -38,7 +38,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.tika.mime.MimeTypes;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
@ -96,7 +95,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
|
||||
* generally text extractors should ignore archives and let unpacking
|
||||
* modules take care of them
|
||||
*/
|
||||
private static final List<String> ARCHIVE_MIME_TYPES
|
||||
static final List<String> ARCHIVE_MIME_TYPES
|
||||
= ImmutableList.of(
|
||||
//ignore unstructured binary and compressed data, for which string extraction or unzipper works better
|
||||
"application/x-7z-compressed", //NON-NLS
|
||||
@ -683,7 +682,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
|
||||
@NbBundle.Messages({
|
||||
"KeywordSearchIngestModule.metadataTitle=METADATA"
|
||||
})
|
||||
private CharSource getMetaDataCharSource(Map<String, String> metadata) {
|
||||
static CharSource getMetaDataCharSource(Map<String, String> metadata) {
|
||||
return CharSource.wrap(new StringBuilder(
|
||||
String.format("\n\n------------------------------%s------------------------------\n\n",
|
||||
Bundle.KeywordSearchIngestModule_metadataTitle()))
|
||||
|
@ -1635,23 +1635,29 @@ public class Server {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the file is indexed (either as a whole as a chunk)
|
||||
* Return true if the file is fully indexed (no chunks are missing)
|
||||
*
|
||||
* @param contentID
|
||||
*
|
||||
* @return true if it is indexed
|
||||
* @return true if it is fully indexed
|
||||
*
|
||||
* @throws KeywordSearchModuleException
|
||||
* @throws NoOpenCoreException
|
||||
*/
|
||||
public boolean queryIsIndexed(long contentID) throws KeywordSearchModuleException, NoOpenCoreException {
|
||||
public boolean queryIsFullyIndexed(long contentID) throws KeywordSearchModuleException, NoOpenCoreException {
|
||||
currentCoreLock.readLock().lock();
|
||||
try {
|
||||
if (null == currentCollection) {
|
||||
throw new NoOpenCoreException();
|
||||
}
|
||||
try {
|
||||
return currentCollection.queryIsIndexed(contentID);
|
||||
int totalNumChunks = currentCollection.queryTotalNumFileChunks(contentID);
|
||||
if (totalNumChunks == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int numIndexedChunks = currentCollection.queryNumIndexedChunks(contentID);
|
||||
return numIndexedChunks == totalNumChunks;
|
||||
} catch (Exception ex) {
|
||||
// intentional "catch all" as Solr is known to throw all kinds of Runtime exceptions
|
||||
throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.queryIsIdxd.exception.msg"), ex);
|
||||
@ -1680,7 +1686,7 @@ public class Server {
|
||||
throw new NoOpenCoreException();
|
||||
}
|
||||
try {
|
||||
return currentCollection.queryNumFileChunks(fileID);
|
||||
return currentCollection.queryTotalNumFileChunks(fileID);
|
||||
} catch (Exception ex) {
|
||||
// intentional "catch all" as Solr is known to throw all kinds of Runtime exceptions
|
||||
throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.queryNumFileChunks.exception.msg"), ex);
|
||||
@ -2484,7 +2490,7 @@ public class Server {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the file is indexed (either as a whole as a chunk)
|
||||
* Return true if the file is indexed (either as a whole or as a chunk)
|
||||
*
|
||||
* @param contentID
|
||||
*
|
||||
@ -2502,17 +2508,20 @@ public class Server {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute query that gets number of indexed file chunks for a file
|
||||
* Execute query that gets total number of file chunks for a file. NOTE:
|
||||
* this does not imply that all of the chunks have been indexed. This
|
||||
* parameter simply stores the total number of chunks that the file had
|
||||
* (as determined during chunking).
|
||||
*
|
||||
* @param contentID file id of the original file broken into chunks and
|
||||
* indexed
|
||||
* indexed
|
||||
*
|
||||
* @return int representing number of indexed file chunks, 0 if there is
|
||||
* no chunks
|
||||
* @return int representing number of file chunks, 0 if there is no
|
||||
* chunks
|
||||
*
|
||||
* @throws SolrServerException
|
||||
*/
|
||||
private int queryNumFileChunks(long contentID) throws SolrServerException, IOException {
|
||||
private int queryTotalNumFileChunks(long contentID) throws SolrServerException, IOException {
|
||||
final SolrQuery q = new SolrQuery();
|
||||
q.setQuery("*:*");
|
||||
String filterQuery = Schema.ID.toString() + ":" + KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
|
||||
@ -2522,7 +2531,7 @@ public class Server {
|
||||
SolrDocumentList solrDocuments = query(q).getResults();
|
||||
if (!solrDocuments.isEmpty()) {
|
||||
SolrDocument solrDocument = solrDocuments.get(0);
|
||||
if (solrDocument != null) {
|
||||
if (solrDocument != null && !solrDocument.isEmpty()) {
|
||||
Object fieldValue = solrDocument.getFieldValue(Schema.NUM_CHUNKS.toString());
|
||||
return (Integer)fieldValue;
|
||||
}
|
||||
@ -2532,11 +2541,27 @@ public class Server {
|
||||
logger.log(Level.SEVERE, "Error getting content from Solr. Solr document id " + contentID + ", query: " + filterQuery, ex); //NON-NLS
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ERROR: we should never get here
|
||||
logger.log(Level.SEVERE, "Error getting content from Solr. Solr document id " + contentID + ", query: " + filterQuery); //NON-NLS
|
||||
// File not indexed
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute query that gets number of indexed chunks for a specific Solr
|
||||
* document, without actually returning the content.
|
||||
*
|
||||
* @param contentID file id of the original file broken into chunks and
|
||||
* indexed
|
||||
*
|
||||
* @return int representing number of indexed chunks
|
||||
*
|
||||
* @throws SolrServerException
|
||||
*/
|
||||
int queryNumIndexedChunks(long contentID) throws SolrServerException, IOException {
|
||||
SolrQuery q = new SolrQuery(Server.Schema.ID + ":" + KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID)) + Server.CHUNK_ID_SEPARATOR + "*");
|
||||
q.setRows(0);
|
||||
int numChunks = (int) query(q).getResults().getNumFound();
|
||||
return numChunks;
|
||||
}
|
||||
}
|
||||
|
||||
class ServerAction extends AbstractAction {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2023 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -30,9 +30,9 @@ import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/**
|
||||
* A "source" for the extracted content viewer that displays "raw" (not
|
||||
* highlighted) indexed text for a file or an artifact.
|
||||
* highlighted) Solr indexed text for a file or an artifact.
|
||||
*/
|
||||
class RawText implements IndexedText {
|
||||
class SolrIndexedText implements ExtractedText {
|
||||
|
||||
private int numPages = 0;
|
||||
private int currentPage = 0;
|
||||
@ -40,15 +40,12 @@ class RawText implements IndexedText {
|
||||
private final Content content;
|
||||
private final BlackboardArtifact blackboardArtifact;
|
||||
private final long objectId;
|
||||
//keep last content cached
|
||||
private String cachedString;
|
||||
private int cachedChunk;
|
||||
private static final Logger logger = Logger.getLogger(RawText.class.getName());
|
||||
private static final Logger logger = Logger.getLogger(SolrIndexedText.class.getName());
|
||||
|
||||
/**
|
||||
* Construct a new RawText object for the given content and object id. This
|
||||
* Construct a new SolrIndexedText object for the given content and object id. This
|
||||
* constructor needs both a content object and an object id because the
|
||||
* RawText implementation attempts to provide useful messages in the text
|
||||
* SolrIndexedText implementation attempts to provide useful messages in the text
|
||||
* content viewer for (a) the case where a file has not been indexed because
|
||||
* known files are being skipped and (b) the case where the file content has
|
||||
* not yet been indexed.
|
||||
@ -56,14 +53,14 @@ class RawText implements IndexedText {
|
||||
* @param content Used to get access to file names and "known" status.
|
||||
* @param objectId Either a file id or an artifact id.
|
||||
*/
|
||||
RawText(Content content, long objectId) {
|
||||
SolrIndexedText(Content content, long objectId) {
|
||||
this.content = content;
|
||||
this.blackboardArtifact = null;
|
||||
this.objectId = objectId;
|
||||
initialize();
|
||||
}
|
||||
|
||||
RawText(BlackboardArtifact bba, long objectId) {
|
||||
SolrIndexedText(BlackboardArtifact bba, long objectId) {
|
||||
this.content = null;
|
||||
this.blackboardArtifact = bba;
|
||||
this.objectId = objectId;
|
||||
@ -155,18 +152,18 @@ class RawText implements IndexedText {
|
||||
} catch (SolrServerException | NoOpenCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Couldn't get extracted text", ex); //NON-NLS
|
||||
}
|
||||
return Bundle.IndexedText_errorMessage_errorGettingText();
|
||||
return Bundle.ExtractedText_errorMessage_errorGettingText();
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"RawText.FileText=File Text",
|
||||
"RawText.ResultText=Result Text"})
|
||||
"SolrIndexedText.FileText=File Text",
|
||||
"SolrIndexedText.ResultText=Result Text"})
|
||||
@Override
|
||||
public String toString() {
|
||||
if (null != content) {
|
||||
return Bundle.RawText_FileText();
|
||||
return Bundle.SolrIndexedText_FileText();
|
||||
} else {
|
||||
return Bundle.RawText_ResultText();
|
||||
return Bundle.SolrIndexedText_ResultText();
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,43 +236,32 @@ class RawText implements IndexedText {
|
||||
//we know it's AbstractFile, but do quick check to make sure if we index other objects in future
|
||||
boolean isKnown = TskData.FileKnown.KNOWN.equals(((AbstractFile) content).getKnown());
|
||||
if (isKnown && KeywordSearchSettings.getSkipKnown()) {
|
||||
msg = Bundle.IndexedText_warningMessage_knownFile();
|
||||
msg = Bundle.ExtractedText_warningMessage_knownFile();
|
||||
}
|
||||
}
|
||||
if (msg == null) {
|
||||
msg = Bundle.IndexedText_warningMessage_noTextAvailable();
|
||||
msg = Bundle.ExtractedText_warningMessage_noTextAvailable();
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
int chunkId = currentPage;
|
||||
|
||||
//check if cached
|
||||
if (cachedString != null) {
|
||||
if (cachedChunk == chunkId) {
|
||||
return cachedString;
|
||||
}
|
||||
}
|
||||
|
||||
//not cached
|
||||
String indexedText = solrServer.getSolrContent(this.objectId, chunkId);
|
||||
if (indexedText == null) {
|
||||
if (content instanceof AbstractFile) {
|
||||
return Bundle.IndexedText_errorMessage_errorGettingText();
|
||||
return Bundle.ExtractedText_errorMessage_errorGettingText();
|
||||
} else {
|
||||
return Bundle.IndexedText_warningMessage_noTextAvailable();
|
||||
return Bundle.ExtractedText_warningMessage_noTextAvailable();
|
||||
}
|
||||
} else if (indexedText.isEmpty()) {
|
||||
return Bundle.IndexedText_warningMessage_noTextAvailable();
|
||||
return Bundle.ExtractedText_warningMessage_noTextAvailable();
|
||||
}
|
||||
|
||||
cachedString = EscapeUtil.escapeHtml(indexedText).trim();
|
||||
StringBuilder sb = new StringBuilder(cachedString.length() + 20);
|
||||
sb.append("<pre>").append(cachedString).append("</pre>"); //NON-NLS
|
||||
cachedString = sb.toString();
|
||||
cachedChunk = chunkId;
|
||||
|
||||
return cachedString;
|
||||
indexedText = EscapeUtil.escapeHtml(indexedText).trim();
|
||||
StringBuilder sb = new StringBuilder(indexedText.length() + 20);
|
||||
sb.append("<pre>").append(indexedText).append("</pre>"); //NON-NLS
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -290,7 +276,7 @@ class RawText implements IndexedText {
|
||||
private String getArtifactText() throws NoOpenCoreException, SolrServerException {
|
||||
String indexedText = KeywordSearch.getServer().getSolrContent(this.objectId, 1);
|
||||
if (indexedText == null || indexedText.isEmpty()) {
|
||||
return Bundle.IndexedText_errorMessage_errorGettingText();
|
||||
return Bundle.ExtractedText_errorMessage_errorGettingText();
|
||||
}
|
||||
|
||||
indexedText = EscapeUtil.escapeHtml(indexedText).trim();
|
@ -167,6 +167,9 @@ class RecentDocumentsByLnk extends Extract {
|
||||
String fileName = FilenameUtils.getName(normalizePathName);
|
||||
String filePath = FilenameUtils.getPath(normalizePathName);
|
||||
List<AbstractFile> sourceFiles;
|
||||
if (filePath == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
sourceFiles = currentCase.getSleuthkitCase().getFileManager().findFilesExactNameExactPath(dataSource, fileName, filePath);
|
||||
for (AbstractFile sourceFile : sourceFiles) {
|
||||
|
@ -301,7 +301,7 @@ class PstParser implements AutoCloseable{
|
||||
email.setRecipients(toAddress);
|
||||
email.setCc(ccAddress);
|
||||
email.setBcc(bccAddress);
|
||||
email.setSender(getSender(msg.getSenderName(), msg.getSentRepresentingSMTPAddress()));
|
||||
email.setSender(getSender(msg.getSenderName(), (msg.getSentRepresentingSMTPAddress().isEmpty()) ? msg.getSenderEmailAddress() : msg.getSentRepresentingSMTPAddress()));
|
||||
email.setSentDate(msg.getMessageDeliveryTime());
|
||||
email.setTextBody(msg.getBody());
|
||||
if (false == msg.getTransportMessageHeaders().isEmpty()) {
|
||||
@ -319,7 +319,7 @@ class PstParser implements AutoCloseable{
|
||||
email.setSubject(msg.getSubject());
|
||||
email.setId(msg.getDescriptorNodeId());
|
||||
email.setMessageID(msg.getInternetMessageId());
|
||||
|
||||
|
||||
String inReplyToID = msg.getInReplyToId();
|
||||
email.setInReplyToID(inReplyToID);
|
||||
|
||||
@ -480,7 +480,7 @@ class PstParser implements AutoCloseable{
|
||||
} else if (addr.isEmpty()) {
|
||||
return name;
|
||||
} else {
|
||||
return name + ": " + addr;
|
||||
return name + " <" + addr + ">";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -769,6 +769,14 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
addArtifactAttribute(((id < 0L) ? NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(id)),
|
||||
ATTRIBUTE_TYPE.TSK_MSG_ID, bbattributes);
|
||||
|
||||
try {
|
||||
addArtifactAttribute((email.hasAttachment() ? "Yes" : ""),
|
||||
blackboard.getOrAddAttributeType("EMAIL_HAS_ATTACHMENT", BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, "Has Attachments"),
|
||||
bbattributes);
|
||||
} catch (Blackboard.BlackboardException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to create EMAIL_HAS_ATTACHMENT attribute" , ex); //NON-NLS
|
||||
}
|
||||
|
||||
addArtifactAttribute(((localPath.isEmpty() == false) ? localPath : ""),
|
||||
ATTRIBUTE_TYPE.TSK_PATH, bbattributes);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user