mirror of
https://github.com/elisspace/autopsy.git
synced 2026-09-18 08:41:43 +00:00
Merge remote-tracking branch 'upstream/develop' into 4052_CancelImageWriter
This commit is contained in:
@@ -292,9 +292,10 @@ public class Case {
|
||||
*/
|
||||
ADDING_DATA_SOURCE_FAILED,
|
||||
/**
|
||||
* A new data source has been added to the current case. The old value
|
||||
* of the PropertyChangeEvent is null, the new value is the newly-added
|
||||
* data source (type: Content). Cast the PropertyChangeEvent to
|
||||
* A new data source or series of data sources have been added to the
|
||||
* current case. The old value of the PropertyChangeEvent is null, the
|
||||
* new value is the newly-added data source (type: Content). Cast the
|
||||
* PropertyChangeEvent to
|
||||
* org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent to
|
||||
* access additional event data.
|
||||
*/
|
||||
@@ -1099,6 +1100,10 @@ public class Case {
|
||||
/*
|
||||
* Open the top components (windows within the main application
|
||||
* window).
|
||||
*
|
||||
* Note: If the core windows are not opened here, they will be
|
||||
* opened via the DirectoryTreeTopComponent 'propertyChange()'
|
||||
* method on a DATA_SOURCE_ADDED event.
|
||||
*/
|
||||
if (newCurrentCase.hasData()) {
|
||||
CoreComponentControl.openCoreWindows();
|
||||
@@ -1351,11 +1356,11 @@ public class Case {
|
||||
public String getReportDirectory() {
|
||||
return getOrCreateSubdirectory(REPORTS_FOLDER);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the full path to the config directory for this case, creating it if
|
||||
* it does not exist.
|
||||
*
|
||||
*
|
||||
* @return The config directory path.
|
||||
*/
|
||||
public String getConfigDirectory() {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
OptionsCategory_Name_TagNamesOptions=Tags
|
||||
OptionsCategory_TagNames=TagNames
|
||||
Blackboard.unableToIndexArtifact.error.msg=Unable to index blackboard artifact {0}
|
||||
TagNameDialog.title.text=New Tag
|
||||
TagNameDialog.JOptionPane.tagNameIllegalCharacters.message=Tag name may not contain any of the following symbols\: \\ \: * ? " < > | , ;
|
||||
TagNameDialog.JOptionPane.tagNameIllegalCharacters.title=Invalid character in tag name
|
||||
|
||||
@@ -59,7 +59,7 @@ abstract class AbstractSqlEamDb implements EamDb {
|
||||
private final Map<String, Collection<CorrelationAttribute>> bulkArtifacts;
|
||||
|
||||
// Maximum length for the value column in the instance tables
|
||||
static final int MAX_VALUE_LENGTH = 128;
|
||||
static final int MAX_VALUE_LENGTH = 256;
|
||||
|
||||
// number of instances to keep in bulk queue before doing an insert.
|
||||
// Update Test code if this changes. It's hard coded there.
|
||||
|
||||
@@ -88,7 +88,21 @@ final public class CoreComponentControl {
|
||||
TopComponent directoryTree = null;
|
||||
TopComponent favorites = null;
|
||||
final WindowManager windowManager = WindowManager.getDefault();
|
||||
|
||||
// Set the UI selections to null before closing the top components.
|
||||
// Otherwise it may experience errors trying to load data for the closed case.
|
||||
for (Mode mode : windowManager.getModes()) {
|
||||
for (TopComponent tc : windowManager.getOpenedTopComponents(mode)) {
|
||||
if(tc instanceof DataContent) {
|
||||
((DataContent) tc).setNode(null);
|
||||
} else if(tc instanceof DataResult) {
|
||||
((DataResult) tc).setNode(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Mode mode : windowManager.getModes()) {
|
||||
|
||||
for (TopComponent tc : windowManager.getOpenedTopComponents(mode)) {
|
||||
String tcName = tc.getName();
|
||||
|
||||
@@ -105,7 +119,7 @@ final public class CoreComponentControl {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (directoryTree != null) {
|
||||
directoryTree.close();
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ public final class FileTypes implements AutopsyVisitableItem {
|
||||
if (typesRoot.showCounts) {
|
||||
//only show "(counting...)" the first time, otherwise it is distracting.
|
||||
setDisplayName(getDisplayNameBase() + ((childCount < 0) ? Bundle.FileTypes_bgCounting_placeholder()
|
||||
: ("(" + childCount + ")"))); //NON-NLS
|
||||
: (" (" + childCount + ")"))); //NON-NLS
|
||||
new SwingWorker<Long, Void>() {
|
||||
@Override
|
||||
protected Long doInBackground() throws Exception {
|
||||
|
||||
@@ -135,7 +135,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
this.forwardList = new LinkedList<>();
|
||||
backButton.setEnabled(false);
|
||||
forwardButton.setEnabled(false);
|
||||
|
||||
|
||||
groupByDatasourceCheckBox.setSelected(UserPreferences.groupItemsInTreeByDatasource());
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE, Case.Events.DATA_SOURCE_ADDED), this);
|
||||
this.em.addPropertyChangeListener(this);
|
||||
IngestManager.getInstance().addIngestJobEventListener(this);
|
||||
@@ -379,10 +379,11 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
public int getPersistenceType() {
|
||||
return TopComponent.PERSISTENCE_NEVER;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ask the user if they want to group by data source when opening a large case.
|
||||
*
|
||||
* Ask the user if they want to group by data source when opening a large
|
||||
* case.
|
||||
*
|
||||
* @param currentCase
|
||||
* @param dataSourceCount
|
||||
*/
|
||||
@@ -410,7 +411,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
|
||||
// Save the response
|
||||
Properties props = new Properties();
|
||||
if(dialog.groupByDataSourceSelected()) {
|
||||
if (dialog.groupByDataSourceSelected()) {
|
||||
props.setProperty("groupByDataSource", "true");
|
||||
} else {
|
||||
props.setProperty("groupByDataSource", "false");
|
||||
@@ -423,7 +424,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called only when top component was closed on all workspaces before and
|
||||
* now is opened for the first time on some workspace. The intent is to
|
||||
@@ -432,8 +433,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
* here.
|
||||
*/
|
||||
@NbBundle.Messages({"# {0} - dataSourceCount",
|
||||
"DirectoryTreeTopComponent.componentOpened.groupDataSources.text=This case contains {0} data sources. Would you like to group by data source for faster loading?",
|
||||
"DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source?"})
|
||||
"DirectoryTreeTopComponent.componentOpened.groupDataSources.text=This case contains {0} data sources. Would you like to group by data source for faster loading?",
|
||||
"DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source?"})
|
||||
@Override
|
||||
public void componentOpened() {
|
||||
// change the cursor to "waiting cursor" for this operation
|
||||
@@ -462,21 +463,21 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
} else {
|
||||
ModuleSettings.setConfigSetting(ModuleSettings.MAIN_SETTINGS, GROUPING_THRESHOLD_NAME, String.valueOf(threshold));
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
int dataSourceCount = currentCase.getDataSources().size();
|
||||
if (! UserPreferences.groupItemsInTreeByDatasource() &&
|
||||
dataSourceCount > threshold) {
|
||||
if (!UserPreferences.groupItemsInTreeByDatasource()
|
||||
&& dataSourceCount > threshold) {
|
||||
promptForDataSourceGrouping(currentCase, dataSourceCount);
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error loading data sources", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if there's at least one image, load the image and open the top componen
|
||||
autopsyTreeChildrenFactory = new AutopsyTreeChildrenFactory();
|
||||
autopsyTreeChildren = Children.create(autopsyTreeChildrenFactory, true);
|
||||
autopsyTreeChildren = Children.create(autopsyTreeChildrenFactory, true);
|
||||
Node root = new AbstractNode(autopsyTreeChildren) {
|
||||
//JIRA-2807: What is the point of these overrides?
|
||||
/**
|
||||
@@ -535,9 +536,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
tree.collapseNode(views);
|
||||
}
|
||||
/*
|
||||
* JIRA-2806: What is this supposed to do? Right now it selects
|
||||
* the data sources node, but the comment seems to indicate
|
||||
* it is supposed to select the first datasource.
|
||||
* JIRA-2806: What is this supposed to do? Right now it
|
||||
* selects the data sources node, but the comment seems to
|
||||
* indicate it is supposed to select the first datasource.
|
||||
*/
|
||||
// select the first image node, if there is one
|
||||
// (this has to happen after dataResult is opened, because the event
|
||||
@@ -566,7 +567,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
// dataResult active)
|
||||
try {
|
||||
Node[] selections = get();
|
||||
if (selections != null && selections.length > 0){
|
||||
if (selections != null && selections.length > 0) {
|
||||
em.setSelectedNodes(selections);
|
||||
}
|
||||
} catch (PropertyVetoException ex) {
|
||||
@@ -674,15 +675,15 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
}
|
||||
|
||||
/**
|
||||
* The "listener" that listens to any changes made in the Case.java class.
|
||||
* It will do something based on the changes in the Case.java class.
|
||||
* The "listener" that monitors changes made in the Case class. This serves
|
||||
* the purpose of keeping the UI in sync with the data as it changes.
|
||||
*
|
||||
* @param evt the property change event
|
||||
* @param event The property change event.
|
||||
*/
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
public void propertyChange(PropertyChangeEvent event) {
|
||||
if (RuntimeProperties.runningWithGUI()) {
|
||||
String changed = evt.getPropertyName();
|
||||
String changed = event.getPropertyName();
|
||||
if (changed.equals(Case.Events.CURRENT_CASE.toString())) { // changed current case
|
||||
// When a case is closed, the old value of this property is the
|
||||
// closed Case object and the new value is null. When a case is
|
||||
@@ -692,15 +693,15 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
// opened events instead of property change events would be a better
|
||||
// solution. Either way, more probably needs to be done to clean up
|
||||
// data model objects when a case is closed.
|
||||
if (evt.getOldValue() != null && evt.getNewValue() == null) {
|
||||
if (event.getOldValue() != null && event.getNewValue() == null) {
|
||||
// The current case has been closed. Reset the ExplorerManager.
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
Node emptyNode = new AbstractNode(Children.LEAF);
|
||||
em.setRootContext(emptyNode);
|
||||
});
|
||||
} else if (evt.getNewValue() != null) {
|
||||
} else if (event.getNewValue() != null) {
|
||||
// A new case has been opened. Reset the ExplorerManager.
|
||||
Case newCase = (Case) evt.getNewValue();
|
||||
Case newCase = (Case) event.getNewValue();
|
||||
final String newCaseName = newCase.getName();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
em.getRootContext().setName(newCaseName);
|
||||
@@ -724,20 +725,27 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
* already closed.
|
||||
*/
|
||||
try {
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
// We only need to trigger openCoreWindows() when the
|
||||
// first data source is added.
|
||||
if (currentCase.getDataSources().size() == 1) {
|
||||
Case.getCurrentCaseThrows();
|
||||
/*
|
||||
* In case the Case 'updateGUIForCaseOpened()' method hasn't
|
||||
* already done so, open the tree and all other core
|
||||
* windows.
|
||||
*
|
||||
* TODO: (JIRA-4053) DirectoryTreeTopComponent should not be
|
||||
* responsible for opening core windows. Consider moving
|
||||
* this elsewhere.
|
||||
*/
|
||||
if (!this.isOpened()) {
|
||||
SwingUtilities.invokeLater(CoreComponentControl::openCoreWindows);
|
||||
}
|
||||
} catch (NoCurrentCaseException | TskCoreException notUsed) {
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
/**
|
||||
* Case is closed, do nothing.
|
||||
*/
|
||||
}
|
||||
} // change in node selection
|
||||
else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||
respondSelection((Node[]) evt.getOldValue(), (Node[]) evt.getNewValue());
|
||||
respondSelection((Node[]) event.getOldValue(), (Node[]) event.getNewValue());
|
||||
} else if (changed.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
|
||||
// nothing to do here.
|
||||
// all nodes should be listening for these events and update accordingly.
|
||||
@@ -882,7 +890,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
|
||||
/**
|
||||
* Rebuilds the autopsy tree.
|
||||
*
|
||||
*
|
||||
* Does nothing if there is no open case.
|
||||
*/
|
||||
private void rebuildTree() {
|
||||
@@ -895,9 +903,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
return;
|
||||
}
|
||||
if (null == currentCase || currentCase.hasData() == false) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// refresh all children of the root.
|
||||
autopsyTreeChildrenFactory.refreshChildren();
|
||||
|
||||
@@ -924,14 +932,14 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Selects the first node in the tree.
|
||||
*
|
||||
*
|
||||
*/
|
||||
private void selectFirstChildNode () {
|
||||
private void selectFirstChildNode() {
|
||||
Children rootChildren = em.getRootContext().getChildren();
|
||||
|
||||
|
||||
if (rootChildren.getNodesCount() > 0) {
|
||||
Node firstNode = rootChildren.getNodeAt(0);
|
||||
if (firstNode != null) {
|
||||
@@ -940,6 +948,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selected node using a path to a previously selected node.
|
||||
*
|
||||
|
||||
@@ -50,9 +50,18 @@ public class DataSourcePanel extends javax.swing.JPanel {
|
||||
*/
|
||||
public DataSourcePanel() {
|
||||
initComponents();
|
||||
this.dataSourceList.addListSelectionListener((ListSelectionEvent evt) -> {
|
||||
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
});
|
||||
if (this.dataSourceList.getModel().getSize() > 1) {
|
||||
this.dataSourceList.addListSelectionListener((ListSelectionEvent evt) -> {
|
||||
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
});
|
||||
} else {
|
||||
/*
|
||||
* Disable data source filtering since there aren't multiple data
|
||||
* sources to choose from.
|
||||
*/
|
||||
this.dataSourceCheckBox.setEnabled(false);
|
||||
this.dataSourceList.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +93,8 @@ public class DataSourcePanel extends javax.swing.JPanel {
|
||||
|
||||
/**
|
||||
* Get a set of data source object ids that are selected.
|
||||
* @return A set of selected object ids.
|
||||
*
|
||||
* @return A set of selected object ids.
|
||||
*/
|
||||
Set<Long> getDataSourcesSelected() {
|
||||
Set<Long> dataSourceObjIdSet = new HashSet<>();
|
||||
@@ -101,6 +111,7 @@ public class DataSourcePanel extends javax.swing.JPanel {
|
||||
|
||||
/**
|
||||
* Is dataSourceCheckBox selected
|
||||
*
|
||||
* @return true if the dataSoureCheckBox is selected
|
||||
*/
|
||||
boolean isSelected() {
|
||||
@@ -108,7 +119,8 @@ public class DataSourcePanel extends javax.swing.JPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the dsList and dataSourceNoteLable if the dataSourceCheckBox is checked.
|
||||
* Enable the dsList and dataSourceNoteLable if the dataSourceCheckBox is
|
||||
* checked.
|
||||
*/
|
||||
final void setComponentsEnabled() {
|
||||
boolean enabled = this.isSelected();
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import static org.sleuthkit.autopsy.casemodule.services.TagsManager.getNotableTagLabel;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@@ -53,6 +54,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute.Type;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
@@ -538,6 +540,65 @@ class TableReportGenerator {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case: ", ex); //NON-NLS
|
||||
return;
|
||||
}
|
||||
|
||||
// Get a list of all selected tag IDs
|
||||
String tagIDList = "";
|
||||
if( ! tagNamesFilter.isEmpty()) {
|
||||
try {
|
||||
Map<String, TagName> tagNamesMap = Case.getCurrentCaseThrows().getServices().getTagsManager().getDisplayNamesToTagNamesMap();
|
||||
for(String tagDisplayName : tagNamesFilter) {
|
||||
if(tagNamesMap.containsKey(tagDisplayName)) {
|
||||
if (! tagIDList.isEmpty()) {
|
||||
tagIDList += ",";
|
||||
}
|
||||
tagIDList += tagNamesMap.get(tagDisplayName).getId();
|
||||
} else {
|
||||
// If the tag name ends with "(Notable)", try stripping that off
|
||||
if(tagDisplayName.endsWith(getNotableTagLabel())) {
|
||||
String editedDisplayName = tagDisplayName.substring(0, tagDisplayName.length() - getNotableTagLabel().length());
|
||||
if(tagNamesMap.containsKey(editedDisplayName)) {
|
||||
if (! tagIDList.isEmpty()) {
|
||||
tagIDList += ",";
|
||||
}
|
||||
tagIDList += tagNamesMap.get(editedDisplayName).getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting tag info - proceeding without tag filter: ", ex); //NON-NLS
|
||||
tagIDList = "";
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there are any ad-hoc results
|
||||
String adHocCountQuery = "SELECT COUNT(*) FROM " + //NON-NLS
|
||||
"(SELECT art.artifact_id FROM blackboard_artifacts AS art, blackboard_attributes AS att1 ";//NON-NLS
|
||||
if (!tagIDList.isEmpty()) {
|
||||
adHocCountQuery += ", blackboard_artifact_tags as tag "; //NON-NLS
|
||||
}
|
||||
adHocCountQuery += "WHERE (att1.artifact_id = art.artifact_id) AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") "; // NON-NLS
|
||||
if (!tagIDList.isEmpty()) {
|
||||
adHocCountQuery += " AND (art.artifact_id = tag.artifact_id) AND (tag.tag_name_id IN (" + tagIDList + ")) "; //NON-NLS
|
||||
}
|
||||
adHocCountQuery += "EXCEPT " + // NON-NLS
|
||||
"SELECT art.artifact_id FROM blackboard_artifacts AS art, blackboard_attributes AS att1 WHERE (att1.artifact_id = art.artifact_id) AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") AND (att1.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + ")) "; //NON-NLS
|
||||
|
||||
int adHocCount = 0;
|
||||
try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(adHocCountQuery)) {
|
||||
ResultSet adHocCountResultSet = dbQuery.getResultSet();
|
||||
if (adHocCountResultSet.next()) {
|
||||
adHocCount = adHocCountResultSet.getInt(1); //NON-NLS
|
||||
} else {
|
||||
throw new TskCoreException("Error counting ad hoc keywords");
|
||||
}
|
||||
} catch (TskCoreException | SQLException ex) {
|
||||
errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedQueryKWLists"));
|
||||
logger.log(Level.SEVERE, "Failed to count ad hoc searches with query " + adHocCountQuery, ex); //NON-NLS
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the query to get the keyword list names
|
||||
if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) {
|
||||
orderByClause = "ORDER BY convert_to(att.value_text, 'SQL_ASCII') ASC NULLS FIRST"; //NON-NLS
|
||||
} else {
|
||||
@@ -546,16 +607,25 @@ class TableReportGenerator {
|
||||
String keywordListQuery
|
||||
= "SELECT att.value_text AS list "
|
||||
+ //NON-NLS
|
||||
"FROM blackboard_attributes AS att, blackboard_artifacts AS art "
|
||||
+ //NON-NLS
|
||||
"WHERE att.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " "
|
||||
"FROM blackboard_attributes AS att, blackboard_artifacts AS art "; // NON-NLS
|
||||
if(! tagIDList.isEmpty()) {
|
||||
keywordListQuery += ", blackboard_artifact_tags as tag "; //NON-NLS
|
||||
}
|
||||
keywordListQuery += "WHERE att.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + " "
|
||||
+ //NON-NLS
|
||||
"AND art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + " "
|
||||
+ //NON-NLS
|
||||
"AND att.artifact_id = art.artifact_id "
|
||||
+ //NON-NLS
|
||||
"GROUP BY list " + orderByClause; //NON-NLS
|
||||
"AND att.artifact_id = art.artifact_id ";
|
||||
if (! tagIDList.isEmpty()) {
|
||||
keywordListQuery += "AND (art.artifact_id = tag.artifact_id) " + //NON-NLS
|
||||
"AND (tag.tag_name_id IN (" + tagIDList + ")) "; //NON-NLS
|
||||
}
|
||||
if (adHocCount > 0) {
|
||||
keywordListQuery += " UNION SELECT \"\" AS list ";
|
||||
}
|
||||
keywordListQuery += "GROUP BY list " + orderByClause; //NON-NLS
|
||||
|
||||
// Make the table of contents links for each list type
|
||||
try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(keywordListQuery)) {
|
||||
ResultSet listsRs = dbQuery.getResultSet();
|
||||
List<String> lists = new ArrayList<>();
|
||||
@@ -579,6 +649,7 @@ class TableReportGenerator {
|
||||
return;
|
||||
}
|
||||
|
||||
// Query for keywords, grouped by list
|
||||
if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) {
|
||||
orderByClause = "ORDER BY convert_to(att3.value_text, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
|
||||
+ "convert_to(att1.value_text, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
|
||||
@@ -588,9 +659,10 @@ class TableReportGenerator {
|
||||
} else {
|
||||
orderByClause = "ORDER BY list ASC, keyword ASC, parent_path ASC, name ASC, preview ASC"; //NON-NLS
|
||||
}
|
||||
// Query for keywords, grouped by list
|
||||
String keywordsQuery
|
||||
= "SELECT art.artifact_id, art.obj_id, att1.value_text AS keyword, att2.value_text AS preview, att3.value_text AS list, f.name AS name, f.parent_path AS parent_path "
|
||||
|
||||
// Query for keywords that are part of a list
|
||||
String keywordListsQuery
|
||||
= "SELECT art.artifact_id AS artifact_id, art.obj_id AS obj_id, att1.value_text AS keyword, att2.value_text AS preview, att3.value_text AS list, f.name AS name, f.parent_path AS parent_path "
|
||||
+ //NON-NLS
|
||||
"FROM blackboard_artifacts AS art, blackboard_attributes AS att1, blackboard_attributes AS att2, blackboard_attributes AS att3, tsk_files AS f "
|
||||
+ //NON-NLS
|
||||
@@ -608,9 +680,24 @@ class TableReportGenerator {
|
||||
+ //NON-NLS
|
||||
"AND (att3.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + ") "
|
||||
+ //NON-NLS
|
||||
"AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") "
|
||||
+ //NON-NLS
|
||||
orderByClause; //NON-NLS
|
||||
"AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") ";
|
||||
|
||||
// Query for keywords that are not part of a list
|
||||
String keywordAdHocQuery =
|
||||
"SELECT art.artifact_id AS artifact_id, art.obj_id AS obj_id, att1.value_text AS keyword, att2.value_text AS preview, \"\" AS list, f.name AS name, f.parent_path AS parent_path " + // NON-NLS
|
||||
"FROM blackboard_artifacts AS art, blackboard_attributes AS att1, blackboard_attributes AS att2, tsk_files AS f " + // NON-NLS
|
||||
"WHERE " + // NON-NLS
|
||||
" (art.artifact_id IN (SELECT art.artifact_id FROM blackboard_artifacts AS art, blackboard_attributes AS att1 WHERE (att1.artifact_id = art.artifact_id) AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") " + // NON-NLS
|
||||
"EXCEPT " + // NON-NLS
|
||||
"SELECT art.artifact_id FROM blackboard_artifacts AS art, blackboard_attributes AS att1 WHERE (att1.artifact_id = art.artifact_id) AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") AND (att1.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + "))) " + //NON-NLS
|
||||
"AND (att1.artifact_id = art.artifact_id) " + //NON-NLS
|
||||
"AND (att2.artifact_id = art.artifact_id) " + //NON-NLS
|
||||
"AND (f.obj_id = art.obj_id) " + //NON-NLS
|
||||
"AND (att1.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID() + ") " + // NON-NLS
|
||||
"AND (att2.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW.getTypeID() + ") " + // NON-NLS
|
||||
"AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") "; // NON-NLS
|
||||
|
||||
String keywordsQuery = keywordListsQuery + " UNION " + keywordAdHocQuery + orderByClause;
|
||||
|
||||
try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(keywordsQuery)) {
|
||||
ResultSet resultSet = dbQuery.getResultSet();
|
||||
@@ -1623,14 +1710,15 @@ class TableReportGenerator {
|
||||
private HashSet<String> getUniqueTagNames(long artifactId) throws TskCoreException {
|
||||
HashSet<String> uniqueTagNames = new HashSet<>();
|
||||
|
||||
String query = "SELECT display_name, artifact_id FROM tag_names AS tn, blackboard_artifact_tags AS bat "
|
||||
String query = "SELECT display_name, artifact_id, knownStatus FROM tag_names AS tn, blackboard_artifact_tags AS bat "
|
||||
+ //NON-NLS
|
||||
"WHERE tn.tag_name_id = bat.tag_name_id AND bat.artifact_id = " + artifactId; //NON-NLS
|
||||
|
||||
try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query)) {
|
||||
ResultSet tagNameRows = dbQuery.getResultSet();
|
||||
while (tagNameRows.next()) {
|
||||
uniqueTagNames.add(tagNameRows.getString("display_name")); //NON-NLS
|
||||
String notableString = tagNameRows.getInt("knownStatus") == TskData.FileKnown.BAD.ordinal() ? getNotableTagLabel() : "";
|
||||
uniqueTagNames.add(tagNameRows.getString("display_name") + notableString); //NON-NLS
|
||||
}
|
||||
} catch (TskCoreException | SQLException | NoCurrentCaseException ex) {
|
||||
throw new TskCoreException("Error getting tag names for artifact: ", ex);
|
||||
|
||||
Reference in New Issue
Block a user