Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
UserActivitySummary.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2020-2021 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.sleuthkit.autopsy.datasourcesummary.datamodel;
20
21import java.io.File;
22import java.util.ArrayList;
23import java.util.Arrays;
24import java.util.Collection;
25import java.util.Collections;
26import java.util.Comparator;
27import java.util.Date;
28import java.util.HashMap;
29import java.util.HashSet;
30import java.util.List;
31import java.util.Map;
32import java.util.Set;
33import java.util.function.Function;
34import java.util.logging.Level;
35import java.util.stream.Collectors;
36import java.util.stream.Stream;
37import org.apache.commons.lang3.StringUtils;
38import org.apache.commons.lang3.tuple.Pair;
39import org.openide.util.NbBundle.Messages;
40import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
41import org.sleuthkit.datamodel.BlackboardArtifact;
42import org.sleuthkit.datamodel.BlackboardAttribute;
43import org.sleuthkit.datamodel.DataSource;
44import org.sleuthkit.datamodel.TskCoreException;
45import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
46import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
47import org.sleuthkit.autopsy.texttranslation.TranslationException;
48import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
49import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
50
56public class UserActivitySummary {
57
62 private static final List<Function<List<String>, String>> SHORT_FOLDER_MATCHERS = Arrays.asList(
63 // handle Program Files and Program Files (x86) - if true, return the next folder
64 (pathList) -> {
65 if (pathList.size() < 2) {
66 return null;
67 }
68
69 String rootParent = pathList.get(0).toUpperCase();
70 if ("PROGRAM FILES".equals(rootParent) || "PROGRAM FILES (X86)".equals(rootParent)) {
71 return pathList.get(1);
72 } else {
73 return null;
74 }
75 },
76 // if there is a folder named "APPLICATION DATA" or "APPDATA"
77 (pathList) -> {
78 for (String pathEl : pathList) {
79 String uppered = pathEl.toUpperCase();
80 if ("APPLICATION DATA".equals(uppered) || "APPDATA".equals(uppered)) {
81 return "AppData";
82 }
83 }
84 return null;
85 }
86 );
87
88 private static final BlackboardArtifact.Type TYPE_DEVICE_ATTACHED = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED);
89 private static final BlackboardArtifact.Type TYPE_WEB_HISTORY = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_WEB_HISTORY);
90
91 private static final BlackboardAttribute.Type TYPE_DATETIME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME);
92 private static final BlackboardAttribute.Type TYPE_DATETIME_ACCESSED = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED);
93 private static final BlackboardAttribute.Type TYPE_DEVICE_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DEVICE_ID);
94 private static final BlackboardAttribute.Type TYPE_DEVICE_MAKE = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE);
95 private static final BlackboardAttribute.Type TYPE_DEVICE_MODEL = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL);
96 private static final BlackboardAttribute.Type TYPE_MESSAGE_TYPE = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE);
97 private static final BlackboardAttribute.Type TYPE_TEXT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_TEXT);
98 private static final BlackboardAttribute.Type TYPE_DATETIME_RCVD = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_RCVD);
99 private static final BlackboardAttribute.Type TYPE_DATETIME_SENT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_SENT);
100 private static final BlackboardAttribute.Type TYPE_DATETIME_START = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_START);
101 private static final BlackboardAttribute.Type TYPE_DATETIME_END = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_END);
102 private static final BlackboardAttribute.Type TYPE_DOMAIN = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN);
103 private static final BlackboardAttribute.Type TYPE_PROG_NAME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PROG_NAME);
104 private static final BlackboardAttribute.Type TYPE_PATH = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH);
105 private static final BlackboardAttribute.Type TYPE_COUNT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COUNT);
106
107 private static final String NTOS_BOOT_IDENTIFIER = "NTOSBOOT";
108 private static final String WINDOWS_PREFIX = "/WINDOWS";
109
110 private static final Comparator<TopAccountResult> TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccessed().compareTo(b.getLastAccessed());
111 private static final Comparator<TopWebSearchResult> TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccessed().compareTo(b.getLastAccessed());
112
117 private static final Comparator<TopProgramsResult> TOP_PROGRAMS_RESULT_COMPARE = (a, b) -> {
118 // first priority for sorting is the run times
119 // if non-0, this is the return value for the comparator
120 int runTimesCompare = nullableCompare(a.getRunTimes(), b.getRunTimes());
121 if (runTimesCompare != 0) {
122 return -runTimesCompare;
123 }
124
125 // second priority for sorting is the last run date
126 // if non-0, this is the return value for the comparator
127 int lastRunCompare = nullableCompare(
128 a.getLastAccessed() == null ? null : a.getLastAccessed().getTime(),
129 b.getLastAccessed() == null ? null : b.getLastAccessed().getTime());
130
131 if (lastRunCompare != 0) {
132 return -lastRunCompare;
133 }
134
135 // otherwise sort alphabetically
136 return (a.getProgramName() == null ? "" : a.getProgramName())
137 .compareToIgnoreCase((b.getProgramName() == null ? "" : b.getProgramName()));
138 };
139
140 private static final Set<String> DEVICE_EXCLUDE_LIST = new HashSet<>(Arrays.asList("ROOT_HUB", "ROOT_HUB20"));
141 private static final Set<String> DOMAIN_EXCLUDE_LIST = new HashSet<>(Arrays.asList("127.0.0.1", "LOCALHOST"));
142
143 private static final long MS_PER_DAY = 1000 * 60 * 60 * 24;
144 private static final long DOMAIN_WINDOW_DAYS = 30;
145 private static final long DOMAIN_WINDOW_MS = DOMAIN_WINDOW_DAYS * MS_PER_DAY;
146
149 private final java.util.logging.Logger logger;
150
156 org.sleuthkit.autopsy.coreutils.Logger.getLogger(UserActivitySummary.class.getName()));
157 }
158
169 SleuthkitCaseProvider provider,
171 java.util.logging.Logger logger) {
172
173 this.caseProvider = provider;
174 this.translationService = translationService;
175 this.logger = logger;
176 }
177
183 private static void assertValidCount(int count) {
184 if (count <= 0) {
185 throw new IllegalArgumentException("Count must be greater than 0");
186 }
187 }
188
197 public static String getShortFolderName(String strPath, String applicationName) {
198 if (strPath == null) {
199 return "";
200 }
201
202 List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
203
204 File file = new File(strPath);
205 while (file != null && org.apache.commons.lang.StringUtils.isNotBlank(file.getName())) {
206 pathEls.add(file.getName());
207 file = file.getParentFile();
208 }
209
210 Collections.reverse(pathEls);
211
212 for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
213 String result = matchEntry.apply(pathEls);
214 if (org.apache.commons.lang.StringUtils.isNotBlank(result)) {
215 return result;
216 }
217 }
218
219 return "";
220 }
221
232 public List<TopDomainsResult> getRecentDomains(DataSource dataSource, int count) throws TskCoreException, SleuthkitCaseProviderException {
233 assertValidCount(count);
234
235 if (dataSource == null) {
236 return Collections.emptyList();
237 }
238
239 Pair<Long, Map<String, List<Pair<BlackboardArtifact, Long>>>> mostRecentAndGroups = getDomainGroupsAndMostRecent(dataSource);
240 // if no recent domains, return accordingly
241 if (mostRecentAndGroups.getKey() == null || mostRecentAndGroups.getValue().size() == 0) {
242 return Collections.emptyList();
243 }
244
245 final long mostRecentMs = mostRecentAndGroups.getLeft();
246 Map<String, List<Pair<BlackboardArtifact, Long>>> groups = mostRecentAndGroups.getRight();
247
248 return groups.entrySet().stream()
249 .map(entry -> getDomainsResult(entry.getKey(), entry.getValue(), mostRecentMs))
250 .filter(result -> result != null)
251 // sort by number of visit times in those 30 days (max to min)
252 .sorted((a, b) -> -Long.compare(a.getVisitTimes(), b.getVisitTimes()))
253 // limit the result number to the parameter provided
254 .limit(count)
255 .collect(Collectors.toList());
256 }
257
270 private TopDomainsResult getDomainsResult(String domain, List<Pair<BlackboardArtifact, Long>> visits, long mostRecentMs) {
271 long visitCount = 0;
272 Long thisMostRecentMs = null;
273 BlackboardArtifact thisMostRecentArtifact = null;
274
275 for (Pair<BlackboardArtifact, Long> visitInstance : visits) {
276 BlackboardArtifact artifact = visitInstance.getLeft();
277 Long visitMs = visitInstance.getRight();
278 // make sure that visit is within window of mostRecentMS; otherwise skip it.
279 if (visitMs == null || visitMs + DOMAIN_WINDOW_MS < mostRecentMs) {
280 continue;
281 }
282
283 // if visit is within window, increment the count and get most recent
284 visitCount++;
285 if (thisMostRecentMs == null || visitMs > thisMostRecentMs) {
286 thisMostRecentMs = visitMs;
287 thisMostRecentArtifact = artifact;
288 }
289 thisMostRecentMs = getMax(thisMostRecentMs, visitMs);
290 }
291
292 // if there are no visits within the window, return null
293 if (visitCount <= 0 || thisMostRecentMs == null) {
294 return null;
295 } else {
296 // create a top domain result with the domain, count, and most recent visit date
297 return new TopDomainsResult(domain, visitCount, new Date(thisMostRecentMs), thisMostRecentArtifact);
298 }
299 }
300
315 private Pair<Long, Map<String, List<Pair<BlackboardArtifact, Long>>>> getDomainGroupsAndMostRecent(DataSource dataSource) throws TskCoreException, SleuthkitCaseProviderException {
316 List<BlackboardArtifact> artifacts = DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_WEB_HISTORY,
318
319 Long mostRecentMs = null;
320 Map<String, List<Pair<BlackboardArtifact, Long>>> domainVisits = new HashMap<>();
321
322 for (BlackboardArtifact art : artifacts) {
325
326 // if there isn't a last access date or domain for this artifact, it can be ignored.
327 // Also, ignore the loopback address.
328 if (artifactDateSecs == null || StringUtils.isBlank(domain) || DOMAIN_EXCLUDE_LIST.contains(domain.toUpperCase().trim())) {
329 continue;
330 }
331
332 Long artifactDateMs = artifactDateSecs * 1000;
333
334 // update the most recent visit date overall
335 mostRecentMs = getMax(mostRecentMs, artifactDateMs);
336
337 //Normalize the domain to lower case.
338 domain = domain.toLowerCase().trim();
339
340 // add this visit date to the list of dates for the domain
341 List<Pair<BlackboardArtifact, Long>> domainVisitList = domainVisits.get(domain);
342 if (domainVisitList == null) {
343 domainVisitList = new ArrayList<>();
344 domainVisits.put(domain, domainVisitList);
345 }
346
347 domainVisitList.add(Pair.of(art, artifactDateMs));
348 }
349
350 return Pair.of(mostRecentMs, domainVisits);
351 }
352
361 private static Long getMax(Long num1, Long num2) {
362 if (num1 == null) {
363 return num2;
364 } else if (num2 == null) {
365 return num1;
366 } else {
367 return num2 > num1 ? num2 : num1;
368 }
369 }
370
379 private static TopWebSearchResult getWebSearchResult(BlackboardArtifact artifact) {
380 String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT);
381 Date dateAccessed = DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME_ACCESSED);
382 return (StringUtils.isNotBlank(searchString) && dateAccessed != null)
383 ? new TopWebSearchResult(searchString, dateAccessed, artifact)
384 : null;
385 }
386
402 public List<TopWebSearchResult> getMostRecentWebSearches(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
403 assertValidCount(count);
404
405 if (dataSource == null) {
406 return Collections.emptyList();
407 }
408
409 // get the artifacts
410 List<BlackboardArtifact> webSearchArtifacts = caseProvider.get().getBlackboard()
411 .getArtifacts(ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), dataSource.getId());
412
413 // group by search string (case insensitive)
414 Collection<TopWebSearchResult> resultGroups = webSearchArtifacts
415 .stream()
416 // get items where search string and date is not null
418 // remove null records
419 .filter(result -> result != null)
420 // get the latest message for each search string
421 .collect(Collectors.toMap(
422 (result) -> result.getSearchString().toUpperCase(),
423 result -> result,
424 (result1, result2) -> TOP_WEBSEARCH_RESULT_DATE_COMPARE.compare(result1, result2) >= 0 ? result1 : result2))
425 .values();
426
427 // get the most recent date for each search term
428 List<TopWebSearchResult> results = resultGroups
429 .stream()
430 // get most recent searches first
431 .sorted(TOP_WEBSEARCH_RESULT_DATE_COMPARE.reversed())
432 .limit(count)
433 // get as list
434 .collect(Collectors.toList());
435
436 // get translation if possible
437 if (translationService.hasProvider()) {
438 for (TopWebSearchResult result : results) {
439 result.setTranslatedResult(getTranslationOrNull(result.getSearchString()));
440 }
441 }
442
443 return results;
444 }
445
455 private String getTranslationOrNull(String original) {
456 if (!translationService.hasProvider() || StringUtils.isBlank(original)) {
457 return null;
458 }
459
460 String translated = null;
461 try {
462 translated = translationService.translate(original);
464 logger.log(Level.WARNING, String.format("There was an error translating text: '%s'", original), ex);
465 }
466
467 // if there is no translation or the translation is the same as the original, return null.
468 if (StringUtils.isBlank(translated)
469 || translated.toUpperCase().trim().equals(original.toUpperCase().trim())) {
470
471 return null;
472 }
473
474 return translated;
475 }
476
487 if (r2.getLastAccessed()== null) {
488 return r1;
489 }
490
491 if (r1.getLastAccessed() == null) {
492 return r2;
493 }
494
495 return r1.getLastAccessed().compareTo(r2.getLastAccessed()) >= 0 ? r1 : r2;
496 }
497
512 public List<TopDeviceAttachedResult> getRecentDevices(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
513 assertValidCount(count);
514
515 if (dataSource == null) {
516 return Collections.emptyList();
517 }
518
519 Collection<TopDeviceAttachedResult> results = DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_DEVICE_ATTACHED,
521 .stream()
522 .map(artifact -> {
523 return new TopDeviceAttachedResult(
528 artifact
529 );
530 })
531 // remove Root Hub identifier
532 .filter(result -> {
533 return result.getDeviceId() == null
534 || result.getDeviceModel() == null
535 || !DEVICE_EXCLUDE_LIST.contains(result.getDeviceModel().trim().toUpperCase());
536 })
537 .collect(Collectors.toMap(result -> result.getDeviceId(), result -> result, (r1, r2) -> getMostRecentDevice(r1, r2)))
538 .values();
539
540 return results.stream()
541 .limit(count)
542 .collect(Collectors.toList());
543 }
544
553 private static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact) {
556 return (StringUtils.isNotBlank(type) && date != null)
557 ? new TopAccountResult(type, date, artifact)
558 : null;
559 }
560
572 private static TopAccountResult getAccountResult(BlackboardArtifact artifact, String messageType, BlackboardAttribute.Type... dateAttrs) {
573 String type = messageType;
574
575 Date latestDate = null;
576 if (dateAttrs != null) {
577 latestDate = Stream.of(dateAttrs)
578 .map((attr) -> DataSourceInfoUtilities.getDateOrNull(artifact, attr))
579 .filter((date) -> date != null)
580 .max((a, b) -> a.compareTo(b))
581 .orElse(null);
582 }
583
584 return (StringUtils.isNotBlank(type) && latestDate != null)
585 ? new TopAccountResult(type, latestDate, artifact)
586 : null;
587 }
588
603 @Messages({
604 "DataSourceUserActivitySummary_getRecentAccounts_emailMessage=Email Message",
605 "DataSourceUserActivitySummary_getRecentAccounts_calllogMessage=Call Log",})
606 public List<TopAccountResult> getRecentAccounts(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
607 assertValidCount(count);
608
609 if (dataSource == null) {
610 return Collections.emptyList();
611 }
612
613 Stream<TopAccountResult> messageResults = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(), dataSource.getId())
614 .stream()
615 .map((art) -> getMessageAccountResult(art));
616
617 Stream<TopAccountResult> emailResults = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(), dataSource.getId())
618 .stream()
619 .map((art) -> {
620 return getAccountResult(
621 art,
622 Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(),
625 });
626
627 Stream<TopAccountResult> calllogResults = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(), dataSource.getId())
628 .stream()
629 .map((art) -> {
630 return getAccountResult(
631 art,
632 Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(),
635 });
636
637 Stream<TopAccountResult> allResults = Stream.concat(messageResults, Stream.concat(emailResults, calllogResults));
638
639 // get them grouped by account type
640 Collection<TopAccountResult> groupedResults = allResults
641 // remove null records
642 .filter(result -> result != null)
643 // get these messages grouped by account type and get the most recent of each type
644 .collect(Collectors.toMap(
645 result -> result.getAccountType(),
646 result -> result,
647 (result1, result2) -> TOP_ACCOUNT_RESULT_DATE_COMPARE.compare(result1, result2) >= 0 ? result1 : result2))
648 .values();
649
650 // get account type sorted by most recent date
651 return groupedResults
652 .stream()
653 // get most recent accounts accessed
654 .sorted(TOP_ACCOUNT_RESULT_DATE_COMPARE.reversed())
655 // limit to count
656 .limit(count)
657 // get as list
658 .collect(Collectors.toList());
659 }
660
668 private TopProgramsResult getTopProgramsResult(BlackboardArtifact artifact) {
669 String programName = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PROG_NAME);
670
671 // ignore items with no name or a ntos boot identifier
672 if (StringUtils.isBlank(programName) || NTOS_BOOT_IDENTIFIER.equalsIgnoreCase(programName)) {
673 return null;
674 }
675
676 String path = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_PATH);
677
678 // ignore windows directory
679 if (StringUtils.startsWithIgnoreCase(path, WINDOWS_PREFIX)) {
680 return null;
681 }
682
683 Integer count = DataSourceInfoUtilities.getIntOrNull(artifact, TYPE_COUNT);
684 Long longCount = (count == null) ? null : (long) count;
685
686 return new TopProgramsResult(
687 programName,
688 path,
689 longCount,
691 artifact
692 );
693 }
694
703 private static Date getMax(Date date1, Date date2) {
704 if (date1 == null) {
705 return date2;
706 } else if (date2 == null) {
707 return date1;
708 } else {
709 return date1.compareTo(date2) > 0 ? date1 : date2;
710 }
711 }
712
722 private static int nullableCompare(Long long1, Long long2) {
723 if (long1 == null && long2 == null) {
724 return 0;
725 } else if (long1 != null && long2 == null) {
726 return 1;
727 } else if (long1 == null && long2 != null) {
728 return -1;
729 }
730
731 return Long.compare(long1, long2);
732 }
733
741 private static boolean isPositiveNum(Long longNum) {
742 return longNum != null && longNum > 0;
743 }
744
764 public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
765 assertValidCount(count);
766
767 if (dataSource == null) {
768 return Collections.emptyList();
769 }
770
771 // Get TopProgramsResults for each TSK_PROG_RUN artifact
772 Collection<TopProgramsResult> results = caseProvider.get().getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID(), dataSource.getId())
773 .stream()
774 // convert to a TopProgramsResult object or null if missing critical information
775 .map((art) -> getTopProgramsResult(art))
776 // remove any null items
777 .filter((res) -> res != null)
778 // group by the program name and program path
779 // The value will be a TopProgramsResult with the max run times
780 // and most recent last run date for each program name / program path pair.
781 .collect(Collectors.toMap(
782 res -> Pair.of(
783 res.getProgramName() == null ? null : res.getProgramName().toUpperCase(),
784 res.getProgramPath() == null ? null : res.getProgramPath().toUpperCase()),
785 res -> res,
786 (res1, res2) -> {
787 Long maxRunTimes = getMax(res1.getRunTimes(), res2.getRunTimes());
788 Date maxDate = getMax(res1.getLastAccessed(), res2.getLastAccessed());
789 TopProgramsResult maxResult = TOP_PROGRAMS_RESULT_COMPARE.compare(res1, res2) >= 0 ? res1 : res2;
790 return new TopProgramsResult(
791 maxResult.getProgramName(),
792 maxResult.getProgramPath(),
793 maxRunTimes,
794 maxDate,
795 maxResult.getArtifact());
796 })).values();
797
798 List<TopProgramsResult> orderedResults = results.stream()
800 .collect(Collectors.toList());
801
802 // only limit the list to count if there is no last run date and no run times.
803 if (!orderedResults.isEmpty()) {
804 TopProgramsResult topResult = orderedResults.get(0);
805 // if run times / last run information is available, the first item should have some value,
806 // and then the items should be limited accordingly.
807 if (isPositiveNum(topResult.getRunTimes())
808 || (topResult.getLastAccessed() != null && isPositiveNum(topResult.getLastAccessed().getTime()))) {
809 return orderedResults.stream().limit(count).collect(Collectors.toList());
810 }
811 }
812
813 // otherwise return the alphabetized list with no limit applied.
814 return orderedResults;
815 }
816
821 public static class LastAccessedArtifact {
822
823 private final Date lastAccessed;
824 private final BlackboardArtifact artifact;
825
832 public LastAccessedArtifact(Date lastAccessed, BlackboardArtifact artifact) {
833 this.lastAccessed = lastAccessed;
834 this.artifact = artifact;
835 }
836
840 public Date getLastAccessed() {
841 return lastAccessed;
842 }
843
847 public BlackboardArtifact getArtifact() {
848 return artifact;
849 }
850 }
851
855 public static class TopWebSearchResult extends LastAccessedArtifact {
856
857 private final String searchString;
858 private String translatedResult;
859
867 public TopWebSearchResult(String searchString, Date dateAccessed, BlackboardArtifact artifact) {
868 super(dateAccessed, artifact);
869 this.searchString = searchString;
870 }
871
875 public String getTranslatedResult() {
876 return translatedResult;
877 }
878
885 this.translatedResult = translatedResult;
886 }
887
891 public String getSearchString() {
892 return searchString;
893 }
894 }
895
899 public static class TopDeviceAttachedResult extends LastAccessedArtifact {
900
901 private final String deviceId;
902 private final String deviceMake;
903 private final String deviceModel;
904
914 public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, BlackboardArtifact artifact) {
915 super(dateAccessed, artifact);
916 this.deviceId = deviceId;
917 this.deviceMake = deviceMake;
918 this.deviceModel = deviceModel;
919 }
920
924 public String getDeviceId() {
925 return deviceId;
926 }
927
931 public String getDeviceMake() {
932 return deviceMake;
933 }
934
938 public String getDeviceModel() {
939 return deviceModel;
940 }
941 }
942
947 public static class TopAccountResult extends LastAccessedArtifact {
948
949 private final String accountType;
950
958 public TopAccountResult(String accountType, Date lastAccess, BlackboardArtifact artifact) {
959 super(lastAccess, artifact);
960 this.accountType = accountType;
961 }
962
966 public String getAccountType() {
967 return accountType;
968 }
969 }
970
974 public static class TopDomainsResult extends LastAccessedArtifact {
975
976 private final String domain;
977 private final Long visitTimes;
978
987 public TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact) {
988 super(lastVisit, artifact);
989 this.domain = domain;
990 this.visitTimes = visitTimes;
991 }
992
996 public String getDomain() {
997 return domain;
998 }
999
1003 public Long getVisitTimes() {
1004 return visitTimes;
1005 }
1006 }
1007
1011 public static class TopProgramsResult extends LastAccessedArtifact {
1012
1013 private final String programName;
1014 private final String programPath;
1015 private final Long runTimes;
1016
1025 TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun, BlackboardArtifact artifact) {
1026 super(lastRun, artifact);
1027 this.programName = programName;
1028 this.programPath = programPath;
1029 this.runTimes = runTimes;
1030 }
1031
1035 public String getProgramName() {
1036 return programName;
1037 }
1038
1042 public String getProgramPath() {
1043 return programPath;
1044 }
1045
1049 public Long getRunTimes() {
1050 return runTimes;
1051 }
1052 }
1053}
static String getStringOrNull(BlackboardArtifact artifact, Type attributeType)
static Long getLongOrNull(BlackboardArtifact artifact, Type attributeType)
static List< BlackboardArtifact > getArtifacts(SleuthkitCase skCase, BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType, SortOrder sortOrder)
static Date getDateOrNull(BlackboardArtifact artifact, Type attributeType)
static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType)
TopAccountResult(String accountType, Date lastAccess, BlackboardArtifact artifact)
TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, BlackboardArtifact artifact)
TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact)
TopWebSearchResult(String searchString, Date dateAccessed, BlackboardArtifact artifact)
TopDomainsResult getDomainsResult(String domain, List< Pair< BlackboardArtifact, Long > > visits, long mostRecentMs)
List< TopDeviceAttachedResult > getRecentDevices(DataSource dataSource, int count)
UserActivitySummary(SleuthkitCaseProvider provider, TextTranslationService translationService, java.util.logging.Logger logger)
List< TopDomainsResult > getRecentDomains(DataSource dataSource, int count)
TopDeviceAttachedResult getMostRecentDevice(TopDeviceAttachedResult r1, TopDeviceAttachedResult r2)
static final List< Function< List< String >, String > > SHORT_FOLDER_MATCHERS
static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact)
Pair< Long, Map< String, List< Pair< BlackboardArtifact, Long > > > > getDomainGroupsAndMostRecent(DataSource dataSource)
static String getShortFolderName(String strPath, String applicationName)
static final Comparator< TopAccountResult > TOP_ACCOUNT_RESULT_DATE_COMPARE
static TopWebSearchResult getWebSearchResult(BlackboardArtifact artifact)
List< TopWebSearchResult > getMostRecentWebSearches(DataSource dataSource, int count)
static final Comparator< TopWebSearchResult > TOP_WEBSEARCH_RESULT_DATE_COMPARE
List< TopProgramsResult > getTopPrograms(DataSource dataSource, int count)
static TopAccountResult getAccountResult(BlackboardArtifact artifact, String messageType, BlackboardAttribute.Type... dateAttrs)
List< TopAccountResult > getRecentAccounts(DataSource dataSource, int count)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.