mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 18:17:43 +00:00
Merge pull request #7813 from gdicristofaro/7072ntl2
7072 new_table_load score changes
This commit is contained in:
commit
73366ec442
@ -106,7 +106,7 @@ import org.sleuthkit.autopsy.mainui.datamodel.DeletedContentSearchParams;
|
|||||||
import org.sleuthkit.autopsy.mainui.datamodel.ReportsDAO.ReportsFetcher;
|
import org.sleuthkit.autopsy.mainui.datamodel.ReportsDAO.ReportsFetcher;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.ReportsSearchParams;
|
import org.sleuthkit.autopsy.mainui.datamodel.ReportsSearchParams;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.ScoreViewSearchParams;
|
import org.sleuthkit.autopsy.mainui.datamodel.ScoreViewSearchParams;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.ViewsDAO.ScoreFileFetcher;
|
import org.sleuthkit.autopsy.mainui.datamodel.ScoreDAO.ScoreContentFetcher;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.events.CacheClearEvent;
|
import org.sleuthkit.autopsy.mainui.datamodel.events.CacheClearEvent;
|
||||||
import org.sleuthkit.autopsy.mainui.nodes.ChildNodeSelectionInfo;
|
import org.sleuthkit.autopsy.mainui.nodes.ChildNodeSelectionInfo;
|
||||||
import org.sleuthkit.autopsy.mainui.nodes.SearchManager;
|
import org.sleuthkit.autopsy.mainui.nodes.SearchManager;
|
||||||
@ -1373,7 +1373,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
|||||||
*/
|
*/
|
||||||
void displayScoreContent(ScoreViewSearchParams scoreSearchParams) {
|
void displayScoreContent(ScoreViewSearchParams scoreSearchParams) {
|
||||||
try {
|
try {
|
||||||
this.searchResultManager = new SearchManager(new ScoreFileFetcher(scoreSearchParams), getPageSize());
|
this.searchResultManager = new SearchManager(new ScoreContentFetcher(scoreSearchParams), getPageSize());
|
||||||
SearchResultsDTO results = searchResultManager.getResults();
|
SearchResultsDTO results = searchResultManager.getResults();
|
||||||
displaySearchResults(results, true);
|
displaySearchResults(results, true);
|
||||||
} catch (ExecutionException ex) {
|
} catch (ExecutionException ex) {
|
||||||
|
@ -150,15 +150,16 @@ abstract class BlackboardArtifactDAO extends AbstractDAO {
|
|||||||
return IGNORED_TYPES;
|
return IGNORED_TYPES;
|
||||||
}
|
}
|
||||||
|
|
||||||
TableData createTableData(BlackboardArtifact.Type artType, List<BlackboardArtifact> arts) throws TskCoreException, NoCurrentCaseException {
|
TableData createTableData(BlackboardArtifact.Type artType, List<? extends BlackboardArtifact> arts) throws TskCoreException, NoCurrentCaseException {
|
||||||
// A linked hashmap is being used for artifactAttributes to ensure that artifact order
|
// A linked hashmap is being used for artifactAttributes to ensure that artifact order
|
||||||
// as well as attribute orders within those artifacts are preserved. This is to maintain
|
// as well as attribute orders within those artifacts are preserved. This is to maintain
|
||||||
// a consistent ordering of attribute columns as received from BlackboardArtifact.getAttributes
|
// a consistent ordering of attribute columns as received from BlackboardArtifact.getAttributes
|
||||||
Map<Long, Map<BlackboardAttribute.Type, Object>> artifactAttributes = new LinkedHashMap<>();
|
Map<Long, Map<BlackboardAttribute.Type, Object>> artifactAttributes = new LinkedHashMap<>();
|
||||||
for (BlackboardArtifact art : arts) {
|
for (BlackboardArtifact art : arts) {
|
||||||
|
BlackboardArtifact.Type thisArtType = artType != null ? artType : art.getType();
|
||||||
Map<BlackboardAttribute.Type, Object> attrs = art.getAttributes().stream()
|
Map<BlackboardAttribute.Type, Object> attrs = art.getAttributes().stream()
|
||||||
.filter(attr -> isRenderedAttr(artType, attr.getAttributeType()))
|
.filter(attr -> isRenderedAttr(thisArtType, attr.getAttributeType()))
|
||||||
.collect(Collectors.toMap(attr -> attr.getAttributeType(), attr -> getAttrValue(artType, attr), (attr1, attr2) -> attr1, LinkedHashMap::new));
|
.collect(Collectors.toMap(attr -> attr.getAttributeType(), attr -> getAttrValue(thisArtType, attr), (attr1, attr2) -> attr1, LinkedHashMap::new));
|
||||||
|
|
||||||
artifactAttributes.put(art.getId(), attrs);
|
artifactAttributes.put(art.getId(), attrs);
|
||||||
}
|
}
|
||||||
@ -205,7 +206,8 @@ abstract class BlackboardArtifactDAO extends AbstractDAO {
|
|||||||
cellValues.add(dataSourceName);
|
cellValues.add(dataSourceName);
|
||||||
|
|
||||||
AbstractFile linkedFile = null;
|
AbstractFile linkedFile = null;
|
||||||
if (artType.getCategory().equals(BlackboardArtifact.Category.DATA_ARTIFACT)) {
|
BlackboardArtifact.Type thisArtType = artType != null ? artType : artifact.getType();
|
||||||
|
if (thisArtType.getCategory().equals(BlackboardArtifact.Category.DATA_ARTIFACT)) {
|
||||||
// Note that we need to get the attribute from the original artifact since it is not displayed.
|
// Note that we need to get the attribute from the original artifact since it is not displayed.
|
||||||
if (artifact.getAttribute(BlackboardAttribute.Type.TSK_PATH_ID) != null) {
|
if (artifact.getAttribute(BlackboardAttribute.Type.TSK_PATH_ID) != null) {
|
||||||
long linkedId = artifact.getAttribute(BlackboardAttribute.Type.TSK_PATH_ID).getValueLong();
|
long linkedId = artifact.getAttribute(BlackboardAttribute.Type.TSK_PATH_ID).getValueLong();
|
||||||
@ -469,7 +471,7 @@ abstract class BlackboardArtifactDAO extends AbstractDAO {
|
|||||||
return pagedArtsStream.collect(Collectors.toList());
|
return pagedArtsStream.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
class TableData {
|
static class TableData {
|
||||||
|
|
||||||
final List<ColumnKey> columnKeys;
|
final List<ColumnKey> columnKeys;
|
||||||
final List<RowDTO> rows;
|
final List<RowDTO> rows;
|
||||||
|
@ -105,6 +105,13 @@ ReportsRowDTO_reportFilePath_displayName=Report File Path
|
|||||||
ReportsRowDTO_reportName_displayName=Report Name
|
ReportsRowDTO_reportName_displayName=Report Name
|
||||||
ReportsRowDTO_sourceModuleName_displayName=Source Module Name
|
ReportsRowDTO_sourceModuleName_displayName=Source Module Name
|
||||||
ResultTag.name.text=Result Tag
|
ResultTag.name.text=Result Tag
|
||||||
|
ScoreDAO_columns_createdDateLbl=Created Date
|
||||||
|
ScoreDAO_columns_noDescription=No Description
|
||||||
|
ScoreDAO_columns_pathLbl=Path
|
||||||
|
ScoreDAO_columns_sourceLbl=Source
|
||||||
|
ScoreDAO_columns_typeLbl=Type
|
||||||
|
ScoreDAO_mainNode_displayName=Score
|
||||||
|
ScoreDAO_types_filelbl=File
|
||||||
ScoreViewFilter_bad_name=Bad Items
|
ScoreViewFilter_bad_name=Bad Items
|
||||||
ScoreViewFilter_suspicious_name=Suspicious Items
|
ScoreViewFilter_suspicious_name=Suspicious Items
|
||||||
TagsDAO.fileColumns.accessTimeColLbl=Accessed Time
|
TagsDAO.fileColumns.accessTimeColLbl=Accessed Time
|
||||||
|
@ -173,6 +173,7 @@ public class MainDAO extends AbstractDAO {
|
|||||||
private final EmailsDAO emailsDAO = EmailsDAO.getInstance();
|
private final EmailsDAO emailsDAO = EmailsDAO.getInstance();
|
||||||
private final HostPersonDAO hostPersonDAO = HostPersonDAO.getInstance();
|
private final HostPersonDAO hostPersonDAO = HostPersonDAO.getInstance();
|
||||||
private final ReportsDAO reportsDAO = ReportsDAO.getInstance();
|
private final ReportsDAO reportsDAO = ReportsDAO.getInstance();
|
||||||
|
private final ScoreDAO scoreDAO = ScoreDAO.getInstance();
|
||||||
|
|
||||||
// NOTE: whenever adding a new sub-dao, it should be added to this list for event updates.
|
// NOTE: whenever adding a new sub-dao, it should be added to this list for event updates.
|
||||||
private final List<AbstractDAO> allDAOs = ImmutableList.of(dataArtifactDAO,
|
private final List<AbstractDAO> allDAOs = ImmutableList.of(dataArtifactDAO,
|
||||||
@ -185,7 +186,8 @@ public class MainDAO extends AbstractDAO {
|
|||||||
creditCardDAO,
|
creditCardDAO,
|
||||||
emailsDAO,
|
emailsDAO,
|
||||||
hostPersonDAO,
|
hostPersonDAO,
|
||||||
reportsDAO);
|
reportsDAO,
|
||||||
|
scoreDAO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers listeners with autopsy event publishers and starts internal
|
* Registers listeners with autopsy event publishers and starts internal
|
||||||
@ -265,6 +267,10 @@ public class MainDAO extends AbstractDAO {
|
|||||||
return reportsDAO;
|
return reportsDAO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ScoreDAO getScoreDAO() {
|
||||||
|
return scoreDAO;
|
||||||
|
}
|
||||||
|
|
||||||
public PropertyChangeManager getResultEventsManager() {
|
public PropertyChangeManager getResultEventsManager() {
|
||||||
return this.resultEventsManager;
|
return this.resultEventsManager;
|
||||||
}
|
}
|
||||||
@ -318,8 +324,7 @@ public class MainDAO extends AbstractDAO {
|
|||||||
*
|
*
|
||||||
* @param evt The event.
|
* @param evt The event.
|
||||||
* @param immediateResultAction If true, result events are immediately
|
* @param immediateResultAction If true, result events are immediately
|
||||||
* fired. Otherwise, the result events are
|
* fired. Otherwise, the result events are batched.
|
||||||
* batched.
|
|
||||||
*/
|
*/
|
||||||
private void handleEvent(PropertyChangeEvent evt, boolean immediateResultAction) {
|
private void handleEvent(PropertyChangeEvent evt, boolean immediateResultAction) {
|
||||||
Collection<DAOEvent> daoEvts = processEvent(evt);
|
Collection<DAOEvent> daoEvts = processEvent(evt);
|
||||||
|
632
Core/src/org/sleuthkit/autopsy/mainui/datamodel/ScoreDAO.java
Normal file
632
Core/src/org/sleuthkit/autopsy/mainui/datamodel/ScoreDAO.java
Normal file
@ -0,0 +1,632 @@
|
|||||||
|
/*
|
||||||
|
* 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.mainui.datamodel;
|
||||||
|
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.events.DAOEvent;
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
|
||||||
|
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||||
|
import static org.sleuthkit.autopsy.mainui.datamodel.AbstractDAO.CACHE_DURATION;
|
||||||
|
import static org.sleuthkit.autopsy.mainui.datamodel.AbstractDAO.CACHE_DURATION_UNITS;
|
||||||
|
import static org.sleuthkit.autopsy.mainui.datamodel.AbstractDAO.CACHE_SIZE;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.BlackboardArtifactDAO.TableData;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.TreeResultsDTO.TreeDisplayCount;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.TreeResultsDTO.TreeItemDTO;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.events.DAOEventUtils;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.events.ScoreContentEvent;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.events.TreeCounts;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.events.TreeEvent;
|
||||||
|
import org.sleuthkit.autopsy.mainui.nodes.DAOFetcher;
|
||||||
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact.Category;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.DataArtifact;
|
||||||
|
import org.sleuthkit.datamodel.Score;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides information to populate the results viewer for data in the views
|
||||||
|
* section.
|
||||||
|
*/
|
||||||
|
@Messages({
|
||||||
|
"ScoreDAO_columns_sourceLbl=Source",
|
||||||
|
"ScoreDAO_columns_typeLbl=Type",
|
||||||
|
"ScoreDAO_columns_pathLbl=Path",
|
||||||
|
"ScoreDAO_columns_createdDateLbl=Created Date",
|
||||||
|
"ScoreDAO_columns_noDescription=No Description",
|
||||||
|
"ScoreDAO_types_filelbl=File",
|
||||||
|
"ScoreDAO_mainNode_displayName=Score"
|
||||||
|
})
|
||||||
|
public class ScoreDAO extends AbstractDAO {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(ScoreDAO.class.getName());
|
||||||
|
|
||||||
|
private static final List<BlackboardAttribute.Type> TIME_ATTRS = Arrays.asList(
|
||||||
|
BlackboardAttribute.Type.TSK_DATETIME,
|
||||||
|
BlackboardAttribute.Type.TSK_DATETIME_ACCESSED,
|
||||||
|
BlackboardAttribute.Type.TSK_DATETIME_RCVD,
|
||||||
|
BlackboardAttribute.Type.TSK_DATETIME_SENT,
|
||||||
|
BlackboardAttribute.Type.TSK_DATETIME_CREATED,
|
||||||
|
BlackboardAttribute.Type.TSK_DATETIME_MODIFIED,
|
||||||
|
BlackboardAttribute.Type.TSK_DATETIME_START,
|
||||||
|
BlackboardAttribute.Type.TSK_DATETIME_END,
|
||||||
|
BlackboardAttribute.Type.TSK_DATETIME_DELETED,
|
||||||
|
BlackboardAttribute.Type.TSK_DATETIME_PASSWORD_RESET,
|
||||||
|
BlackboardAttribute.Type.TSK_DATETIME_PASSWORD_FAIL
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final Map<Integer, Integer> TIME_ATTR_IMPORTANCE = IntStream.range(0, TIME_ATTRS.size())
|
||||||
|
.mapToObj(idx -> Pair.of(TIME_ATTRS.get(idx).getTypeID(), idx))
|
||||||
|
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (v1, v2) -> v1));
|
||||||
|
|
||||||
|
private static final List<ColumnKey> RESULT_SCORE_COLUMNS = Arrays.asList(
|
||||||
|
getFileColumnKey(Bundle.ScoreDAO_columns_sourceLbl()),
|
||||||
|
getFileColumnKey(Bundle.ScoreDAO_columns_typeLbl()),
|
||||||
|
getFileColumnKey(Bundle.ScoreDAO_columns_pathLbl()),
|
||||||
|
getFileColumnKey(Bundle.ScoreDAO_columns_createdDateLbl())
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final String SCORE_TYPE_ID = ScoreDAO.class.getName() + "_SIGNATURE_ID";
|
||||||
|
|
||||||
|
private static final String BASE_AGGR_SCORE_QUERY
|
||||||
|
= "FROM tsk_aggregate_score aggr_score\n"
|
||||||
|
+ "INNER JOIN (\n"
|
||||||
|
+ " SELECT obj_id, data_source_obj_id, 'f' AS type FROM tsk_files\n"
|
||||||
|
+ " UNION SELECT artifact_obj_id AS obj_id, data_source_obj_id, 'a' AS type FROM blackboard_artifacts\n"
|
||||||
|
+ " WHERE blackboard_artifacts.artifact_type_id IN\n"
|
||||||
|
+ " (SELECT artifact_type_id FROM blackboard_artifact_types WHERE category_type = " + Category.DATA_ARTIFACT.getID() + ")\n"
|
||||||
|
+ ") art_files ON aggr_score.obj_id = art_files.obj_id\n";
|
||||||
|
|
||||||
|
private static ScoreDAO instance = null;
|
||||||
|
|
||||||
|
synchronized static ScoreDAO getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new ScoreDAO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Cache<SearchParams<Object>, SearchResultsDTO> searchParamsCache
|
||||||
|
= CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).expireAfterAccess(CACHE_DURATION, CACHE_DURATION_UNITS).build();
|
||||||
|
private final TreeCounts<DAOEvent> treeCounts = new TreeCounts<>();
|
||||||
|
|
||||||
|
private static ColumnKey getFileColumnKey(String name) {
|
||||||
|
return new ColumnKey(name, name, Bundle.ScoreDAO_columns_noDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
private SleuthkitCase getCase() throws NoCurrentCaseException {
|
||||||
|
return Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchResultsDTO getFilesByScore(ScoreViewSearchParams key, long startItem, Long maxCount) throws ExecutionException, IllegalArgumentException {
|
||||||
|
if (key.getDataSourceId() != null && key.getDataSourceId() <= 0) {
|
||||||
|
throw new IllegalArgumentException("Data source id must be greater than 0 or null");
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchParams<Object> searchParams = new SearchParams<>(key, startItem, maxCount);
|
||||||
|
return searchParamsCache.get(searchParams, () -> fetchScoreSearchResultsDTOs(key.getFilter(), key.getDataSourceId(), startItem, maxCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isScoreContentInvalidating(ScoreViewSearchParams params, DAOEvent eventData) {
|
||||||
|
if (!(eventData instanceof ScoreContentEvent)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScoreContentEvent scoreContentEvt = (ScoreContentEvent) eventData;
|
||||||
|
|
||||||
|
ScoreViewFilter evtFilter = scoreContentEvt.getFilter();
|
||||||
|
ScoreViewFilter paramsFilter = params.getFilter();
|
||||||
|
|
||||||
|
Long evtDsId = scoreContentEvt.getDataSourceId();
|
||||||
|
Long paramsDsId = params.getDataSourceId();
|
||||||
|
|
||||||
|
return (evtFilter == null || evtFilter.equals(paramsFilter))
|
||||||
|
&& (paramsDsId == null || evtDsId == null
|
||||||
|
|| Objects.equals(paramsDsId, evtDsId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns counts for deleted content categories.
|
||||||
|
*
|
||||||
|
* @param dataSourceId The data source object id or null if no data source
|
||||||
|
* filtering should occur.
|
||||||
|
*
|
||||||
|
* @return The results.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* @throws ExecutionException
|
||||||
|
*/
|
||||||
|
public TreeResultsDTO<ScoreViewSearchParams> getScoreContentCounts(Long dataSourceId) throws IllegalArgumentException, ExecutionException {
|
||||||
|
Set<ScoreViewFilter> indeterminateFilters = new HashSet<>();
|
||||||
|
for (DAOEvent evt : this.treeCounts.getEnqueued()) {
|
||||||
|
if (evt instanceof ScoreContentEvent) {
|
||||||
|
ScoreContentEvent scoreEvt = (ScoreContentEvent) evt;
|
||||||
|
if (dataSourceId == null || scoreEvt.getDataSourceId() == null || Objects.equals(scoreEvt.getDataSourceId(), dataSourceId)) {
|
||||||
|
if (scoreEvt.getFilter() == null) {
|
||||||
|
// if null filter, indicates full refresh and all file sizes need refresh.
|
||||||
|
indeterminateFilters.addAll(Arrays.asList(ScoreViewFilter.values()));
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
indeterminateFilters.add(scoreEvt.getFilter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String dsClause = getDsFilter(dataSourceId);
|
||||||
|
|
||||||
|
String queryStrSelects = Stream.of(ScoreViewFilter.values())
|
||||||
|
.map((filter) -> Pair.of(filter.name(), getScoreFilter(filter.getScores())))
|
||||||
|
.map((filterSqlPair) -> {
|
||||||
|
String filterSql = Stream.of(filterSqlPair.getRight(), dsClause)
|
||||||
|
.filter(StringUtils::isNotBlank)
|
||||||
|
.collect(Collectors.joining("\nAND "));
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(filterSql)) {
|
||||||
|
filterSql = "\n WHERE " + filterSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "(SELECT COUNT(aggr_score.obj_id) "
|
||||||
|
+ BASE_AGGR_SCORE_QUERY
|
||||||
|
+ filterSql
|
||||||
|
+ ") AS "
|
||||||
|
+ filterSqlPair.getLeft();
|
||||||
|
})
|
||||||
|
.collect(Collectors.joining(",\n"));
|
||||||
|
|
||||||
|
String queryStr = "\n" + queryStrSelects;
|
||||||
|
|
||||||
|
try {
|
||||||
|
SleuthkitCase skCase = getCase();
|
||||||
|
|
||||||
|
List<TreeItemDTO<ScoreViewSearchParams>> treeList = new ArrayList<>();
|
||||||
|
skCase.getCaseDbAccessManager().select(queryStr, (resultSet) -> {
|
||||||
|
try {
|
||||||
|
if (resultSet.next()) {
|
||||||
|
for (ScoreViewFilter filter : ScoreViewFilter.values()) {
|
||||||
|
long count = resultSet.getLong(filter.name());
|
||||||
|
TreeDisplayCount displayCount = indeterminateFilters.contains(filter)
|
||||||
|
? TreeDisplayCount.INDETERMINATE
|
||||||
|
: TreeDisplayCount.getDeterminate(count);
|
||||||
|
|
||||||
|
treeList.add(createScoreContentTreeItem(filter, dataSourceId, displayCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
logger.log(Level.WARNING, "An error occurred while fetching file type counts.", ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return new TreeResultsDTO<>(treeList);
|
||||||
|
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||||
|
throw new ExecutionException("An error occurred while fetching file counts with query:\n" + queryStr, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TreeItemDTO<ScoreViewSearchParams> createScoreContentTreeItem(ScoreViewFilter filter, Long dataSourceId, TreeDisplayCount displayCount) {
|
||||||
|
return new TreeItemDTO<>(
|
||||||
|
"SCORE_CONTENT",
|
||||||
|
new ScoreViewSearchParams(filter, dataSourceId),
|
||||||
|
filter,
|
||||||
|
filter == null ? "" : filter.getDisplayName(),
|
||||||
|
displayCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getScoreFilter(Collection<Score> scores) {
|
||||||
|
if (CollectionUtils.isEmpty(scores)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return "("
|
||||||
|
+ scores.stream()
|
||||||
|
.map(s -> MessageFormat.format(
|
||||||
|
" (aggr_score.significance = {0} AND aggr_score.priority = {1}) ",
|
||||||
|
s.getSignificance().getId(),
|
||||||
|
s.getPriority().getId()))
|
||||||
|
.collect(Collectors.joining(" OR "))
|
||||||
|
+ ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getDsFilter(Long dataSourceId) {
|
||||||
|
return (dataSourceId == null || dataSourceId <= 0)
|
||||||
|
? null
|
||||||
|
: "aggr_score.data_source_obj_id = " + dataSourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SearchResultsDTO fetchScoreSearchResultsDTOs(ScoreViewFilter filter, Long dataSourceId, long startItem, Long maxResultCount) throws NoCurrentCaseException, TskCoreException {
|
||||||
|
String scoreClause = getScoreFilter(filter.getScores());
|
||||||
|
String dsClause = getDsFilter(dataSourceId);
|
||||||
|
|
||||||
|
String filterSql = Stream.of(scoreClause, dsClause)
|
||||||
|
.filter(str -> str != null)
|
||||||
|
.collect(Collectors.joining(" AND "));
|
||||||
|
|
||||||
|
filterSql = StringUtils.isNotEmpty(filterSql) ? " WHERE " + filterSql + "\n" : "";
|
||||||
|
|
||||||
|
String baseQuery = BASE_AGGR_SCORE_QUERY
|
||||||
|
+ filterSql
|
||||||
|
+ "ORDER BY art_files.obj_id";
|
||||||
|
|
||||||
|
String countQuery = " COUNT(art_files.obj_id) AS count\n" + baseQuery;
|
||||||
|
|
||||||
|
AtomicLong totalCountRef = new AtomicLong(0);
|
||||||
|
AtomicReference<SQLException> countException = new AtomicReference<>(null);
|
||||||
|
getCase().getCaseDbAccessManager()
|
||||||
|
.select(countQuery, (rs) -> {
|
||||||
|
try {
|
||||||
|
if (rs.next()) {
|
||||||
|
totalCountRef.set(rs.getLong("count"));
|
||||||
|
}
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
countException.set(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
SQLException sqlEx = countException.get();
|
||||||
|
if (sqlEx != null) {
|
||||||
|
throw new TskCoreException(
|
||||||
|
MessageFormat.format("A sql exception occurred fetching results with query: SELECT {0}", countQuery),
|
||||||
|
sqlEx);
|
||||||
|
}
|
||||||
|
|
||||||
|
String objIdQuery = " art_files.obj_id, art_files.type\n" + baseQuery + "\n"
|
||||||
|
+ (maxResultCount != null && maxResultCount > 0 ? " LIMIT " + maxResultCount : "")
|
||||||
|
+ (startItem > 0 ? " OFFSET " + startItem : "");;
|
||||||
|
|
||||||
|
List<Long> fileIds = new ArrayList<>();
|
||||||
|
List<Long> artifactIds = new ArrayList<>();
|
||||||
|
AtomicReference<SQLException> objIdException = new AtomicReference<>(null);
|
||||||
|
getCase().getCaseDbAccessManager()
|
||||||
|
.select(objIdQuery, (rs) -> {
|
||||||
|
try {
|
||||||
|
while (rs.next()) {
|
||||||
|
String type = rs.getString("type");
|
||||||
|
if ("f".equalsIgnoreCase(type)) {
|
||||||
|
fileIds.add(rs.getLong("obj_id"));
|
||||||
|
} else {
|
||||||
|
artifactIds.add(rs.getLong("obj_id"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
objIdException.set(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
sqlEx = objIdException.get();
|
||||||
|
if (sqlEx != null) {
|
||||||
|
throw new TskCoreException(
|
||||||
|
MessageFormat.format("A sql exception occurred fetching results with query: SELECT {0}", objIdQuery),
|
||||||
|
sqlEx);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RowDTO> dataRows = new ArrayList<>();
|
||||||
|
|
||||||
|
if (!fileIds.isEmpty()) {
|
||||||
|
String joinedFileIds = fileIds.stream()
|
||||||
|
.map(l -> Long.toString(l))
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
|
||||||
|
List<AbstractFile> files = getCase().findAllFilesWhere("obj_id IN (" + joinedFileIds + ")");
|
||||||
|
|
||||||
|
for (AbstractFile file : files) {
|
||||||
|
List<Object> cellValues = Arrays.asList(
|
||||||
|
file.getName(),
|
||||||
|
Bundle.ScoreDAO_types_filelbl(),
|
||||||
|
file.getUniquePath(),
|
||||||
|
file.getCtime() <= 0
|
||||||
|
? null
|
||||||
|
: TimeZoneUtils.getFormattedTime(file.getCtime())
|
||||||
|
);
|
||||||
|
|
||||||
|
dataRows.add(new ScoreResultRowDTO(
|
||||||
|
new FileRowDTO(
|
||||||
|
file,
|
||||||
|
file.getId(),
|
||||||
|
file.getName(),
|
||||||
|
file.getNameExtension(),
|
||||||
|
MediaTypeUtils.getExtensionMediaType(file.getNameExtension()),
|
||||||
|
file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.ALLOC),
|
||||||
|
file.getType(),
|
||||||
|
cellValues),
|
||||||
|
// the modified column types: source name, type, path, created time
|
||||||
|
cellValues,
|
||||||
|
file.getId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!artifactIds.isEmpty()) {
|
||||||
|
String joinedArtifactIds = artifactIds.stream()
|
||||||
|
.map(l -> Long.toString(l))
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
|
||||||
|
List<DataArtifact> dataArtifacts = getCase().getBlackboard().getDataArtifactsWhere("artifacts.artifact_obj_id IN (" + joinedArtifactIds + ")");
|
||||||
|
TableData artTableData = MainDAO.getInstance().getDataArtifactsDAO().createTableData(null, dataArtifacts);
|
||||||
|
|
||||||
|
// all rows should be data artifact rows, and can be appended accordingly
|
||||||
|
for (RowDTO rowDTO : artTableData.rows) {
|
||||||
|
if (rowDTO instanceof DataArtifactRowDTO dataArtRow) {
|
||||||
|
BlackboardArtifact.Type artifactType = dataArtRow.getArtifact().getType();
|
||||||
|
List<Object> cellValues = Arrays.asList(
|
||||||
|
dataArtRow.getSrcContent().getName(),
|
||||||
|
artifactType.getDisplayName(),
|
||||||
|
dataArtRow.getArtifact().getUniquePath(),
|
||||||
|
getTimeStamp(dataArtRow.getArtifact())
|
||||||
|
);
|
||||||
|
|
||||||
|
dataRows.add(new ScoreResultRowDTO(
|
||||||
|
new DataArtifactRowDTO(
|
||||||
|
dataArtRow.getArtifact(),
|
||||||
|
dataArtRow.getSrcContent(),
|
||||||
|
dataArtRow.getLinkedFile(),
|
||||||
|
dataArtRow.isTimelineSupported(),
|
||||||
|
cellValues,
|
||||||
|
dataArtRow.getId()),
|
||||||
|
artifactType,
|
||||||
|
cellValues,
|
||||||
|
dataArtRow.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BaseSearchResultsDTO(
|
||||||
|
SCORE_TYPE_ID,
|
||||||
|
Bundle.ScoreDAO_mainNode_displayName(),
|
||||||
|
RESULT_SCORE_COLUMNS,
|
||||||
|
dataRows,
|
||||||
|
SCORE_TYPE_ID,
|
||||||
|
startItem,
|
||||||
|
totalCountRef.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTimeStamp(BlackboardArtifact artifact) {
|
||||||
|
Long time = getTime(artifact);
|
||||||
|
if (time == null || time <= 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return TimeZoneUtils.getFormattedTime(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long getTime(BlackboardArtifact artifact) {
|
||||||
|
try {
|
||||||
|
BlackboardAttribute timeAttr = artifact.getAttributes().stream()
|
||||||
|
.filter((attr) -> TIME_ATTR_IMPORTANCE.keySet().contains(attr.getAttributeType().getTypeID()))
|
||||||
|
.sorted(Comparator.comparing(attr -> TIME_ATTR_IMPORTANCE.get(attr.getAttributeType().getTypeID())))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
if (timeAttr != null) {
|
||||||
|
return timeAttr.getValueLong();
|
||||||
|
} else {
|
||||||
|
return (artifact.getParent() instanceof AbstractFile) ? ((AbstractFile) artifact.getParent()).getCtime() : null;
|
||||||
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, "An exception occurred while fetching time for artifact", ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeItemDTO<?> createTreeItem(DAOEvent daoEvent, TreeDisplayCount count) {
|
||||||
|
|
||||||
|
if (daoEvent instanceof ScoreContentEvent) {
|
||||||
|
ScoreContentEvent scoreEvt = (ScoreContentEvent) daoEvent;
|
||||||
|
return createScoreContentTreeItem(scoreEvt.getFilter(), scoreEvt.getDataSourceId(), count);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void clearCaches() {
|
||||||
|
this.searchParamsCache.invalidateAll();
|
||||||
|
handleIngestComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Set<? extends DAOEvent> handleIngestComplete() {
|
||||||
|
SubDAOUtils.invalidateKeys(this.searchParamsCache,
|
||||||
|
(searchParams) -> searchParamsMatchEvent(true, null, true, searchParams));
|
||||||
|
|
||||||
|
Set<? extends DAOEvent> treeEvts = SubDAOUtils.getIngestCompleteEvents(this.treeCounts,
|
||||||
|
(daoEvt, count) -> createTreeItem(daoEvt, count));
|
||||||
|
|
||||||
|
Set<? extends DAOEvent> fileViewRefreshEvents = getFileViewRefreshEvents(null);
|
||||||
|
|
||||||
|
List<? extends DAOEvent> fileViewRefreshTreeEvents = fileViewRefreshEvents.stream()
|
||||||
|
.map(evt -> new TreeEvent(createTreeItem(evt, TreeDisplayCount.UNSPECIFIED), true))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return Stream.of(treeEvts, fileViewRefreshEvents, fileViewRefreshTreeEvents)
|
||||||
|
.flatMap(c -> c.stream())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Set<TreeEvent> shouldRefreshTree() {
|
||||||
|
return SubDAOUtils.getRefreshEvents(this.treeCounts,
|
||||||
|
(daoEvt, count) -> createTreeItem(daoEvt, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Set<DAOEvent> processEvent(PropertyChangeEvent evt) {
|
||||||
|
AbstractFile af;
|
||||||
|
if (Case.Events.DATA_SOURCE_ADDED.toString().equals(evt.getPropertyName())) {
|
||||||
|
Long dsId = evt.getNewValue() instanceof Long ? (Long) evt.getNewValue() : null;
|
||||||
|
return invalidateScoreParamsAndReturnEvents(dsId);
|
||||||
|
|
||||||
|
} else if ((af = DAOEventUtils.getFileFromFileEvent(evt)) != null) {
|
||||||
|
return invalidateScoreParamsAndReturnEvents(af.getDataSourceObjectId());
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleDataEvent dataEvt;
|
||||||
|
if (Case.Events.CONTENT_TAG_ADDED.toString().equals(evt.getPropertyName()) && (evt instanceof ContentTagAddedEvent) && ((ContentTagAddedEvent) evt).getAddedTag().getContent() instanceof AbstractFile) {
|
||||||
|
ContentTagAddedEvent tagAddedEvt = (ContentTagAddedEvent) evt;
|
||||||
|
return invalidateScoreParamsAndReturnEvents(((AbstractFile) tagAddedEvt.getAddedTag().getContent()).getDataSourceObjectId());
|
||||||
|
} else if (Case.Events.CONTENT_TAG_DELETED.toString().equals(evt.getPropertyName())) {
|
||||||
|
return invalidateScoreParamsAndReturnEvents(null);
|
||||||
|
} else if (Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString().equals(evt.getPropertyName()) && (evt instanceof BlackBoardArtifactTagAddedEvent)) {
|
||||||
|
BlackBoardArtifactTagAddedEvent artifactAddedEvt = (BlackBoardArtifactTagAddedEvent) evt;
|
||||||
|
return invalidateScoreParamsAndReturnEvents(artifactAddedEvt.getAddedTag().getArtifact().getDataSourceObjectID());
|
||||||
|
} else if (Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString().equals(evt.getPropertyName())) {
|
||||||
|
return invalidateScoreParamsAndReturnEvents(null);
|
||||||
|
} else if ((dataEvt = DAOEventUtils.getModuelDataFromArtifactEvent(evt)) != null
|
||||||
|
&& Category.ANALYSIS_RESULT.equals(dataEvt.getBlackboardArtifactType().getCategory())) {
|
||||||
|
Set<Long> dsIds = dataEvt.getArtifacts().stream().map(ar -> ar.getDataSourceObjectID()).distinct().collect(Collectors.toSet());
|
||||||
|
return invalidateScoreParamsAndReturnEventsFromSet(dsIds);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<DAOEvent> invalidateScoreParamsAndReturnEvents(Long dataSourceId) {
|
||||||
|
return invalidateScoreParamsAndReturnEventsFromSet(dataSourceId != null ? Collections.singleton(dataSourceId) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<DAOEvent> invalidateScoreParamsAndReturnEventsFromSet(Set<Long> dataSourceIds) {
|
||||||
|
|
||||||
|
SubDAOUtils.invalidateKeys(this.searchParamsCache,
|
||||||
|
(searchParams) -> {
|
||||||
|
if (searchParams instanceof ScoreViewSearchParams) {
|
||||||
|
ScoreViewSearchParams scoreParams = (ScoreViewSearchParams) searchParams;
|
||||||
|
return (CollectionUtils.isEmpty(dataSourceIds) || scoreParams.getDataSourceId() == null || dataSourceIds.contains(scoreParams.getDataSourceId()));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Set<DAOEvent> dataEvents = CollectionUtils.isEmpty(dataSourceIds)
|
||||||
|
? Collections.singleton(new ScoreContentEvent(null, null))
|
||||||
|
: dataSourceIds.stream().map(dsId -> new ScoreContentEvent(null, dsId)).collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Set<DAOEvent> treeEvents = CollectionUtils.isEmpty(dataSourceIds)
|
||||||
|
? Collections.singleton(new TreeEvent(createScoreContentTreeItem(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
TreeDisplayCount.UNSPECIFIED),
|
||||||
|
true))
|
||||||
|
: dataSourceIds.stream().map(dsId -> new TreeEvent(
|
||||||
|
createScoreContentTreeItem(
|
||||||
|
null,
|
||||||
|
dsId,
|
||||||
|
TreeDisplayCount.UNSPECIFIED), true))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
return Stream.of(dataEvents, treeEvents).flatMap(s -> s.stream()).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean searchParamsMatchEvent(
|
||||||
|
boolean invalidatesScore,
|
||||||
|
Long dsId,
|
||||||
|
boolean dataSourceAdded,
|
||||||
|
Object searchParams) {
|
||||||
|
|
||||||
|
if (searchParams instanceof ScoreViewSearchParams) {
|
||||||
|
ScoreViewSearchParams scoreParams = (ScoreViewSearchParams) searchParams;
|
||||||
|
return (dataSourceAdded || (invalidatesScore && (scoreParams.getDataSourceId() == null || dsId == null || Objects.equals(scoreParams.getDataSourceId(), dsId))));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns events for when a full refresh is required because module content
|
||||||
|
* events will not necessarily provide events for files (i.e. data source
|
||||||
|
* added, ingest cancelled/completed).
|
||||||
|
*
|
||||||
|
* @param dataSourceId The data source id or null if not applicable.
|
||||||
|
*
|
||||||
|
* @return The set of events that apply in this situation.
|
||||||
|
*/
|
||||||
|
private Set<DAOEvent> getFileViewRefreshEvents(Long dataSourceId) {
|
||||||
|
return ImmutableSet.of(
|
||||||
|
new ScoreContentEvent(null, dataSourceId)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles fetching and paging of data for deleted content.
|
||||||
|
*/
|
||||||
|
public static class ScoreContentFetcher extends DAOFetcher<ScoreViewSearchParams> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param params Parameters to handle fetching of data.
|
||||||
|
*/
|
||||||
|
public ScoreContentFetcher(ScoreViewSearchParams params) {
|
||||||
|
super(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ScoreDAO getDAO() {
|
||||||
|
return MainDAO.getInstance().getScoreDAO();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchResultsDTO getSearchResults(int pageSize, int pageIdx) throws ExecutionException {
|
||||||
|
return getDAO().getFilesByScore(this.getParameters(), pageIdx * pageSize, (long) pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRefreshRequired(DAOEvent evt) {
|
||||||
|
return getDAO().isScoreContentInvalidating(this.getParameters(), evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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.mainui.datamodel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Score results dto.
|
||||||
|
*/
|
||||||
|
public class ScoreResultRowDTO extends BaseRowDTO {
|
||||||
|
private static final String TYPE_ID = "SCORE_RESULT_ROW";
|
||||||
|
|
||||||
|
public static String getTypeIdForClass() {
|
||||||
|
return TYPE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final FileRowDTO fileDTO;
|
||||||
|
private final DataArtifactRowDTO artifactDTO;
|
||||||
|
private final BlackboardArtifact.Type artifactType;
|
||||||
|
|
||||||
|
|
||||||
|
public ScoreResultRowDTO(FileRowDTO fileDTO, List<Object> cellValues, long id) {
|
||||||
|
super(cellValues, TYPE_ID, id);
|
||||||
|
this.fileDTO = fileDTO;
|
||||||
|
this.artifactDTO = null;
|
||||||
|
this.artifactType = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScoreResultRowDTO(DataArtifactRowDTO artifactDTO, BlackboardArtifact.Type artifactType, List<Object> cellValues, long id) {
|
||||||
|
super(cellValues, TYPE_ID, id);
|
||||||
|
this.fileDTO = null;
|
||||||
|
this.artifactDTO = artifactDTO;
|
||||||
|
this.artifactType = artifactType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileRowDTO getFileDTO() {
|
||||||
|
return fileDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataArtifactRowDTO getArtifactDTO() {
|
||||||
|
return artifactDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlackboardArtifact.Type getArtifactType() {
|
||||||
|
return artifactType;
|
||||||
|
}
|
||||||
|
}
|
@ -42,19 +42,13 @@ import java.util.logging.Level;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
|
||||||
import static org.sleuthkit.autopsy.core.UserPreferences.hideKnownFilesInViewsTree;
|
import static org.sleuthkit.autopsy.core.UserPreferences.hideKnownFilesInViewsTree;
|
||||||
import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTree;
|
import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTree;
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
|
||||||
import static org.sleuthkit.autopsy.mainui.datamodel.AbstractDAO.CACHE_DURATION;
|
import static org.sleuthkit.autopsy.mainui.datamodel.AbstractDAO.CACHE_DURATION;
|
||||||
import static org.sleuthkit.autopsy.mainui.datamodel.AbstractDAO.CACHE_DURATION_UNITS;
|
import static org.sleuthkit.autopsy.mainui.datamodel.AbstractDAO.CACHE_DURATION_UNITS;
|
||||||
import static org.sleuthkit.autopsy.mainui.datamodel.AbstractDAO.CACHE_SIZE;
|
import static org.sleuthkit.autopsy.mainui.datamodel.AbstractDAO.CACHE_SIZE;
|
||||||
@ -65,12 +59,10 @@ import org.sleuthkit.autopsy.mainui.datamodel.events.DeletedContentEvent;
|
|||||||
import org.sleuthkit.autopsy.mainui.datamodel.events.FileTypeExtensionsEvent;
|
import org.sleuthkit.autopsy.mainui.datamodel.events.FileTypeExtensionsEvent;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.events.FileTypeMimeEvent;
|
import org.sleuthkit.autopsy.mainui.datamodel.events.FileTypeMimeEvent;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.events.FileTypeSizeEvent;
|
import org.sleuthkit.autopsy.mainui.datamodel.events.FileTypeSizeEvent;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.events.ScoreContentEvent;
|
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.events.TreeCounts;
|
import org.sleuthkit.autopsy.mainui.datamodel.events.TreeCounts;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.events.TreeEvent;
|
import org.sleuthkit.autopsy.mainui.datamodel.events.TreeEvent;
|
||||||
import org.sleuthkit.autopsy.mainui.nodes.DAOFetcher;
|
import org.sleuthkit.autopsy.mainui.nodes.DAOFetcher;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact.Category;
|
|
||||||
import org.sleuthkit.datamodel.CaseDbAccessManager.CaseDbPreparedStatement;
|
import org.sleuthkit.datamodel.CaseDbAccessManager.CaseDbPreparedStatement;
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
@ -155,15 +147,6 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
return searchParamsCache.get(searchParams, () -> fetchSizeSearchResultsDTOs(key.getSizeFilter(), key.getDataSourceId(), startItem, maxCount));
|
return searchParamsCache.get(searchParams, () -> fetchSizeSearchResultsDTOs(key.getSizeFilter(), key.getDataSourceId(), startItem, maxCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchResultsDTO getFilesByScore(ScoreViewSearchParams key, long startItem, Long maxCount) throws ExecutionException, IllegalArgumentException {
|
|
||||||
if (key.getDataSourceId() != null && key.getDataSourceId() <= 0) {
|
|
||||||
throw new IllegalArgumentException("Data source id must be greater than 0 or null");
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchParams<Object> searchParams = new SearchParams<>(key, startItem, maxCount);
|
|
||||||
return searchParamsCache.get(searchParams, () -> fetchScoreSearchResultsDTOs(key.getFilter(), key.getDataSourceId(), startItem, maxCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns search results for the given deleted content search params.
|
* Returns search results for the given deleted content search params.
|
||||||
*
|
*
|
||||||
@ -228,24 +211,6 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
|| Objects.equals(params.getDataSourceId(), deletedContentEvt.getDataSourceId()));
|
|| Objects.equals(params.getDataSourceId(), deletedContentEvt.getDataSourceId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isScoreContentInvalidating(ScoreViewSearchParams params, DAOEvent eventData) {
|
|
||||||
if (!(eventData instanceof ScoreContentEvent)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScoreContentEvent scoreContentEvt = (ScoreContentEvent) eventData;
|
|
||||||
|
|
||||||
ScoreViewFilter evtFilter = scoreContentEvt.getFilter();
|
|
||||||
ScoreViewFilter paramsFilter = params.getFilter();
|
|
||||||
|
|
||||||
Long evtDsId = scoreContentEvt.getDataSourceId();
|
|
||||||
Long paramsDsId = params.getDataSourceId();
|
|
||||||
|
|
||||||
return (evtFilter == null || evtFilter.equals(paramsFilter))
|
|
||||||
&& (paramsDsId == null || evtDsId == null
|
|
||||||
|| Objects.equals(paramsDsId, evtDsId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a sql 'and' clause to filter by data source id if one is present.
|
* Returns a sql 'and' clause to filter by data source id if one is present.
|
||||||
*
|
*
|
||||||
@ -458,31 +423,6 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a clause to be proceeded with 'where' or 'and' that will show
|
|
||||||
* files specified by the filter and the specified data source.
|
|
||||||
*
|
|
||||||
* @param filter The scores filter.
|
|
||||||
* @param dataSourceId The id of the data source or null if no data source
|
|
||||||
* filtering.
|
|
||||||
*
|
|
||||||
* @return The clause to be proceeded with 'where' or 'and'.
|
|
||||||
*/
|
|
||||||
private String getFileScoreWhereStatement(ScoreViewFilter filter, Long dataSourceId) {
|
|
||||||
String scoreWhereClause = filter.getScores().stream()
|
|
||||||
.map(s -> MessageFormat.format(
|
|
||||||
" (tsk_aggregate_score.significance = {0} AND tsk_aggregate_score.priority = {1}) ",
|
|
||||||
s.getSignificance().getId(),
|
|
||||||
s.getPriority().getId()))
|
|
||||||
.collect(Collectors.joining(" OR "));
|
|
||||||
String query = MessageFormat.format(
|
|
||||||
" obj_id IN (SELECT tsk_aggregate_score.obj_id FROM tsk_aggregate_score WHERE {0}) {1}",
|
|
||||||
scoreWhereClause,
|
|
||||||
getDataSourceAndClause(dataSourceId));
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns counts for a collection of file extension search filters.
|
* Returns counts for a collection of file extension search filters.
|
||||||
*
|
*
|
||||||
@ -695,70 +635,6 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns counts for deleted content categories.
|
|
||||||
*
|
|
||||||
* @param dataSourceId The data source object id or null if no data source
|
|
||||||
* filtering should occur.
|
|
||||||
*
|
|
||||||
* @return The results.
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException
|
|
||||||
* @throws ExecutionException
|
|
||||||
*/
|
|
||||||
public TreeResultsDTO<ScoreViewSearchParams> getScoreContentCounts(Long dataSourceId) throws IllegalArgumentException, ExecutionException {
|
|
||||||
Set<ScoreViewFilter> indeterminateFilters = new HashSet<>();
|
|
||||||
for (DAOEvent evt : this.treeCounts.getEnqueued()) {
|
|
||||||
if (evt instanceof ScoreContentEvent) {
|
|
||||||
ScoreContentEvent scoreEvt = (ScoreContentEvent) evt;
|
|
||||||
if (dataSourceId == null || scoreEvt.getDataSourceId() == null || Objects.equals(scoreEvt.getDataSourceId(), dataSourceId)) {
|
|
||||||
if (scoreEvt.getFilter() == null) {
|
|
||||||
// if null filter, indicates full refresh and all file sizes need refresh.
|
|
||||||
indeterminateFilters.addAll(Arrays.asList(ScoreViewFilter.values()));
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
indeterminateFilters.add(scoreEvt.getFilter());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String queryStr = Stream.of(ScoreViewFilter.values())
|
|
||||||
.map((filter) -> {
|
|
||||||
String clause = getFileScoreWhereStatement(filter, dataSourceId);
|
|
||||||
return MessageFormat.format(" (SELECT COUNT(*) FROM tsk_files WHERE {0}) AS {1}", clause, filter.name());
|
|
||||||
})
|
|
||||||
.collect(Collectors.joining(", \n"));
|
|
||||||
|
|
||||||
try {
|
|
||||||
SleuthkitCase skCase = getCase();
|
|
||||||
|
|
||||||
List<TreeItemDTO<ScoreViewSearchParams>> treeList = new ArrayList<>();
|
|
||||||
skCase.getCaseDbAccessManager().select(queryStr, (resultSet) -> {
|
|
||||||
try {
|
|
||||||
if (resultSet.next()) {
|
|
||||||
for (ScoreViewFilter filter : ScoreViewFilter.values()) {
|
|
||||||
long count = resultSet.getLong(filter.name());
|
|
||||||
TreeDisplayCount displayCount = indeterminateFilters.contains(filter)
|
|
||||||
? TreeDisplayCount.INDETERMINATE
|
|
||||||
: TreeDisplayCount.getDeterminate(count);
|
|
||||||
|
|
||||||
treeList.add(createScoreContentTreeItem(filter, dataSourceId, displayCount));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException ex) {
|
|
||||||
logger.log(Level.WARNING, "An error occurred while fetching file type counts.", ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return new TreeResultsDTO<>(treeList);
|
|
||||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
|
||||||
throw new ExecutionException("An error occurred while fetching file counts with query:\n" + queryStr, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TreeItemDTO<DeletedContentSearchParams> createDeletedContentTreeItem(DeletedContentFilter filter, Long dataSourceId, TreeDisplayCount displayCount) {
|
private static TreeItemDTO<DeletedContentSearchParams> createDeletedContentTreeItem(DeletedContentFilter filter, Long dataSourceId, TreeDisplayCount displayCount) {
|
||||||
return new TreeItemDTO<>(
|
return new TreeItemDTO<>(
|
||||||
"DELETED_CONTENT",
|
"DELETED_CONTENT",
|
||||||
@ -768,16 +644,6 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
displayCount);
|
displayCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static TreeItemDTO<ScoreViewSearchParams> createScoreContentTreeItem(ScoreViewFilter filter, Long dataSourceId, TreeDisplayCount displayCount) {
|
|
||||||
return new TreeItemDTO<>(
|
|
||||||
"SCORE_CONTENT",
|
|
||||||
new ScoreViewSearchParams(filter, dataSourceId),
|
|
||||||
filter,
|
|
||||||
filter == null ? "" : filter.getDisplayName(),
|
|
||||||
displayCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a size tree item.
|
* Creates a size tree item.
|
||||||
*
|
*
|
||||||
@ -1089,11 +955,6 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
return fetchFileViewFiles(whereStatement, filter.getDisplayName(), startItem, maxResultCount);
|
return fetchFileViewFiles(whereStatement, filter.getDisplayName(), startItem, maxResultCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SearchResultsDTO fetchScoreSearchResultsDTOs(ScoreViewFilter filter, Long dataSourceId, long startItem, Long maxResultCount) throws NoCurrentCaseException, TskCoreException {
|
|
||||||
String whereStatement = getFileScoreWhereStatement(filter, dataSourceId);
|
|
||||||
return fetchFileViewFiles(whereStatement, filter.getDisplayName(), startItem, maxResultCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SearchResultsDTO fetchFileViewFiles(String originalWhereStatement, String displayName, long startItem, Long maxResultCount) throws NoCurrentCaseException, TskCoreException {
|
private SearchResultsDTO fetchFileViewFiles(String originalWhereStatement, String displayName, long startItem, Long maxResultCount) throws NoCurrentCaseException, TskCoreException {
|
||||||
|
|
||||||
// Add offset and/or paging, if specified
|
// Add offset and/or paging, if specified
|
||||||
@ -1156,9 +1017,6 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
} else if (daoEvent instanceof DeletedContentEvent) {
|
} else if (daoEvent instanceof DeletedContentEvent) {
|
||||||
DeletedContentEvent sizeEvt = (DeletedContentEvent) daoEvent;
|
DeletedContentEvent sizeEvt = (DeletedContentEvent) daoEvent;
|
||||||
return createDeletedContentTreeItem(sizeEvt.getFilter(), sizeEvt.getDataSourceId(), count);
|
return createDeletedContentTreeItem(sizeEvt.getFilter(), sizeEvt.getDataSourceId(), count);
|
||||||
} else if (daoEvent instanceof ScoreContentEvent) {
|
|
||||||
ScoreContentEvent scoreEvt = (ScoreContentEvent) daoEvent;
|
|
||||||
return createScoreContentTreeItem(scoreEvt.getFilter(), scoreEvt.getDataSourceId(), count);
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -1174,7 +1032,7 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
@Override
|
@Override
|
||||||
Set<? extends DAOEvent> handleIngestComplete() {
|
Set<? extends DAOEvent> handleIngestComplete() {
|
||||||
SubDAOUtils.invalidateKeys(this.searchParamsCache,
|
SubDAOUtils.invalidateKeys(this.searchParamsCache,
|
||||||
(searchParams) -> searchParamsMatchEvent(null, null, null, null, true, null, true, searchParams));
|
(searchParams) -> searchParamsMatchEvent(null, null, null, null, null, true, searchParams));
|
||||||
|
|
||||||
Set<? extends DAOEvent> treeEvts = SubDAOUtils.getIngestCompleteEvents(this.treeCounts,
|
Set<? extends DAOEvent> treeEvts = SubDAOUtils.getIngestCompleteEvents(this.treeCounts,
|
||||||
(daoEvt, count) -> createTreeItem(daoEvt, count));
|
(daoEvt, count) -> createTreeItem(daoEvt, count));
|
||||||
@ -1204,16 +1062,12 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
Set<DeletedContentFilter> deletedContentFilters = null;
|
Set<DeletedContentFilter> deletedContentFilters = null;
|
||||||
String evtMimeType = null;
|
String evtMimeType = null;
|
||||||
FileSizeFilter evtFileSize = null;
|
FileSizeFilter evtFileSize = null;
|
||||||
boolean scoreInvalidating = false;
|
|
||||||
|
|
||||||
AbstractFile af;
|
|
||||||
|
|
||||||
if (Case.Events.DATA_SOURCE_ADDED.toString().equals(evt.getPropertyName())) {
|
if (Case.Events.DATA_SOURCE_ADDED.toString().equals(evt.getPropertyName())) {
|
||||||
dsId = evt.getNewValue() instanceof Long ? (Long) evt.getNewValue() : null;
|
dsId = evt.getNewValue() instanceof Long ? (Long) evt.getNewValue() : null;
|
||||||
dataSourceAdded = true;
|
dataSourceAdded = true;
|
||||||
scoreInvalidating = true;
|
} else {
|
||||||
return invalidateAndReturnEvents(evtExtFilters, evtMimeType, evtFileSize, deletedContentFilters, scoreInvalidating, dsId, dataSourceAdded);
|
AbstractFile af = DAOEventUtils.getFileFromFileEvent(evt);
|
||||||
} else if ((af = DAOEventUtils.getFileFromFileEvent(evt)) != null) {
|
|
||||||
if (af == null) {
|
if (af == null) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
} else if (hideKnownFilesInViewsTree() && TskData.FileKnown.KNOWN.equals(af.getKnown())) {
|
} else if (hideKnownFilesInViewsTree() && TskData.FileKnown.KNOWN.equals(af.getKnown())) {
|
||||||
@ -1245,31 +1099,9 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
if (evtExtFilters == null || evtExtFilters.isEmpty() && deletedContentFilters.isEmpty() && evtMimeType == null && evtFileSize == null) {
|
if (evtExtFilters == null || evtExtFilters.isEmpty() && deletedContentFilters.isEmpty() && evtMimeType == null && evtFileSize == null) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
// a file is being added, so invalidate all score items where data source id is in scope
|
|
||||||
scoreInvalidating = true;
|
|
||||||
return invalidateAndReturnEvents(evtExtFilters, evtMimeType, evtFileSize, deletedContentFilters, scoreInvalidating, dsId, dataSourceAdded);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleDataEvent dataEvt;
|
return invalidateAndReturnEvents(evtExtFilters, evtMimeType, evtFileSize, deletedContentFilters, dsId, dataSourceAdded);
|
||||||
if (Case.Events.CONTENT_TAG_ADDED.toString().equals(evt.getPropertyName()) && (evt instanceof ContentTagAddedEvent) && ((ContentTagAddedEvent) evt).getAddedTag().getContent() instanceof AbstractFile) {
|
|
||||||
ContentTagAddedEvent tagAddedEvt = (ContentTagAddedEvent) evt;
|
|
||||||
return invalidateScoreParamsAndReturnEvents(((AbstractFile) tagAddedEvt.getAddedTag().getContent()).getDataSourceObjectId());
|
|
||||||
} else if (Case.Events.CONTENT_TAG_DELETED.toString().equals(evt.getPropertyName())) {
|
|
||||||
return invalidateScoreParamsAndReturnEvents(null);
|
|
||||||
} else if (Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED.toString().equals(evt.getPropertyName()) && (evt instanceof BlackBoardArtifactTagAddedEvent)) {
|
|
||||||
BlackBoardArtifactTagAddedEvent artifactAddedEvt = (BlackBoardArtifactTagAddedEvent) evt;
|
|
||||||
return invalidateScoreParamsAndReturnEvents(artifactAddedEvt.getAddedTag().getArtifact().getDataSourceObjectID());
|
|
||||||
} else if (Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED.toString().equals(evt.getPropertyName())) {
|
|
||||||
return invalidateScoreParamsAndReturnEvents(null);
|
|
||||||
} else if ((dataEvt = DAOEventUtils.getModuelDataFromArtifactEvent(evt)) != null
|
|
||||||
&& Category.ANALYSIS_RESULT.equals(dataEvt.getBlackboardArtifactType().getCategory())) {
|
|
||||||
Set<Long> dsIds = dataEvt.getArtifacts().stream().map(ar -> ar.getDataSourceObjectID()).distinct().collect(Collectors.toSet());
|
|
||||||
return invalidateScoreParamsAndReturnEventsFromSet(dsIds);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1279,7 +1111,6 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
* @param evtMimeType The mime type or null.
|
* @param evtMimeType The mime type or null.
|
||||||
* @param evtFileSize The file size filter or null.
|
* @param evtFileSize The file size filter or null.
|
||||||
* @param deletedContentFilters The set of affected deleted content filters.
|
* @param deletedContentFilters The set of affected deleted content filters.
|
||||||
* @param invalidateScore All score views should be invalidated if data source id is in scope.
|
|
||||||
* @param dsId The data source id or null.
|
* @param dsId The data source id or null.
|
||||||
* @param dataSourceAdded Whether or not this is a data source added
|
* @param dataSourceAdded Whether or not this is a data source added
|
||||||
* event.
|
* event.
|
||||||
@ -1287,42 +1118,19 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
* @return The set of dao events to be fired.
|
* @return The set of dao events to be fired.
|
||||||
*/
|
*/
|
||||||
private Set<DAOEvent> invalidateAndReturnEvents(Set<FileExtSearchFilter> evtExtFilters, String evtMimeType,
|
private Set<DAOEvent> invalidateAndReturnEvents(Set<FileExtSearchFilter> evtExtFilters, String evtMimeType,
|
||||||
FileSizeFilter evtFileSize, Set<DeletedContentFilter> deletedContentFilters, boolean invalidateScore, Long dsId, boolean dataSourceAdded) {
|
FileSizeFilter evtFileSize, Set<DeletedContentFilter> deletedContentFilters, Long dsId, boolean dataSourceAdded) {
|
||||||
|
|
||||||
SubDAOUtils.invalidateKeys(this.searchParamsCache,
|
SubDAOUtils.invalidateKeys(this.searchParamsCache,
|
||||||
(searchParams) -> searchParamsMatchEvent(evtExtFilters, deletedContentFilters,
|
(searchParams) -> searchParamsMatchEvent(evtExtFilters, deletedContentFilters,
|
||||||
evtMimeType, evtFileSize, invalidateScore, dsId, dataSourceAdded, searchParams));
|
evtMimeType, evtFileSize, dsId, dataSourceAdded, searchParams));
|
||||||
|
|
||||||
return getDAOEvents(evtExtFilters, deletedContentFilters, evtMimeType, evtFileSize, invalidateScore, dsId, dataSourceAdded);
|
return getDAOEvents(evtExtFilters, deletedContentFilters, evtMimeType, evtFileSize, dsId, dataSourceAdded);
|
||||||
}
|
|
||||||
|
|
||||||
private Set<DAOEvent> invalidateScoreParamsAndReturnEvents(Long dataSourceId) {
|
|
||||||
return invalidateScoreParamsAndReturnEventsFromSet(dataSourceId != null ? Collections.singleton(dataSourceId) : null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Set<DAOEvent> invalidateScoreParamsAndReturnEventsFromSet(Set<Long> dataSourceIds) {
|
|
||||||
|
|
||||||
SubDAOUtils.invalidateKeys(this.searchParamsCache,
|
|
||||||
(searchParams) -> {
|
|
||||||
if (searchParams instanceof ScoreViewSearchParams) {
|
|
||||||
ScoreViewSearchParams scoreParams = (ScoreViewSearchParams) searchParams;
|
|
||||||
return (CollectionUtils.isEmpty(dataSourceIds) || scoreParams.getDataSourceId() == null || dataSourceIds.contains(scoreParams.getDataSourceId()));
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return CollectionUtils.isEmpty(dataSourceIds)
|
|
||||||
? Collections.singleton(new ScoreContentEvent(null, null))
|
|
||||||
: dataSourceIds.stream().map(dsId -> new ScoreContentEvent(null, dsId)).collect(Collectors.toSet());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean searchParamsMatchEvent(Set<FileExtSearchFilter> evtExtFilters,
|
private boolean searchParamsMatchEvent(Set<FileExtSearchFilter> evtExtFilters,
|
||||||
Set<DeletedContentFilter> deletedContentFilters,
|
Set<DeletedContentFilter> deletedContentFilters,
|
||||||
String evtMimeType,
|
String evtMimeType,
|
||||||
FileSizeFilter evtFileSize,
|
FileSizeFilter evtFileSize,
|
||||||
boolean invalidatesScore,
|
|
||||||
Long dsId,
|
Long dsId,
|
||||||
boolean dataSourceAdded,
|
boolean dataSourceAdded,
|
||||||
Object searchParams) {
|
Object searchParams) {
|
||||||
@ -1349,9 +1157,6 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
DeletedContentSearchParams deletedParams = (DeletedContentSearchParams) searchParams;
|
DeletedContentSearchParams deletedParams = (DeletedContentSearchParams) searchParams;
|
||||||
return (dataSourceAdded || (deletedContentFilters != null && deletedContentFilters.contains(deletedParams.getFilter())))
|
return (dataSourceAdded || (deletedContentFilters != null && deletedContentFilters.contains(deletedParams.getFilter())))
|
||||||
&& (deletedParams.getDataSourceId() == null || dsId == null || Objects.equals(deletedParams.getDataSourceId(), dsId));
|
&& (deletedParams.getDataSourceId() == null || dsId == null || Objects.equals(deletedParams.getDataSourceId(), dsId));
|
||||||
} else if (searchParams instanceof ScoreViewSearchParams) {
|
|
||||||
ScoreViewSearchParams scoreParams = (ScoreViewSearchParams) searchParams;
|
|
||||||
return (dataSourceAdded || (invalidatesScore && (scoreParams.getDataSourceId() == null || dsId == null || Objects.equals(scoreParams.getDataSourceId(), dsId))));
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1374,7 +1179,6 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
Set<DeletedContentFilter> deletedContentFilters,
|
Set<DeletedContentFilter> deletedContentFilters,
|
||||||
String mimeType,
|
String mimeType,
|
||||||
FileSizeFilter sizeFilter,
|
FileSizeFilter sizeFilter,
|
||||||
boolean invalidateScore,
|
|
||||||
Long dsId,
|
Long dsId,
|
||||||
boolean dataSourceAdded) {
|
boolean dataSourceAdded) {
|
||||||
|
|
||||||
@ -1399,10 +1203,6 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
daoEvents.add(new FileTypeSizeEvent(sizeFilter, dsId));
|
daoEvents.add(new FileTypeSizeEvent(sizeFilter, dsId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invalidateScore) {
|
|
||||||
daoEvents.add(new ScoreContentEvent(null, dsId));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TreeEvent> treeEvents = this.treeCounts.enqueueAll(daoEvents).stream()
|
List<TreeEvent> treeEvents = this.treeCounts.enqueueAll(daoEvents).stream()
|
||||||
.map(daoEvt -> new TreeEvent(createTreeItem(daoEvt, TreeDisplayCount.INDETERMINATE), false))
|
.map(daoEvt -> new TreeEvent(createTreeItem(daoEvt, TreeDisplayCount.INDETERMINATE), false))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@ -1456,13 +1256,10 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
return ImmutableSet.of(
|
return ImmutableSet.of(
|
||||||
new DeletedContentEvent(null, dataSourceId),
|
new DeletedContentEvent(null, dataSourceId),
|
||||||
new FileTypeSizeEvent(null, dataSourceId),
|
new FileTypeSizeEvent(null, dataSourceId),
|
||||||
new FileTypeExtensionsEvent(null, dataSourceId),
|
new FileTypeExtensionsEvent(null, dataSourceId)
|
||||||
new ScoreContentEvent(null, dataSourceId)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles fetching and paging of data for file types by extension.
|
* Handles fetching and paging of data for file types by extension.
|
||||||
*/
|
*/
|
||||||
@ -1579,35 +1376,4 @@ public class ViewsDAO extends AbstractDAO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles fetching and paging of data for deleted content.
|
|
||||||
*/
|
|
||||||
public static class ScoreFileFetcher extends DAOFetcher<ScoreViewSearchParams> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main constructor.
|
|
||||||
*
|
|
||||||
* @param params Parameters to handle fetching of data.
|
|
||||||
*/
|
|
||||||
public ScoreFileFetcher(ScoreViewSearchParams params) {
|
|
||||||
super(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ViewsDAO getDAO() {
|
|
||||||
return MainDAO.getInstance().getViewsDAO();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SearchResultsDTO getSearchResults(int pageSize, int pageIdx) throws ExecutionException {
|
|
||||||
return getDAO().getFilesByScore(this.getParameters(), pageIdx * pageSize, (long) pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRefreshRequired(DAOEvent evt) {
|
|
||||||
return getDAO().isScoreContentInvalidating(this.getParameters(), evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ RootFactory_OsAccountsRootNode_displayName=OS Accounts
|
|||||||
RootFactory_ReportsRootNode_displayName=Reports
|
RootFactory_ReportsRootNode_displayName=Reports
|
||||||
RootFactory_TagsRootNode_displayName=Tags
|
RootFactory_TagsRootNode_displayName=Tags
|
||||||
RootFactory_ViewsRootNode_displayName=Views
|
RootFactory_ViewsRootNode_displayName=Views
|
||||||
|
ScoreTypeFactory_ScoreParentNode_displayName=Score
|
||||||
SearchResultRootNode_createSheet_childCount_displayName=Child Count
|
SearchResultRootNode_createSheet_childCount_displayName=Child Count
|
||||||
SearchResultRootNode_createSheet_childCount_name=Child Count
|
SearchResultRootNode_createSheet_childCount_name=Child Count
|
||||||
SearchResultRootNode_createSheet_type_displayName=Name
|
SearchResultRootNode_createSheet_type_displayName=Name
|
||||||
@ -31,6 +32,5 @@ ViewsTypeFactory_DeletedParentNode_displayName=Deleted Files
|
|||||||
ViewsTypeFactory_ExtensionParentNode_displayName=By Extension
|
ViewsTypeFactory_ExtensionParentNode_displayName=By Extension
|
||||||
ViewsTypeFactory_FileTypesParentNode_displayName=File Types
|
ViewsTypeFactory_FileTypesParentNode_displayName=File Types
|
||||||
ViewsTypeFactory_MimeParentNode_displayName=By MIME Type
|
ViewsTypeFactory_MimeParentNode_displayName=By MIME Type
|
||||||
ViewsTypeFactory_ScoreParentNode_displayName=Score
|
|
||||||
ViewsTypeFactory_SizeParentNode_displayName=File Size
|
ViewsTypeFactory_SizeParentNode_displayName=File Size
|
||||||
VolumnNode_ExtractUnallocAction_text=Extract Unallocated Space to Single Files
|
VolumnNode_ExtractUnallocAction_text=Extract Unallocated Space to Single Files
|
||||||
|
@ -33,6 +33,7 @@ import org.sleuthkit.autopsy.mainui.datamodel.DataArtifactRowDTO;
|
|||||||
import org.sleuthkit.autopsy.mainui.datamodel.DataArtifactTableSearchResultsDTO;
|
import org.sleuthkit.autopsy.mainui.datamodel.DataArtifactTableSearchResultsDTO;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.DataArtifactTableSearchResultsDTO.CommAccoutTableSearchResultsDTO;
|
import org.sleuthkit.autopsy.mainui.datamodel.DataArtifactTableSearchResultsDTO.CommAccoutTableSearchResultsDTO;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.SearchResultsDTO;
|
||||||
import org.sleuthkit.datamodel.DataArtifact;
|
import org.sleuthkit.datamodel.DataArtifact;
|
||||||
import org.sleuthkit.datamodel.Tag;
|
import org.sleuthkit.datamodel.Tag;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
@ -57,7 +58,7 @@ public class DataArtifactNode extends ArtifactNode<DataArtifact, DataArtifactRow
|
|||||||
this(tableData, artifactRow, getIconFilePath(tableData), backgroundTasksPool);
|
this(tableData, artifactRow, getIconFilePath(tableData), backgroundTasksPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataArtifactNode(DataArtifactTableSearchResultsDTO tableData, DataArtifactRowDTO artifactRow, String iconPath, ExecutorService backgroundTasksPool) {
|
public DataArtifactNode(SearchResultsDTO tableData, DataArtifactRowDTO artifactRow, String iconPath, ExecutorService backgroundTasksPool) {
|
||||||
super(tableData, artifactRow, tableData.getColumns(), createLookup(artifactRow), iconPath, backgroundTasksPool);
|
super(tableData, artifactRow, tableData.getColumns(), createLookup(artifactRow), iconPath, backgroundTasksPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ import org.sleuthkit.autopsy.mainui.datamodel.events.DAOAggregateEvent;
|
|||||||
import org.sleuthkit.autopsy.mainui.datamodel.events.DAOEvent;
|
import org.sleuthkit.autopsy.mainui.datamodel.events.DAOEvent;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.events.HostPersonEvent;
|
import org.sleuthkit.autopsy.mainui.datamodel.events.HostPersonEvent;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.events.TreeEvent;
|
import org.sleuthkit.autopsy.mainui.datamodel.events.TreeEvent;
|
||||||
|
import org.sleuthkit.autopsy.mainui.nodes.ScoreTypeFactory.ScoreParentNode;
|
||||||
import org.sleuthkit.autopsy.mainui.nodes.TreeNode.StaticTreeNode;
|
import org.sleuthkit.autopsy.mainui.nodes.TreeNode.StaticTreeNode;
|
||||||
import org.sleuthkit.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
import org.sleuthkit.datamodel.Host;
|
import org.sleuthkit.datamodel.Host;
|
||||||
@ -194,6 +195,7 @@ public class RootFactory {
|
|||||||
new AnalysisResultsRootNode(null),
|
new AnalysisResultsRootNode(null),
|
||||||
new OsAccountsRootNode(null),
|
new OsAccountsRootNode(null),
|
||||||
new TagsRootNode(null),
|
new TagsRootNode(null),
|
||||||
|
new ScoreParentNode(null),
|
||||||
new ReportsRootNode()
|
new ReportsRootNode()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -505,6 +507,7 @@ public class RootFactory {
|
|||||||
new DataArtifactsRootNode(dataSourceObjId),
|
new DataArtifactsRootNode(dataSourceObjId),
|
||||||
new AnalysisResultsRootNode(dataSourceObjId),
|
new AnalysisResultsRootNode(dataSourceObjId),
|
||||||
new OsAccountsRootNode(dataSourceObjId),
|
new OsAccountsRootNode(dataSourceObjId),
|
||||||
|
new ScoreParentNode(dataSourceObjId),
|
||||||
new TagsRootNode(dataSourceObjId)
|
new TagsRootNode(dataSourceObjId)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* 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.mainui.nodes;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import org.openide.nodes.Children;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
|
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.MainDAO;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.ScoreViewFilter;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.ScoreViewSearchParams;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.TreeResultsDTO;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.TreeResultsDTO.TreeItemDTO;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.events.DAOAggregateEvent;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.events.DAOEvent;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.events.TreeEvent;
|
||||||
|
import org.sleuthkit.autopsy.mainui.nodes.TreeNode.StaticTreeNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Factories for displaying views.
|
||||||
|
*/
|
||||||
|
public class ScoreTypeFactory {
|
||||||
|
private static final String SCORE_ICON = "org/sleuthkit/autopsy/images/red-circle-exclamation.png";
|
||||||
|
/**
|
||||||
|
* Children of 'Score' in the tree.
|
||||||
|
*/
|
||||||
|
public static class ScoreChildren extends Children.Array {
|
||||||
|
|
||||||
|
public ScoreChildren(Long dataSourceId) {
|
||||||
|
super(ImmutableList.of(
|
||||||
|
new ScoreParentNode(dataSourceId)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent of score nodes in the tree.
|
||||||
|
*/
|
||||||
|
@Messages({"ScoreTypeFactory_ScoreParentNode_displayName=Score"})
|
||||||
|
public static class ScoreParentNode extends StaticTreeNode {
|
||||||
|
|
||||||
|
ScoreParentNode(Long dataSourceId) {
|
||||||
|
super(
|
||||||
|
"FILE_VIEW_SCORE_PARENT",
|
||||||
|
Bundle.ScoreTypeFactory_ScoreParentNode_displayName(),
|
||||||
|
SCORE_ICON,
|
||||||
|
new ScoreContentFactory(dataSourceId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The factory for creating deleted content tree nodes.
|
||||||
|
*/
|
||||||
|
public static class ScoreContentFactory extends TreeChildFactory<ScoreViewSearchParams> {
|
||||||
|
|
||||||
|
private final Long dataSourceId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param dataSourceId The data source to filter files to or null.
|
||||||
|
*/
|
||||||
|
public ScoreContentFactory(Long dataSourceId) {
|
||||||
|
this.dataSourceId = dataSourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TreeNode<ScoreViewSearchParams> createNewNode(TreeResultsDTO.TreeItemDTO<? extends ScoreViewSearchParams> rowData) {
|
||||||
|
return new ScoreContentTypeNode(rowData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TreeResultsDTO<? extends ScoreViewSearchParams> getChildResults() throws IllegalArgumentException, ExecutionException {
|
||||||
|
return MainDAO.getInstance().getScoreDAO().getScoreContentCounts(dataSourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleDAOAggregateEvent(DAOAggregateEvent aggEvt) {
|
||||||
|
for (DAOEvent evt : aggEvt.getEvents()) {
|
||||||
|
if (evt instanceof TreeEvent) {
|
||||||
|
TreeResultsDTO.TreeItemDTO<ScoreViewSearchParams> treeItem = super.getTypedTreeItem((TreeEvent) evt, ScoreViewSearchParams.class);
|
||||||
|
// if search params has null filter, trigger full refresh
|
||||||
|
if (treeItem != null && treeItem.getSearchParams().getFilter() == null) {
|
||||||
|
super.update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.handleDAOAggregateEvent(aggEvt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TreeResultsDTO.TreeItemDTO<? extends ScoreViewSearchParams> getOrCreateRelevantChild(TreeEvent treeEvt) {
|
||||||
|
TreeResultsDTO.TreeItemDTO<ScoreViewSearchParams> originalTreeItem = super.getTypedTreeItem(treeEvt, ScoreViewSearchParams.class);
|
||||||
|
|
||||||
|
if (originalTreeItem != null
|
||||||
|
// only create child if size filter is present (if null, update should be triggered separately)
|
||||||
|
&& originalTreeItem.getSearchParams().getFilter() != null
|
||||||
|
&& (this.dataSourceId == null || Objects.equals(this.dataSourceId, originalTreeItem.getSearchParams().getDataSourceId()))) {
|
||||||
|
|
||||||
|
// generate new type so that if it is a subtree event (i.e. keyword hits), the right tree item is created.
|
||||||
|
ScoreViewSearchParams searchParam = originalTreeItem.getSearchParams();
|
||||||
|
return new TreeResultsDTO.TreeItemDTO<>(
|
||||||
|
ScoreViewSearchParams.getTypeId(),
|
||||||
|
new ScoreViewSearchParams(searchParam.getFilter(), this.dataSourceId),
|
||||||
|
searchParam.getFilter(),
|
||||||
|
searchParam.getFilter().getDisplayName(),
|
||||||
|
originalTreeItem.getDisplayCount());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(TreeItemDTO<? extends ScoreViewSearchParams> o1, TreeItemDTO<? extends ScoreViewSearchParams> o2) {
|
||||||
|
return Integer.compare(o1.getSearchParams().getFilter().getId(), o2.getSearchParams().getFilter().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a deleted content tree node.
|
||||||
|
*/
|
||||||
|
static class ScoreContentTypeNode extends TreeNode<ScoreViewSearchParams> {
|
||||||
|
|
||||||
|
private static final String BAD_SCORE_ICON = SCORE_ICON;
|
||||||
|
private static final String SUS_SCORE_ICON = "org/sleuthkit/autopsy/images/yellow-circle-yield.png";
|
||||||
|
|
||||||
|
private static String getIcon(ScoreViewFilter filter) {
|
||||||
|
switch (filter) {
|
||||||
|
case SUSPICIOUS:
|
||||||
|
return SUS_SCORE_ICON;
|
||||||
|
case BAD:
|
||||||
|
default:
|
||||||
|
return BAD_SCORE_ICON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main constructor.
|
||||||
|
*
|
||||||
|
* @param itemData The data for the node.
|
||||||
|
*/
|
||||||
|
ScoreContentTypeNode(TreeResultsDTO.TreeItemDTO<? extends ScoreViewSearchParams> itemData) {
|
||||||
|
super("SCORE_CONTENT_" + itemData.getSearchParams().getFilter().name(), getIcon(itemData.getSearchParams().getFilter()), itemData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void respondSelection(DataResultTopComponent dataResultPanel) {
|
||||||
|
dataResultPanel.displayScoreContent(this.getItemData().getSearchParams());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -27,6 +27,7 @@ import java.util.stream.Collectors;
|
|||||||
import org.openide.nodes.ChildFactory;
|
import org.openide.nodes.ChildFactory;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.AnalysisResultRowDTO;
|
import org.sleuthkit.autopsy.mainui.datamodel.AnalysisResultRowDTO;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.AnalysisResultTableSearchResultsDTO;
|
import org.sleuthkit.autopsy.mainui.datamodel.AnalysisResultTableSearchResultsDTO;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.BlackboardArtifactTagsRowDTO;
|
import org.sleuthkit.autopsy.mainui.datamodel.BlackboardArtifactTagsRowDTO;
|
||||||
@ -49,6 +50,7 @@ import org.sleuthkit.autopsy.mainui.datamodel.RowDTO;
|
|||||||
import org.sleuthkit.autopsy.mainui.datamodel.ContentRowDTO.VolumeRowDTO;
|
import org.sleuthkit.autopsy.mainui.datamodel.ContentRowDTO.VolumeRowDTO;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.CreditCardByFileRowDTO;
|
import org.sleuthkit.autopsy.mainui.datamodel.CreditCardByFileRowDTO;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.ReportsRowDTO;
|
import org.sleuthkit.autopsy.mainui.datamodel.ReportsRowDTO;
|
||||||
|
import org.sleuthkit.autopsy.mainui.datamodel.ScoreResultRowDTO;
|
||||||
import org.sleuthkit.autopsy.mainui.nodes.FileNode.LayoutFileNode;
|
import org.sleuthkit.autopsy.mainui.nodes.FileNode.LayoutFileNode;
|
||||||
import org.sleuthkit.autopsy.mainui.nodes.FileNode.SlackFileNode;
|
import org.sleuthkit.autopsy.mainui.nodes.FileNode.SlackFileNode;
|
||||||
import org.sleuthkit.autopsy.mainui.nodes.SpecialDirectoryNode.LocalDirectoryNode;
|
import org.sleuthkit.autopsy.mainui.nodes.SpecialDirectoryNode.LocalDirectoryNode;
|
||||||
@ -89,7 +91,14 @@ public class SearchResultChildFactory extends ChildFactory<ChildKey> {
|
|||||||
protected Node createNodeForKey(ChildKey key) {
|
protected Node createNodeForKey(ChildKey key) {
|
||||||
String typeId = key.getRow().getTypeId();
|
String typeId = key.getRow().getTypeId();
|
||||||
try {
|
try {
|
||||||
if (DataArtifactRowDTO.getTypeIdForClass().equals(typeId)) {
|
if (ScoreResultRowDTO.getTypeIdForClass().equals(typeId) && key.getRow() instanceof ScoreResultRowDTO scoreRow) {
|
||||||
|
if (scoreRow.getArtifactDTO() != null && scoreRow.getArtifactType() != null) {
|
||||||
|
String iconPath = IconsUtil.getIconFilePath(scoreRow.getArtifactType().getTypeID());
|
||||||
|
return new DataArtifactNode(key.getSearchResults(), scoreRow.getArtifactDTO(), iconPath, nodeThreadPool);
|
||||||
|
} else if (scoreRow.getFileDTO() != null) {
|
||||||
|
return new FileNode(key.getSearchResults(), scoreRow.getFileDTO(), true, nodeThreadPool);
|
||||||
|
}
|
||||||
|
} else if (DataArtifactRowDTO.getTypeIdForClass().equals(typeId)) {
|
||||||
return new DataArtifactNode((DataArtifactTableSearchResultsDTO) key.getSearchResults(), (DataArtifactRowDTO) key.getRow(), nodeThreadPool);
|
return new DataArtifactNode((DataArtifactTableSearchResultsDTO) key.getSearchResults(), (DataArtifactRowDTO) key.getRow(), nodeThreadPool);
|
||||||
} else if (FileRowDTO.getTypeIdForClass().equals(typeId)) {
|
} else if (FileRowDTO.getTypeIdForClass().equals(typeId)) {
|
||||||
return new FileNode(key.getSearchResults(), (FileRowDTO) key.getRow(), true, nodeThreadPool);
|
return new FileNode(key.getSearchResults(), (FileRowDTO) key.getRow(), true, nodeThreadPool);
|
||||||
|
@ -37,8 +37,6 @@ import org.sleuthkit.autopsy.mainui.datamodel.FileTypeMimeSearchParams;
|
|||||||
import org.sleuthkit.autopsy.mainui.datamodel.DeletedContentSearchParams;
|
import org.sleuthkit.autopsy.mainui.datamodel.DeletedContentSearchParams;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.FileTypeSizeSearchParams;
|
import org.sleuthkit.autopsy.mainui.datamodel.FileTypeSizeSearchParams;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.MainDAO;
|
import org.sleuthkit.autopsy.mainui.datamodel.MainDAO;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.ScoreViewFilter;
|
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.ScoreViewSearchParams;
|
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.TreeResultsDTO;
|
import org.sleuthkit.autopsy.mainui.datamodel.TreeResultsDTO;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.TreeResultsDTO.TreeItemDTO;
|
import org.sleuthkit.autopsy.mainui.datamodel.TreeResultsDTO.TreeItemDTO;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.events.DAOAggregateEvent;
|
import org.sleuthkit.autopsy.mainui.datamodel.events.DAOAggregateEvent;
|
||||||
@ -56,7 +54,6 @@ public class ViewsTypeFactory {
|
|||||||
|
|
||||||
private static final String FILE_TYPES_ICON = "org/sleuthkit/autopsy/images/file_types.png";
|
private static final String FILE_TYPES_ICON = "org/sleuthkit/autopsy/images/file_types.png";
|
||||||
private static final String SIZE_ICON = "org/sleuthkit/autopsy/images/file-size-16.png";
|
private static final String SIZE_ICON = "org/sleuthkit/autopsy/images/file-size-16.png";
|
||||||
private static final String SCORE_ICON = "org/sleuthkit/autopsy/images/red-circle-exclamation.png";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Node for file extensions parent in the tree.
|
* Node for file extensions parent in the tree.
|
||||||
@ -122,22 +119,6 @@ public class ViewsTypeFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parent of file size nodes in the tree.
|
|
||||||
*/
|
|
||||||
@Messages({"ViewsTypeFactory_ScoreParentNode_displayName=Score"})
|
|
||||||
private static class ScoreParentNode extends StaticTreeNode {
|
|
||||||
|
|
||||||
ScoreParentNode(Long dataSourceId) {
|
|
||||||
super(
|
|
||||||
"FILE_VIEW_SCORE_PARENT",
|
|
||||||
Bundle.ViewsTypeFactory_ScoreParentNode_displayName(),
|
|
||||||
SCORE_ICON,
|
|
||||||
new ScoreContentFactory(dataSourceId)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 'File Types' children in the tree.
|
* 'File Types' children in the tree.
|
||||||
*/
|
*/
|
||||||
@ -177,8 +158,7 @@ public class ViewsTypeFactory {
|
|||||||
super(ImmutableList.of(
|
super(ImmutableList.of(
|
||||||
new FileTypesParentNode(dataSourceId),
|
new FileTypesParentNode(dataSourceId),
|
||||||
new DeletedParentNode(dataSourceId),
|
new DeletedParentNode(dataSourceId),
|
||||||
new SizeParentNode(dataSourceId),
|
new SizeParentNode(dataSourceId)
|
||||||
new ScoreParentNode(dataSourceId)
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -651,108 +631,4 @@ public class ViewsTypeFactory {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The factory for creating deleted content tree nodes.
|
|
||||||
*/
|
|
||||||
public static class ScoreContentFactory extends TreeChildFactory<ScoreViewSearchParams> {
|
|
||||||
|
|
||||||
private final Long dataSourceId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main constructor.
|
|
||||||
*
|
|
||||||
* @param dataSourceId The data source to filter files to or null.
|
|
||||||
*/
|
|
||||||
public ScoreContentFactory(Long dataSourceId) {
|
|
||||||
this.dataSourceId = dataSourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected TreeNode<ScoreViewSearchParams> createNewNode(TreeResultsDTO.TreeItemDTO<? extends ScoreViewSearchParams> rowData) {
|
|
||||||
return new ScoreContentTypeNode(rowData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected TreeResultsDTO<? extends ScoreViewSearchParams> getChildResults() throws IllegalArgumentException, ExecutionException {
|
|
||||||
return MainDAO.getInstance().getViewsDAO().getScoreContentCounts(dataSourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void handleDAOAggregateEvent(DAOAggregateEvent aggEvt) {
|
|
||||||
for (DAOEvent evt : aggEvt.getEvents()) {
|
|
||||||
if (evt instanceof TreeEvent) {
|
|
||||||
TreeResultsDTO.TreeItemDTO<DeletedContentSearchParams> treeItem = super.getTypedTreeItem((TreeEvent) evt, DeletedContentSearchParams.class);
|
|
||||||
// if search params has null filter, trigger full refresh
|
|
||||||
if (treeItem != null && treeItem.getSearchParams().getFilter() == null) {
|
|
||||||
super.update();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.handleDAOAggregateEvent(aggEvt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected TreeResultsDTO.TreeItemDTO<? extends ScoreViewSearchParams> getOrCreateRelevantChild(TreeEvent treeEvt) {
|
|
||||||
TreeResultsDTO.TreeItemDTO<ScoreViewSearchParams> originalTreeItem = super.getTypedTreeItem(treeEvt, ScoreViewSearchParams.class);
|
|
||||||
|
|
||||||
if (originalTreeItem != null
|
|
||||||
// only create child if size filter is present (if null, update should be triggered separately)
|
|
||||||
&& originalTreeItem.getSearchParams().getFilter() != null
|
|
||||||
&& (this.dataSourceId == null || Objects.equals(this.dataSourceId, originalTreeItem.getSearchParams().getDataSourceId()))) {
|
|
||||||
|
|
||||||
// generate new type so that if it is a subtree event (i.e. keyword hits), the right tree item is created.
|
|
||||||
ScoreViewSearchParams searchParam = originalTreeItem.getSearchParams();
|
|
||||||
return new TreeResultsDTO.TreeItemDTO<>(
|
|
||||||
ScoreViewSearchParams.getTypeId(),
|
|
||||||
new ScoreViewSearchParams(searchParam.getFilter(), this.dataSourceId),
|
|
||||||
searchParam.getFilter(),
|
|
||||||
searchParam.getFilter().getDisplayName(),
|
|
||||||
originalTreeItem.getDisplayCount());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(TreeItemDTO<? extends ScoreViewSearchParams> o1, TreeItemDTO<? extends ScoreViewSearchParams> o2) {
|
|
||||||
return Integer.compare(o1.getSearchParams().getFilter().getId(), o2.getSearchParams().getFilter().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows a deleted content tree node.
|
|
||||||
*/
|
|
||||||
static class ScoreContentTypeNode extends TreeNode<ScoreViewSearchParams> {
|
|
||||||
|
|
||||||
private static final String BAD_SCORE_ICON = SCORE_ICON;
|
|
||||||
private static final String SUS_SCORE_ICON = "org/sleuthkit/autopsy/images/yellow-circle-yield.png";
|
|
||||||
|
|
||||||
private static String getIcon(ScoreViewFilter filter) {
|
|
||||||
switch (filter) {
|
|
||||||
case SUSPICIOUS:
|
|
||||||
return SUS_SCORE_ICON;
|
|
||||||
case BAD:
|
|
||||||
default:
|
|
||||||
return BAD_SCORE_ICON;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main constructor.
|
|
||||||
*
|
|
||||||
* @param itemData The data for the node.
|
|
||||||
*/
|
|
||||||
ScoreContentTypeNode(TreeResultsDTO.TreeItemDTO<? extends ScoreViewSearchParams> itemData) {
|
|
||||||
super("SCORE_CONTENT_" + itemData.getSearchParams().getFilter().name(), getIcon(itemData.getSearchParams().getFilter()), itemData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void respondSelection(DataResultTopComponent dataResultPanel) {
|
|
||||||
dataResultPanel.displayScoreContent(this.getItemData().getSearchParams());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user