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.Arrays;
28 import java.util.Collections;
29 import java.util.List;
31 import java.util.HashSet;
33 import java.util.Optional;
34 import java.util.StringJoiner;
35 import org.apache.commons.lang3.tuple.Pair;
43 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD;
44 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY;
45 import static org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_ACCOUNT_TYPE;
46 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN;
48 import org.
sleuthkit.datamodel.CaseDbAccessManager.CaseDbAccessQueryCallback;
58 class DomainSearchCacheLoader
extends CacheLoader<SearchKey, Map<GroupKey, List<Result>>> {
61 public Map<GroupKey, List<Result>> load(SearchKey key)
throws DiscoveryException, SQLException, TskCoreException, InterruptedException {
62 List<Result> domainResults = getResultDomainsFromDatabase(key);
66 Set<AttributeType> searchAttributes =
new HashSet<>();
67 searchAttributes.add(key.getGroupAttributeType());
68 searchAttributes.addAll(key.getFileSortingMethod().getRequiredAttributes());
69 for (AttributeType attr : searchAttributes) {
70 if (Thread.currentThread().isInterrupted()) {
71 throw new InterruptedException();
73 attr.addAttributeToResults(domainResults,
74 key.getSleuthkitCase(), key.getCentralRepository());
77 for (AbstractFilter filter : key.getFilters()) {
78 if (Thread.currentThread().isInterrupted()) {
79 throw new InterruptedException();
81 if (filter.useAlternateFilter()) {
82 domainResults = filter.applyAlternateFilter(domainResults, key.getSleuthkitCase(), key.getCentralRepository());
86 final SearchResults searchResults =
new SearchResults(
87 key.getGroupSortingType(),
88 key.getGroupAttributeType(),
89 key.getFileSortingMethod());
90 searchResults.add(domainResults);
91 return searchResults.toLinkedHashMap();
102 List<Result> getResultDomainsFromDatabase(SearchKey key)
throws TskCoreException, SQLException, DiscoveryException, InterruptedException {
106 final Pair<String, String> filterClauses = createWhereAndHavingClause(key.getFilters());
107 final String whereClause = filterClauses.getLeft();
108 final String havingClause = filterClauses.getRight();
115 final String domainsTable
116 =
"SELECT LOWER(MAX(value_text)) AS domain,"
117 +
" MAX(value_int64) AS date,"
118 +
" artifact_id AS parent_artifact_id,"
119 +
" MAX(artifact_type_id) AS parent_artifact_type_id "
120 +
"FROM blackboard_attributes "
121 +
"WHERE " + whereClause +
" "
122 +
"GROUP BY artifact_id "
123 +
"HAVING " + havingClause;
126 final Instant currentTime = Instant.now();
127 final Instant sixtyDaysAgo = currentTime.minus(60, ChronoUnit.DAYS);
131 final AttributeType groupAttribute = key.getGroupAttributeType();
132 final String groupByClause = (groupAttribute instanceof DataSourceAttribute)
133 ?
"data_source_obj_id, domain" :
"domain";
135 final Optional<AbstractFilter> dataSourceFilter = key.getFilters().stream()
136 .filter(filter -> filter instanceof DataSourceFilter)
139 String dataSourceWhereClause = null;
140 if (dataSourceFilter.isPresent()) {
141 dataSourceWhereClause = dataSourceFilter.get().getWhereClause();
146 final String domainsQuery
150 +
" MIN(date) AS activity_start,"
151 +
" MAX(date) AS activity_end,"
153 +
" WHEN artifact_type_id = " + TSK_WEB_DOWNLOAD.getTypeID() +
" THEN 1 "
155 +
" END) AS fileDownloads,"
157 +
" WHEN artifact_type_id = " + TSK_WEB_HISTORY.getTypeID() +
" THEN 1 "
159 +
" END) AS totalPageViews,"
161 +
" WHEN artifact_type_id = " + TSK_WEB_HISTORY.getTypeID() +
" AND"
162 +
" date BETWEEN " + sixtyDaysAgo.getEpochSecond() +
" AND " + currentTime.getEpochSecond() +
" THEN 1 "
164 +
" END) AS pageViewsInLast60,"
166 +
" WHEN artifact_type_id = " + TSK_WEB_ACCOUNT_TYPE.getTypeID() +
" THEN 1 "
168 +
" END) AS countOfKnownAccountTypes,"
169 +
" MAX(data_source_obj_id) AS dataSource "
170 +
"FROM blackboard_artifacts"
171 +
" JOIN (" + domainsTable +
") AS domains_table"
172 +
" ON artifact_id = parent_artifact_id "
174 ((dataSourceWhereClause != null) ?
"WHERE " + dataSourceWhereClause +
" " :
"")
175 +
"GROUP BY " + groupByClause;
177 final SleuthkitCase caseDb = key.getSleuthkitCase();
178 final CaseDbAccessManager dbManager = caseDb.getCaseDbAccessManager();
180 final DomainCallback domainCallback =
new DomainCallback(caseDb);
181 dbManager.select(domainsQuery, domainCallback);
183 if (domainCallback.getSQLException() != null) {
184 throw domainCallback.getSQLException();
187 if (domainCallback.getTskCoreException() != null) {
188 throw domainCallback.getTskCoreException();
191 if (domainCallback.getInterruptedException() != null) {
192 throw domainCallback.getInterruptedException();
195 return domainCallback.getResultDomains();
211 Pair<String, String> createWhereAndHavingClause(List<AbstractFilter> filters) {
212 final StringJoiner whereClause =
new StringJoiner(
" OR ");
213 final StringJoiner havingClause =
new StringJoiner(
" AND ");
216 ArtifactTypeFilter artifactTypeFilter =
new ArtifactTypeFilter(SearchData.Type.DOMAIN.getArtifactTypes());
217 boolean hasDateTimeFilter =
false;
219 for (AbstractFilter filter : filters) {
220 if (filter instanceof ArtifactTypeFilter) {
222 artifactTypeFilter = ((ArtifactTypeFilter) filter);
223 }
else if (!(filter instanceof DataSourceFilter) && !filter.useAlternateFilter()) {
224 if (filter instanceof ArtifactDateRangeFilter) {
225 hasDateTimeFilter =
true;
228 whereClause.add(
"(" + filter.getWhereClause() +
")");
229 havingClause.add(
"SUM(CASE WHEN " + filter.getWhereClause() +
" THEN 1 ELSE 0 END) > 0");
233 if (!hasDateTimeFilter) {
234 whereClause.add(ArtifactDateRangeFilter.createAttributeTypeClause());
237 String domainAttributeFilter =
"attribute_type_id = " + TSK_DOMAIN.getTypeID()
238 +
" AND value_text <> ''";
240 whereClause.add(
"(" + domainAttributeFilter +
")");
241 havingClause.add(
"SUM(CASE WHEN " + domainAttributeFilter +
" THEN 1 ELSE 0 END) > 0");
244 whereClause.toString() +
" AND (" + artifactTypeFilter.getWhereClause(Arrays.asList(TSK_WEB_ACCOUNT_TYPE)) +
")",
245 havingClause.toString()
257 private final SleuthkitCase
skc;
275 this.resultDomains =
new ArrayList<>();
282 resultSet.setFetchSize(500);
284 while (resultSet.next()) {
285 if (Thread.currentThread().isInterrupted()) {
286 throw new InterruptedException();
289 String domain = resultSet.getString(
"domain");
291 if (bannedDomains.contains(domain)) {
297 long activityStart = resultSet.getLong(
"activity_start");
298 long activityEnd = resultSet.getLong(
"activity_end");
299 long filesDownloaded = resultSet.getLong(
"fileDownloads");
300 long totalPageViews = resultSet.getLong(
"totalPageViews");
301 long pageViewsInLast60 = resultSet.getLong(
"pageViewsInLast60");
302 long countOfKnownAccountTypes = resultSet.getLong(
"countOfKnownAccountTypes");
303 long dataSourceID = resultSet.getLong(
"dataSource");
304 Content dataSource = skc.getContentById(dataSourceID);
306 resultDomains.add(
new ResultDomain(domain, activityStart,
307 activityEnd, totalPageViews, pageViewsInLast60, filesDownloaded,
308 countOfKnownAccountTypes, dataSource));
310 }
catch (SQLException ex) {
312 }
catch (TskCoreException ex) {
314 }
catch (InterruptedException ex) {
315 this.interruptedException = ex;
327 return Collections.unmodifiableList(this.resultDomains);
void process(ResultSet resultSet)
DomainCallback(SleuthkitCase skc)
TskCoreException getTskCoreException()
final Set< String > bannedDomains
final List< Result > resultDomains
SQLException getSQLException()
InterruptedException getInterruptedException()
TskCoreException coreCause
List< Result > getResultDomains()
InterruptedException interruptedException