19 package org.sleuthkit.autopsy.discovery.search;
21 import com.google.common.cache.CacheLoader;
22 import java.sql.ResultSet;
23 import java.sql.SQLException;
24 import java.time.Instant;
25 import java.time.temporal.ChronoUnit;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.List;
30 import java.util.HashSet;
32 import java.util.Optional;
33 import java.util.StringJoiner;
34 import org.apache.commons.lang3.tuple.Pair;
42 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD;
43 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY;
44 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN;
46 import org.
sleuthkit.datamodel.CaseDbAccessManager.CaseDbAccessQueryCallback;
56 class DomainSearchCacheLoader
extends CacheLoader<SearchKey, Map<GroupKey, List<Result>>> {
59 public Map<GroupKey, List<Result>> load(SearchKey key)
throws DiscoveryException, SQLException, TskCoreException {
61 List<Result> domainResults = getResultDomainsFromDatabase(key);
64 for (AbstractFilter filter : key.getFilters()) {
65 if (filter.useAlternateFilter()) {
66 domainResults = filter.applyAlternateFilter(domainResults, key.getSleuthkitCase(), key.getCentralRepository());
73 List<AttributeType> searchAttributes =
new ArrayList<>();
74 searchAttributes.add(key.getGroupAttributeType());
75 searchAttributes.addAll(key.getFileSortingMethod().getRequiredAttributes());
77 for (AttributeType attr : searchAttributes) {
78 attr.addAttributeToResults(domainResults,
79 key.getSleuthkitCase(), key.getCentralRepository());
83 final SearchResults searchResults =
new SearchResults(
84 key.getGroupSortingType(),
85 key.getGroupAttributeType(),
86 key.getFileSortingMethod());
87 searchResults.add(domainResults);
88 return searchResults.toLinkedHashMap();
99 List<Result> getResultDomainsFromDatabase(SearchKey key)
throws TskCoreException, SQLException, DiscoveryException {
103 final Pair<String, String> filterClauses = createWhereAndHavingClause(key.getFilters());
104 final String whereClause = filterClauses.getLeft();
105 final String havingClause = filterClauses.getRight();
112 final String domainsTable
113 =
"SELECT LOWER(MAX(value_text)) AS domain,"
114 +
" MAX(value_int64) AS date,"
115 +
" artifact_id AS parent_artifact_id,"
116 +
" MAX(artifact_type_id) AS parent_artifact_type_id "
117 +
"FROM blackboard_attributes "
118 +
"WHERE " + whereClause +
" "
119 +
"GROUP BY artifact_id "
120 +
"HAVING " + havingClause;
123 final Instant currentTime = Instant.now();
124 final Instant sixtyDaysAgo = currentTime.minus(60, ChronoUnit.DAYS);
128 final AttributeType groupAttribute = key.getGroupAttributeType();
129 final String groupByClause = (groupAttribute instanceof DataSourceAttribute)
130 ?
"data_source_obj_id, domain" :
"domain";
132 final Optional<AbstractFilter> dataSourceFilter = key.getFilters().stream()
133 .filter(filter -> filter instanceof DataSourceFilter)
136 String dataSourceWhereClause = null;
137 if (dataSourceFilter.isPresent()) {
138 dataSourceWhereClause = dataSourceFilter.get().getWhereClause();
143 final String domainsQuery
147 +
" MIN(date) AS activity_start,"
148 +
" MAX(date) AS activity_end,"
150 +
" WHEN artifact_type_id = " + TSK_WEB_DOWNLOAD.getTypeID() +
" THEN 1 "
152 +
" END) AS fileDownloads,"
154 +
" WHEN artifact_type_id = " + TSK_WEB_HISTORY.getTypeID() +
" THEN 1 "
156 +
" END) AS totalVisits,"
158 +
" WHEN artifact_type_id = " + TSK_WEB_HISTORY.getTypeID() +
" AND"
159 +
" date BETWEEN " + sixtyDaysAgo.getEpochSecond() +
" AND " + currentTime.getEpochSecond() +
" THEN 1 "
162 +
" MAX(data_source_obj_id) AS dataSource "
163 +
"FROM blackboard_artifacts"
164 +
" JOIN (" + domainsTable +
") AS domains_table"
165 +
" ON artifact_id = parent_artifact_id "
167 ((dataSourceWhereClause != null) ?
"WHERE " + dataSourceWhereClause +
" " :
"")
168 +
"GROUP BY " + groupByClause;
170 final SleuthkitCase caseDb = key.getSleuthkitCase();
171 final CaseDbAccessManager dbManager = caseDb.getCaseDbAccessManager();
173 final DomainCallback domainCallback =
new DomainCallback(caseDb);
174 dbManager.select(domainsQuery, domainCallback);
176 if (domainCallback.getSQLException() != null) {
177 throw domainCallback.getSQLException();
180 if (domainCallback.getTskCoreException() != null) {
181 throw domainCallback.getTskCoreException();
184 return domainCallback.getResultDomains();
200 Pair<String, String> createWhereAndHavingClause(List<AbstractFilter> filters) {
201 final StringJoiner whereClause =
new StringJoiner(
" OR ");
202 final StringJoiner havingClause =
new StringJoiner(
" AND ");
204 String artifactTypeFilter = null;
205 boolean hasDateTimeFilter =
false;
207 for (AbstractFilter filter : filters) {
208 if (filter instanceof ArtifactTypeFilter) {
209 artifactTypeFilter = filter.getWhereClause();
210 }
else if (!(filter instanceof DataSourceFilter) && !filter.useAlternateFilter()) {
211 if (filter instanceof ArtifactDateRangeFilter) {
212 hasDateTimeFilter =
true;
215 whereClause.add(
"(" + filter.getWhereClause() +
")");
216 havingClause.add(
"SUM(CASE WHEN " + filter.getWhereClause() +
" THEN 1 ELSE 0 END) > 0");
220 if (!hasDateTimeFilter) {
221 whereClause.add(ArtifactDateRangeFilter.createAttributeTypeClause());
224 String domainAttributeFilter =
"attribute_type_id = " + TSK_DOMAIN.getTypeID()
225 +
" AND value_text <> ''";
227 whereClause.add(
"(" + domainAttributeFilter +
")");
228 havingClause.add(
"SUM(CASE WHEN " + domainAttributeFilter +
" THEN 1 ELSE 0 END) > 0");
231 whereClause.toString() + ((artifactTypeFilter != null) ?
" AND (" + artifactTypeFilter +
")" :
""),
232 havingClause.toString()
244 private final SleuthkitCase
skc;
259 this.resultDomains =
new ArrayList<>();
266 resultSet.setFetchSize(500);
268 while (resultSet.next()) {
269 String domain = resultSet.getString(
"domain");
271 if (bannedDomains.contains(domain)) {
277 Long activityStart = resultSet.getLong(
"activity_start");
278 if (resultSet.wasNull()) {
279 activityStart = null;
281 Long activityEnd = resultSet.getLong(
"activity_end");
282 if (resultSet.wasNull()) {
285 Long filesDownloaded = resultSet.getLong(
"fileDownloads");
286 if (resultSet.wasNull()) {
287 filesDownloaded = null;
289 Long totalVisits = resultSet.getLong(
"totalVisits");
290 if (resultSet.wasNull()) {
294 Long visitsInLast60 = resultSet.getLong(
"last60");
295 if (resultSet.wasNull()) {
296 visitsInLast60 = null;
298 Long dataSourceID = resultSet.getLong(
"dataSource");
300 Content dataSource = skc.getContentById(dataSourceID);
302 resultDomains.add(
new ResultDomain(domain, activityStart,
303 activityEnd, totalVisits, visitsInLast60, filesDownloaded, dataSource));
305 }
catch (SQLException ex) {
307 }
catch (TskCoreException ex) {
320 return Collections.unmodifiableList(this.resultDomains);
void process(ResultSet resultSet)
DomainCallback(SleuthkitCase skc)
TskCoreException getTskCoreException()
final Set< String > bannedDomains
final List< Result > resultDomains
SQLException getSQLException()
TskCoreException coreCause
List< Result > getResultDomains()