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