From 1dee32d4eda41ab7a80b4a1d1cc8531a8bdac488 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 3 Dec 2020 13:39:08 -0500 Subject: [PATCH 01/19] Added hardware acceleration option --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index fc25cba14d..8a07f589c8 100644 --- a/build.xml +++ b/build.xml @@ -156,7 +156,7 @@ - + From 6240c1772b8e7b842a417bcbd55e3e749d744de3 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 16 Dec 2020 10:43:55 -0500 Subject: [PATCH 02/19] Enhanced the page views group by option --- .../discovery/search/Bundle.properties-MERGED | 7 +-- .../discovery/search/DiscoveryAttributes.java | 4 +- .../discovery/search/DiscoveryKeyUtils.java | 18 +++----- .../autopsy/discovery/search/SearchData.java | 46 +++++++++++++++++++ 4 files changed, 59 insertions(+), 16 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED index bb72ee0090..925cfa37a8 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED @@ -27,9 +27,6 @@ DiscoveryKeyUtils.KeywordListGroupKey.noKeywords=None DiscoveryKeyUtils.MostRecentActivityDateGroupKey.noDate=No Date Available DiscoveryKeyUtils.NoGroupingGroupKey.allFiles=All Files DiscoveryKeyUtils.ObjectDetectedGroupKey.noSets=None -# {0} - totalVisits -DiscoveryKeyUtils.PageViewsGroupKey.displayName={0} page views -DiscoveryKeyUtils.PageViewsGroupKey.noVisits=No page views # {0} - domain # {1} - artifactType DomainSearchArtifactsRequest.toString.text=Domain: {0} ArtifactType: {1} @@ -92,6 +89,10 @@ SearchData.Frequency.unique.displayName=Unique (1) SearchData.Frequency.unknown.displayName=Unknown SearchData.Frequency.verycommon.displayName=Very Common (100+) SearchData.notPrevNotable.displayName=Previously Not Notable +SearchData.PageViews.over1000=1000+ page views +# {0} - minValue +# {1} - maxValue +SearchData.PageViews.rangeTemplate={0}-{1} page views SearchData.prevNotable.displayName=Previously Notable SearchData.Score.interesting.displayName=Interesting SearchData.Score.notable.displayName=Notable diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryAttributes.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryAttributes.java index 4beb9eed17..d935fb6e52 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryAttributes.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryAttributes.java @@ -986,9 +986,9 @@ public class DiscoveryAttributes { */ public static List getOptionsForGroupingForDomains() { if (CentralRepository.isEnabled()) { - return Arrays.asList(FREQUENCY, MOST_RECENT_DATE, FIRST_DATE, PAGE_VIEWS, PREVIOUSLY_NOTABLE, DOMAIN_CATEGORY); + return Arrays.asList(PAGE_VIEWS, FREQUENCY, MOST_RECENT_DATE, FIRST_DATE, PREVIOUSLY_NOTABLE, DOMAIN_CATEGORY); } else { - return Arrays.asList(MOST_RECENT_DATE, FIRST_DATE, PAGE_VIEWS, DOMAIN_CATEGORY); + return Arrays.asList(PAGE_VIEWS, MOST_RECENT_DATE, FIRST_DATE, DOMAIN_CATEGORY); } } } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryKeyUtils.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryKeyUtils.java index 3ef553be57..fd1b77dd44 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryKeyUtils.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryKeyUtils.java @@ -29,6 +29,7 @@ import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.discovery.search.SearchData.PageViews; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; @@ -1316,28 +1317,23 @@ public class DiscoveryKeyUtils { static class PageViewsGroupKey extends GroupKey { private final String displayName; - private final Long pageViews; + private final PageViews pageViews; /** * Construct a new NumberOfVisitsGroupKey. * * @param result The Result to create the group key for. */ - @NbBundle.Messages({ - "# {0} - totalVisits", - "DiscoveryKeyUtils.PageViewsGroupKey.displayName={0} page views", - "DiscoveryKeyUtils.PageViewsGroupKey.noVisits=No page views"}) PageViewsGroupKey(Result result) { if (result instanceof ResultDomain) { Long totalPageViews = ((ResultDomain) result).getTotalPageViews(); if (totalPageViews == null) { totalPageViews = 0L; } - pageViews = totalPageViews; - displayName = Bundle.DiscoveryKeyUtils_PageViewsGroupKey_displayName(Long.toString(pageViews)); + pageViews = PageViews.fromPageViewCount(totalPageViews); + displayName = pageViews.toString(); } else { - displayName = Bundle.DiscoveryKeyUtils_PageViewsGroupKey_noVisits(); - pageViews = -1L; + throw new IllegalArgumentException("Expected a domain instance only."); } } @@ -1356,7 +1352,7 @@ public class DiscoveryKeyUtils { * * @return The number of page views this group is for. */ - Long getPageViews() { + PageViews getPageViews() { return pageViews; } @@ -1378,7 +1374,7 @@ public class DiscoveryKeyUtils { public int compareTo(GroupKey otherGroupKey) { if (otherGroupKey instanceof PageViewsGroupKey) { PageViewsGroupKey pageViewsKey = (PageViewsGroupKey) otherGroupKey; - return Long.compare(getPageViews(), pageViewsKey.getPageViews()); + return getPageViews().compareTo(pageViewsKey.getPageViews()); } else { return compareClassNames(otherGroupKey); } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/SearchData.java b/Core/src/org/sleuthkit/autopsy/discovery/search/SearchData.java index 05e89f1220..b59556015a 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/SearchData.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/SearchData.java @@ -67,6 +67,52 @@ public final class SearchData { return displayName; } } + + @NbBundle.Messages({ + "# {0} - minValue", + "# {1} - maxValue", + "SearchData.PageViews.rangeTemplate={0}-{1} page views", + "SearchData.PageViews.over1000=1000+ page views" + }) + public enum PageViews { + OVER_1000(1001, Long.MAX_VALUE), // ranking, minValue, maxValue + UP_TO_1000(501, 1000), + UP_TO_500(101, 500), + UP_TO_100(51, 100), + UP_TO_50(11, 50), + UP_TO_10(0, 10); + + private final long minValue; + private final long maxValue; + + PageViews(long minValue, long maxValue) { + this.maxValue = maxValue; + this.minValue = minValue; + } + + @Override + public String toString() { + if (this == PageViews.OVER_1000) { + return Bundle.SearchData_PageViews_over1000(); + } else { + return Bundle.SearchData_PageViews_rangeTemplate(Long.toString(minValue), Long.toString(maxValue)); + } + } + + boolean covers(long count) { + return count >= minValue && count <= maxValue; + } + + public static PageViews fromPageViewCount(long count) { + for (PageViews view : PageViews.values()) { + if (view.covers(count)) { + return view; + } + } + return null; + } + } + /** * Enum representing how often the result occurs in the Central Repository. */ From c6027c9b5f80b5466fbeb5860fe90e6cd31f0c06 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Wed, 16 Dec 2020 14:58:29 -0500 Subject: [PATCH 03/19] Update ExtractRegistry.java Add data to ignore in trust records from regripper output. --- .../org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index 13ede70c90..bdcd9fbe30 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -1784,7 +1784,8 @@ class ExtractRegistry extends Extract { line = line.trim(); usedTime = Long.valueOf(0); if (!line.contains("**") && !line.contains("----------") && !line.contains("LastWrite") - && !line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("TrustRecords")) { + && !line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("TrustRecords") + && !line.contains("VBAWarnings =")) { // Columns are // Date : / // Split line on " : " which is the record delimiter between position and file From 477d8c6d720d06875f7017aa429d2bcd4a76238c Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 16 Dec 2020 17:38:47 -0500 Subject: [PATCH 04/19] Implemented the DoD, tested and commented the code --- .../discovery/search/Bundle.properties-MERGED | 6 +- .../discovery/search/DiscoveryKeyUtils.java | 142 +++++++----------- .../discovery/search/DomainSearch.java | 6 +- .../search/DomainSearchCacheLoader.java | 9 +- .../discovery/search/ResultDomain.java | 2 +- .../autopsy/discovery/search/SearchData.java | 8 +- .../discovery/search/SearchFiltering.java | 9 +- .../discovery/ui/Bundle.properties-MERGED | 12 ++ .../discovery/ui/DomainSummaryPanel.java | 29 +++- .../discovery/ui/MonthAbbreviation.java | 80 ++++++++++ 10 files changed, 195 insertions(+), 108 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/discovery/ui/MonthAbbreviation.java diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED index 6a029f0759..cd823d353b 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/Bundle.properties-MERGED @@ -1,3 +1,7 @@ +# {0} - month abbreviation +# {1} - day of month +# {2} - year +DiscoveryAttributes.ActivityDateGroupKey.getDisplayNameTemplate=Week of {0} {1}, {2} DiscoveryAttributes.GroupingAttributeType.datasource.displayName=Data Source DiscoveryAttributes.GroupingAttributeType.fileType.displayName=File Type DiscoveryAttributes.GroupingAttributeType.firstDate.displayName=First Activity Date @@ -20,11 +24,9 @@ DiscoveryKeyUtils.DataSourceGroupKey.datasourceAndID={0}(ID: {1}) # {0} - Data source ID DiscoveryKeyUtils.DataSourceGroupKey.idOnly=Data source (ID: {0}) DiscoveryKeyUtils.FileTagGroupKey.noSets=None -DiscoveryKeyUtils.FirstActivityDateGroupKey.noDate=No Date Available DiscoveryKeyUtils.HashHitsGroupKey.noHashHits=None DiscoveryKeyUtils.InterestingItemGroupKey.noSets=None DiscoveryKeyUtils.KeywordListGroupKey.noKeywords=None -DiscoveryKeyUtils.LastActivityDateGroupKey.noDate=No Date Available DiscoveryKeyUtils.NoGroupingGroupKey.allFiles=All Files DiscoveryKeyUtils.ObjectDetectedGroupKey.noSets=None # {0} - domain diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryKeyUtils.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryKeyUtils.java index d55242c191..a3c8b4a584 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryKeyUtils.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DiscoveryKeyUtils.java @@ -18,18 +18,21 @@ */ package org.sleuthkit.autopsy.discovery.search; -import java.text.SimpleDateFormat; +import java.time.DayOfWeek; +import java.time.Instant; +import java.time.ZonedDateTime; +import java.time.temporal.TemporalAdjusters; import java.util.Collections; -import java.util.Date; import java.util.List; -import java.util.Locale; import java.util.Objects; -import java.util.concurrent.TimeUnit; +import java.util.TimeZone; import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.discovery.search.SearchData.PageViews; +import org.sleuthkit.autopsy.discovery.ui.MonthAbbreviation; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; @@ -1134,29 +1137,34 @@ public class DiscoveryKeyUtils { */ static class LastActivityDateGroupKey extends GroupKey { - private final Long epochDate; - private final String dateNameString; + private ZonedDateTime currentWeekCutOff; /** * Construct a new LastActivityDateGroupKey. * * @param result The Result to create the group key for. */ - @NbBundle.Messages({ - "DiscoveryKeyUtils.LastActivityDateGroupKey.noDate=No Date Available"}) LastActivityDateGroupKey(Result result) { if (result instanceof ResultDomain) { - epochDate = ((ResultDomain) result).getActivityEnd(); - dateNameString = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(new Date(TimeUnit.SECONDS.toMillis(epochDate))); + ResultDomain domainResult = ((ResultDomain) result); + currentWeekCutOff = getCurrentWeekCutOff(domainResult.getActivityEnd(), domainResult); } else { - epochDate = Long.MAX_VALUE; - dateNameString = Bundle.DiscoveryKeyUtils_LastActivityDateGroupKey_noDate(); + throw new IllegalArgumentException("Expected a domain result only."); } } + @NbBundle.Messages({ + "# {0} - month abbreviation", + "# {1} - day of month", + "# {2} - year", + "DiscoveryAttributes.ActivityDateGroupKey.getDisplayNameTemplate=Week of {0} {1}, {2}" + }) @Override String getDisplayName() { - return getDateNameString(); + MonthAbbreviation currentCutOffMonth = MonthAbbreviation.fromMonthValue(currentWeekCutOff.getMonthValue()); + return Bundle.DiscoveryAttributes_ActivityDateGroupKey_getDisplayNameTemplate( + currentCutOffMonth.toString(), Integer.toString(currentWeekCutOff.getDayOfMonth()), + Integer.toString(currentWeekCutOff.getYear())); } @Override @@ -1170,53 +1178,40 @@ public class DiscoveryKeyUtils { } LastActivityDateGroupKey dateGroupKey = (LastActivityDateGroupKey) otherKey; - return getDateNameString().equals(dateGroupKey.getDateNameString()); + return getDisplayName().equals(dateGroupKey.getDisplayName()); } @Override public int hashCode() { - return Objects.hash(getDateNameString()); + return Objects.hash(getDisplayName()); } @Override public int compareTo(GroupKey otherGroupKey) { if (otherGroupKey instanceof LastActivityDateGroupKey) { LastActivityDateGroupKey otherDateGroupKey = (LastActivityDateGroupKey) otherGroupKey; - - // Put the empty list at the end - if (this.getEpochDate().equals(Long.MAX_VALUE)) { - if (otherDateGroupKey.getEpochDate().equals(Long.MAX_VALUE)) { - return 0; - } else { - return 1; - } - } else if (otherDateGroupKey.getEpochDate().equals(Long.MAX_VALUE)) { - return -1; - } - - return getDateNameString().compareTo(otherDateGroupKey.getDateNameString()); + return Long.compare(otherDateGroupKey.currentWeekCutOff.toEpochSecond(), currentWeekCutOff.toEpochSecond()); } else { return compareClassNames(otherGroupKey); } } - - /** - * Get the date this group is for as a Long. - * - * @return The date. - */ - Long getEpochDate() { - return epochDate; - } - - /** - * Get the name which identifies this group. - * - * @return The dateNameString. - */ - String getDateNameString() { - return dateNameString; - } + } + + /** + * Get the next closed Sunday given an epoch time and timezone. + * Dates for grouping are managed on a weekly basis. Each Sunday + * acts as the boundary and representative for the week. + */ + private static ZonedDateTime getCurrentWeekCutOff(long epochSeconds, ResultDomain domainResult) { + Instant startActivityAsInsant = Instant.ofEpochSecond(epochSeconds); + // Determines the timezone using the settings panel or value parsed from the + // parent data source + TimeZone currentTimeZone = ContentUtils.getTimeZone(domainResult.getDataSource()); + // Convert to a datetime using epoch and timezone. + ZonedDateTime startActivityAsDateTime = ZonedDateTime.ofInstant(startActivityAsInsant, currentTimeZone.toZoneId()); + // Get the closest Sunday, which is the cut off for the current week. + // Use this cut off to perform grouping and comparing. + return startActivityAsDateTime.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)); } /** @@ -1224,29 +1219,28 @@ public class DiscoveryKeyUtils { */ static class FirstActivityDateGroupKey extends GroupKey { - private final Long epochDate; - private final String dateNameString; + private ZonedDateTime currentWeekCutOff; /** * Construct a new FirstActivityDateGroupKey. * * @param result The Result to create the group key for. */ - @NbBundle.Messages({ - "DiscoveryKeyUtils.FirstActivityDateGroupKey.noDate=No Date Available"}) FirstActivityDateGroupKey(Result result) { if (result instanceof ResultDomain) { - epochDate = ((ResultDomain) result).getActivityStart(); - dateNameString = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(new Date(TimeUnit.SECONDS.toMillis(epochDate))); + ResultDomain domainResult = ((ResultDomain) result); + currentWeekCutOff = getCurrentWeekCutOff(domainResult.getActivityStart(), domainResult); } else { - epochDate = Long.MAX_VALUE; - dateNameString = Bundle.DiscoveryKeyUtils_FirstActivityDateGroupKey_noDate(); + throw new IllegalArgumentException("Expected a domain result only."); } } - + @Override String getDisplayName() { - return getDateNameString(); + MonthAbbreviation currentCutOffMonth = MonthAbbreviation.fromMonthValue(currentWeekCutOff.getMonthValue()); + return Bundle.DiscoveryAttributes_ActivityDateGroupKey_getDisplayNameTemplate( + currentCutOffMonth.toString(), Integer.toString(currentWeekCutOff.getDayOfMonth()), + Integer.toString(currentWeekCutOff.getYear())); } @Override @@ -1260,53 +1254,23 @@ public class DiscoveryKeyUtils { } FirstActivityDateGroupKey dateGroupKey = (FirstActivityDateGroupKey) otherKey; - return getDateNameString().equals(dateGroupKey.getDateNameString()); + return getDisplayName().equals(dateGroupKey.getDisplayName()); } @Override public int hashCode() { - return Objects.hash(getDateNameString()); + return Objects.hash(getDisplayName()); } @Override public int compareTo(GroupKey otherGroupKey) { if (otherGroupKey instanceof FirstActivityDateGroupKey) { FirstActivityDateGroupKey otherDateGroupKey = (FirstActivityDateGroupKey) otherGroupKey; - - // Put the empty list at the end - if (this.getEpochDate().equals(Long.MAX_VALUE)) { - if (otherDateGroupKey.getEpochDate().equals(Long.MAX_VALUE)) { - return 0; - } else { - return 1; - } - } else if (otherDateGroupKey.getEpochDate().equals(Long.MAX_VALUE)) { - return -1; - } - - return getDateNameString().compareTo(otherDateGroupKey.getDateNameString()); + return Long.compare(otherDateGroupKey.currentWeekCutOff.toEpochSecond(), currentWeekCutOff.toEpochSecond()); } else { return compareClassNames(otherGroupKey); } } - - /** - * Get the date this group is for as a Long. - * - * @return The date. - */ - Long getEpochDate() { - return epochDate; - } - - /** - * Get the name which identifies this group. - * - * @return The dateNameString. - */ - String getDateNameString() { - return dateNameString; - } } /** diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java index 290530bf54..0ceda46203 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java @@ -24,12 +24,15 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.TimeZone; import org.apache.commons.lang3.StringUtils; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; +import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.discovery.search.DiscoveryKeyUtils.GroupKey; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TimeUtilities; import org.sleuthkit.datamodel.TskCoreException; /** @@ -245,7 +248,8 @@ public class DomainSearch { private String getDate(BlackboardArtifact artifact) throws TskCoreException { for (BlackboardAttribute attribute : artifact.getAttributes()) { if (attribute.getAttributeType().getTypeName().startsWith("TSK_DATETIME")) { - String dateString = attribute.getDisplayString(); + TimeZone timeZone = ContentUtils.getTimeZone(artifact); + String dateString = TimeUtilities.epochToTime(attribute.getValueLong(), timeZone); if (dateString.length() >= 10) { return dateString.substring(0, 10); } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchCacheLoader.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchCacheLoader.java index bbded2952e..9aaa375eba 100755 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchCacheLoader.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchCacheLoader.java @@ -212,13 +212,14 @@ class DomainSearchCacheLoader extends CacheLoader 0"); return Pair.of( - whereClause.toString() + ((artifactTypeFilter != null) ? " AND (" + artifactTypeFilter + ")" : ""), + whereClause.toString() + " AND (" + artifactTypeFilter.getWhereClause(Arrays.asList(TSK_WEB_ACCOUNT_TYPE)) + ")", havingClause.toString() ); } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/ResultDomain.java b/Core/src/org/sleuthkit/autopsy/discovery/search/ResultDomain.java index ad8776e847..5da9567887 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/ResultDomain.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/ResultDomain.java @@ -151,7 +151,7 @@ public class ResultDomain extends Result { } @Override - public Content getDataSource() throws TskCoreException { + public Content getDataSource() { return this.dataSource; } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/SearchData.java b/Core/src/org/sleuthkit/autopsy/discovery/search/SearchData.java index b59556015a..d94a189f11 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/SearchData.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/SearchData.java @@ -36,7 +36,13 @@ import org.sleuthkit.datamodel.BlackboardArtifact; public final class SearchData { private final static long BYTES_PER_MB = 1000000; - private static final Set DOMAIN_ARTIFACT_TYPES = EnumSet.of(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY); + private static final Set DOMAIN_ARTIFACT_TYPES = + EnumSet.of(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK, + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE, + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE, + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY, + BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY); /** diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/SearchFiltering.java b/Core/src/org/sleuthkit/autopsy/discovery/search/SearchFiltering.java index 8fab7e84eb..d5142c08c2 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/SearchFiltering.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/SearchFiltering.java @@ -33,6 +33,7 @@ import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; @@ -206,7 +207,7 @@ public class SearchFiltering { */ public static class ArtifactTypeFilter extends AbstractFilter { - private final List types; + private final Collection types; /** * Construct a new ArtifactTypeFilter. @@ -214,7 +215,7 @@ public class SearchFiltering { * @param types The list of BlackboardArtifact types to include in * results from. */ - public ArtifactTypeFilter(List types) { + public ArtifactTypeFilter(Collection types) { this.types = types; } @@ -223,8 +224,8 @@ public class SearchFiltering { * * @return The list of artifact types specified by the filter. */ - public List getTypes() { - return Collections.unmodifiableList(types); + public Collection getTypes() { + return Collections.unmodifiableCollection(types); } private StringJoiner joinStandardArtifactTypes() { diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED index 608ac005ae..4f5a801573 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED @@ -72,6 +72,18 @@ MiniTimelineArtifactListPanel.value.noValue=No value available. MiniTimelineDateListPanel.countColumn.name=Count MiniTimelineDateListPanel.dateColumn.name=Date MiniTimelineDateListPanel.value.noValue=No value available. +MonthAbbreviation.aprilAbbrev=Apr +MonthAbbreviation.augustAbbrev=Aug +MonthAbbreviation.decemberAbbrev=Dec +MonthAbbreviation.feburaryAbbrev=Feb +MonthAbbreviation.januraryAbbrev=Jan +MonthAbbreviation.julyAbbrev=Jul +MonthAbbreviation.juneAbbrev=Jun +MonthAbbreviation.marchAbbrev=Mar +MonthAbbreviation.mayAbbrev=May +MonthAbbreviation.novemberAbbrev=Nov +MonthAbbreviation.octoberAbbrev=Oct +MonthAbbreviation.septemberAbbrev=Sep ObjectDetectedFilterPanel.error.text=At least one object type name must be selected. ParentFolderFilterPanel.error.text=At least one parent path must be entered. PastOccurrencesFilterPanel.error.text=At least one value in the past occurrence filter must be selected. diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java index becc7b6511..ef3e4967f1 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryPanel.java @@ -22,15 +22,16 @@ import java.awt.Color; import java.awt.Component; import java.awt.Point; import java.awt.event.MouseEvent; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; +import java.time.Instant; +import java.time.ZonedDateTime; +import java.util.TimeZone; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.ListCellRenderer; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.ThreadConfined; +import org.sleuthkit.autopsy.datamodel.ContentUtils; /** * Class which displays a preview and details about a domain. @@ -39,7 +40,6 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer< private static final long serialVersionUID = 1L; private static final Color SELECTION_COLOR = new Color(0, 120, 215); - private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd yyyy", Locale.getDefault()); /** * Creates new form DomainPanel. @@ -149,8 +149,9 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer< @Override public Component getListCellRendererComponent(JList list, DomainWrapper value, int index, boolean isSelected, boolean cellHasFocus) { domainNameLabel.setText(value.getResultDomain().getDomain()); - String startDate = dateFormat.format(new Date(value.getResultDomain().getActivityStart() * 1000)); - String endDate = dateFormat.format(new Date(value.getResultDomain().getActivityEnd() * 1000)); + TimeZone timeZone = ContentUtils.getTimeZone(value.getResultDomain().getDataSource()); + String startDate = formatDate(value.getResultDomain().getActivityStart(), timeZone); + String endDate = formatDate(value.getResultDomain().getActivityEnd(), timeZone); activityLabel.setText(Bundle.DomainSummaryPanel_activity_text(startDate, endDate)); totalVisitsLabel.setText(Bundle.DomainSummaryPanel_totalPages_text() + value.getResultDomain().getTotalPageViews()); pagesLabel.setText(Bundle.DomainSummaryPanel_pages_text() + value.getResultDomain().getPageViewsInLast60Days()); @@ -165,6 +166,22 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer< setBackground(isSelected ? SELECTION_COLOR : list.getBackground()); return this; } + + /** + * Formats an epoch time in a given time zone using the following pattern + * + * MMM dd YYYY + * + * The pattern below is formatted manually to reuse the MonthAbbreviation utility. + */ + private String formatDate(long epochSeconds, TimeZone timeZone) { + Instant epochSecondsAsInstant = Instant.ofEpochSecond(epochSeconds); + ZonedDateTime dateTime = ZonedDateTime.ofInstant(epochSecondsAsInstant, timeZone.toZoneId()); + MonthAbbreviation currentCutOffMonth = MonthAbbreviation.fromMonthValue(dateTime.getMonthValue()); + return String.format("%s %02d %04d", + currentCutOffMonth.toString(), + dateTime.getDayOfMonth(), dateTime.getYear()); + } @ThreadConfined(type = ThreadConfined.ThreadType.AWT) @Override diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/MonthAbbreviation.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/MonthAbbreviation.java new file mode 100755 index 0000000000..0a9c896ac4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/MonthAbbreviation.java @@ -0,0 +1,80 @@ +/* + * Autopsy + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.discovery.ui; + +import org.openide.util.NbBundle; + +/** + * Utility for representing month abbreviations + */ +@NbBundle.Messages({ + "MonthAbbreviation.januraryAbbrev=Jan", + "MonthAbbreviation.feburaryAbbrev=Feb", + "MonthAbbreviation.marchAbbrev=Mar", + "MonthAbbreviation.aprilAbbrev=Apr", + "MonthAbbreviation.mayAbbrev=May", + "MonthAbbreviation.juneAbbrev=Jun", + "MonthAbbreviation.julyAbbrev=Jul", + "MonthAbbreviation.augustAbbrev=Aug", + "MonthAbbreviation.septemberAbbrev=Sep", + "MonthAbbreviation.octoberAbbrev=Oct", + "MonthAbbreviation.novemberAbbrev=Nov", + "MonthAbbreviation.decemberAbbrev=Dec" +}) +public enum MonthAbbreviation { + JANURARY(Bundle.MonthAbbreviation_januraryAbbrev()), + FEBURARY(Bundle.MonthAbbreviation_feburaryAbbrev()), + MARCH(Bundle.MonthAbbreviation_marchAbbrev()), + APRIL(Bundle.MonthAbbreviation_aprilAbbrev()), + MAY(Bundle.MonthAbbreviation_mayAbbrev()), + JUNE(Bundle.MonthAbbreviation_juneAbbrev()), + JULY(Bundle.MonthAbbreviation_julyAbbrev()), + AUGUST(Bundle.MonthAbbreviation_augustAbbrev()), + SEPTEMBER(Bundle.MonthAbbreviation_septemberAbbrev()), + OCTOBER(Bundle.MonthAbbreviation_octoberAbbrev()), + NOVEMBER(Bundle.MonthAbbreviation_novemberAbbrev()), + DECEMBER(Bundle.MonthAbbreviation_decemberAbbrev()); + + private final String abbreviation; + + MonthAbbreviation(String abbreviation) { + this.abbreviation = abbreviation; + } + + @Override + public String toString() { + return this.abbreviation; + } + + /** + * Converts a month value (1-12) to the appropriate abbreviation. + * + * @param value Month value (1-12). + * @return Abbreviation matching the month value, null if not found. + */ + public static MonthAbbreviation fromMonthValue(int value) { + MonthAbbreviation[] months = MonthAbbreviation.values(); + for(int i = 0; i < months.length; i++) { + if (i + 1 == value) { + return months[i]; + } + } + return null; + } +} From 2d215536bb152c48523cbe8df83f85ee37609f34 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 16 Dec 2020 17:46:35 -0500 Subject: [PATCH 05/19] Added comments to the PageViews class --- .../sleuthkit/autopsy/discovery/search/SearchData.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/SearchData.java b/Core/src/org/sleuthkit/autopsy/discovery/search/SearchData.java index d94a189f11..87505f267b 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/SearchData.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/SearchData.java @@ -74,6 +74,10 @@ public final class SearchData { } } + /** + * Enum representing the number of page views a domain has received. Page + * views a grouped into ranges for cleaner display and management. + */ @NbBundle.Messages({ "# {0} - minValue", "# {1} - maxValue", @@ -105,10 +109,16 @@ public final class SearchData { } } + /** + * Determines if the given count is covered by this PageView interval. + */ boolean covers(long count) { return count >= minValue && count <= maxValue; } + /** + * Utility to fetch the appropriate PageView interval for the given count. + */ public static PageViews fromPageViewCount(long count) { for (PageViews view : PageViews.values()) { if (view.covers(count)) { From 3b3aba763822886eb857bd0dfa906b7bf665c892 Mon Sep 17 00:00:00 2001 From: apriestman Date: Thu, 17 Dec 2020 10:42:25 -0500 Subject: [PATCH 06/19] Handle case close events correctly --- .../casemodule/IngestJobInfoPanel.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java index 9faa17f51c..4dcdc5260f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java @@ -89,8 +89,15 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { return; } - if (CURRENT_CASE == Case.Events.valueOf(evt.getPropertyName())) { - refresh(); + // Check whether we have a case open or case close event. + if ((CURRENT_CASE == Case.Events.valueOf(evt.getPropertyName()))) { + if (evt.getNewValue() != null) { + // Case open + refresh(); + } else { + // Case close + reset(); + } } }); } @@ -124,7 +131,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { */ private void refresh() { try { - if (Case.isCaseOpen()) { + if (Case.isCaseOpen()) { // Note - this will generally return true when handling a case close event SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); this.ingestJobs = skCase.getIngestJobs(); setDataSource(selectedDataSource); @@ -138,6 +145,14 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { JOptionPane.showMessageDialog(this, Bundle.IngestJobInfoPanel_loadIngestJob_error_text(), Bundle.IngestJobInfoPanel_loadIngestJob_error_title(), JOptionPane.ERROR_MESSAGE); } } + + /** + * Reset the panel. + */ + private void reset() { + this.ingestJobs = new ArrayList<>(); + setDataSource(null); + } @Messages({"IngestJobInfoPanel.IngestJobTableModel.StartTime.header=Start Time", "IngestJobInfoPanel.IngestJobTableModel.EndTime.header=End Time", From 0a9b1299b2d5eea1f52d250be928b96b6b425e25 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 17 Dec 2020 15:29:32 -0500 Subject: [PATCH 07/19] Added persona list to CVT summary view tab --- .../relationships/Bundle.properties | 6 + .../relationships/Bundle.properties-MERGED | 8 + .../relationships/PersonaPanel.form | 47 ++++ .../relationships/PersonaPanel.java | 115 ++++++++ .../relationships/SummaryPanelWorker.java | 135 ++++++++++ .../relationships/SummaryPersonaPane.form | 104 +++++++ .../relationships/SummaryPersonaPane.java | 254 ++++++++++++++++++ .../relationships/SummaryViewer.form | 31 ++- .../relationships/SummaryViewer.java | 65 +++-- 9 files changed, 738 insertions(+), 27 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.form create mode 100755 Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.java create mode 100755 Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPanelWorker.java create mode 100755 Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.form create mode 100755 Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties index 6ecb170c10..122e0b1c51 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties @@ -28,3 +28,9 @@ SummaryViewer.contactsLabel.text=Book Entries: SummaryViewer.accountCountry.text= SummaryViewer.fileRefPane.border.title=File References in Current Case SummaryViewer.selectAccountFileRefLabel.text= SummaryViewer_FileRefNameColumn_Title=Path +SummaryViewer_Persona_Message= +SummaryViewer_Select_account_for_persona= +SummaryViewer.personaPanel.border.title=Persona +PersonaPanel.personaIDLabel.text=jLabel1 +SummaryPersonaPane.noPersonaLabel.text=No personas found +SummaryPersonaPane.messageLabel.text= +SummaryPersonaPane.createButton.text=Create +PersonaPanel.viewButton.text=View diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.form new file mode 100755 index 0000000000..e76f4fe702 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.form @@ -0,0 +1,47 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.java new file mode 100755 index 0000000000..b6968f9cd2 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.java @@ -0,0 +1,115 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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 obt ain 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.communications.relationships; + +import java.awt.Dimension; +import javax.swing.JButton; +import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; + +/** + * + * Panel to show a single persona with a button for viewing persona details. + */ +public class PersonaPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + private final Persona persona; + + /** + * Creates new form PersonaPanel + */ + PersonaPanel(Persona persona) { + initComponents(); + this.persona = persona; + personaIDLabel.setText(persona.getName()); + } + + /** + * Returns the persona displayed by this panel. + * + * @return + */ + Persona getPersona() { + return persona; + } + + /** + * Returns the preferred width for the given persona label. + * + * @return + */ + int getPersonaLabelPreferedWidth() { + return personaIDLabel.getPreferredSize().width; + } + + /** + * Sets the preferred width for the persona name label. + * + * @param width + */ + void setPersonalLabelPreferredWidth(int width) { + Dimension currentDim = personaIDLabel.getPreferredSize(); + personaIDLabel.setPreferredSize(new Dimension(Math.max(currentDim.width, width), currentDim.height)); + } + + /** + * Returns the View button for this panel. + * + * @return + */ + JButton getViewButton() { + return viewButton; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + personaIDLabel = new javax.swing.JLabel(); + viewButton = new javax.swing.JButton(); + + setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(personaIDLabel, org.openide.util.NbBundle.getMessage(PersonaPanel.class, "PersonaPanel.personaIDLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0); + add(personaIDLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(viewButton, org.openide.util.NbBundle.getMessage(PersonaPanel.class, "PersonaPanel.viewButton.text")); // NOI18N + viewButton.setMargin(new java.awt.Insets(0, 5, 0, 5)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0); + add(viewButton, gridBagConstraints); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel personaIDLabel; + private javax.swing.JButton viewButton; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPanelWorker.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPanelWorker.java new file mode 100755 index 0000000000..2014f93bf2 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPanelWorker.java @@ -0,0 +1,135 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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 obt ain 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.communications.relationships; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.swing.SwingWorker; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; +import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; +import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; +import org.sleuthkit.datamodel.Account; +import org.sleuthkit.datamodel.AccountFileInstance; + +/** + * Runnable SwingWorker for gather the data that the Summary panel needs. + */ +class SummaryPanelWorker extends SwingWorker { + + private final Account account; + + // Construct a instance + SummaryPanelWorker(Account account) { + this.account = account; + } + + /** + * Returns the account the worker is gathering data for. + * + * @return + */ + Account getAccount() { + return account; + } + + @Override + protected SummaryWorkerResults doInBackground() throws Exception { + CentralRepoAccount crAccount = null; + List stringList = new ArrayList<>(); + List accountFileInstanceList = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().getAccountFileInstances(account); + for (AccountFileInstance instance : accountFileInstanceList) { + stringList.add(instance.getFile().getUniquePath()); + } + + List personaList = new ArrayList<>(); + if (CentralRepository.isEnabled()) { + Collection personaAccountList = PersonaAccount.getPersonaAccountsForAccount(account); + PersonaAccount.getPersonaAccountsForAccount(account); + + for (PersonaAccount pAccount : personaAccountList) { + personaList.add(pAccount.getPersona()); + } + + crAccount = CentralRepository.getInstance().getAccount(CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName()), account.getTypeSpecificID()); + } + + return new SummaryWorkerResults(stringList, personaList, crAccount); + } + + /** + * Wraps the results of the worker for easy of returning and usage by the + * SummaryViewer. + */ + final static class SummaryWorkerResults { + + private final List accountFileInstancePaths; + private final List personaList; + private final CentralRepoAccount centralRepoAccount; + + /** + * Constructor. + * + * @param accountFileInstancePaths List of instance paths. + * @param personaList List of personas for the account + * @param centralRepoAccount CentralRepoAccount for the given + * account, maybe null if CR is not + * enabled. + */ + SummaryWorkerResults(List accountFileInstancePaths, List personaList, CentralRepoAccount centralRepoAccount) { + this.accountFileInstancePaths = accountFileInstancePaths; + this.personaList = personaList; + this.centralRepoAccount = centralRepoAccount; + } + + /** + * Returns the list of instance paths for the account given to the + * worker. + * + * @return + */ + List getPaths() { + return accountFileInstancePaths; + } + + /** + * Returns the list of personas found for the account given to the + * worker. This list maybe empty if none were found or cr is not + * enabled. + * + * @return + */ + List getPersonaList() { + return personaList; + } + + /** + * Return the cr account for the account given to the worker. This maybe + * null if the cr was not enabled. + * + * @return + */ + CentralRepoAccount getCRAccount() { + return centralRepoAccount; + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.form new file mode 100755 index 0000000000..2fd6f636be --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.form @@ -0,0 +1,104 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.java new file mode 100755 index 0000000000..981f2b5bb7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.java @@ -0,0 +1,254 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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 obt ain 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.communications.relationships; + +import java.awt.CardLayout; +import java.awt.Component; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.swing.JButton; +import javax.swing.JPanel; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount; +import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; +import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog; +import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback; +import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode; +import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel; + +/** + * Panel to show the Personas for a given account. That is apart SummaryViewer. + */ +public final class SummaryPersonaPane extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + + private final Map personaMap; + private final ViewButtonHandler viewButtonHandler = new ViewButtonHandler(); + private CentralRepoAccount currentAccount = null; + + /** + * Creates new form SummaryPersonaPane + */ + SummaryPersonaPane() { + initComponents(); + personaScrollPane.setViewportView(new JPanel()); + + personaMap = new HashMap<>(); + } + + /** + * Clear the persona list. + */ + void clearList() { + personaScrollPane.setViewportView(new JPanel()); + personaMap.clear(); + } + + /** + * Show the message panel. + */ + void showMessagePanel() { + CardLayout layout = (CardLayout) getLayout(); + layout.show(this, "message"); + } + + /** + * Set the message that appears when the message panel is visible. + * + * @param message Message to show. + */ + void setMessage(String message) { + messageLabel.setText(message); + } + + /** + * Update the list of personas to the new given list. + * + * @param personaList New list of personas to show + */ + void updatePersonaList(CentralRepoAccount account, List personaList) { + JPanel panel = new JPanel(); + currentAccount = account; + CardLayout layout = (CardLayout) getLayout(); + if (personaList.isEmpty()) { + layout.show(this, "create"); + } else { + panel.setLayout(new GridLayout(personaList.size(), 1)); + int maxWidth = 0; + List panelList = new ArrayList<>(); + for (Persona persona : personaList) { + PersonaPanel personaPanel = new PersonaPanel(persona); + JButton viewButton = personaPanel.getViewButton(); + panel.add(personaPanel); + + personaMap.put(viewButton, persona); + viewButton.addActionListener(viewButtonHandler); + + panelList.add(personaPanel); + maxWidth = Math.max(personaPanel.getPersonaLabelPreferedWidth(), maxWidth); + } + + //Set the preferred width of the labeles to the buttons line up. + if (panelList.size() > 1) { + for (PersonaPanel ppanel : panelList) { + ppanel.setPersonalLabelPreferredWidth(maxWidth); + } + } + + personaScrollPane.setViewportView(panel); + layout.show(this, "persona"); + } + + } + + /** + * ActionListener to handle the launching of the view dialog for the given + * persona. + */ + final private class ViewButtonHandler implements ActionListener { + + @Override + public void actionPerformed(ActionEvent e) { + Persona persona = personaMap.get((Component) e.getSource()); + new PersonaDetailsDialog(SummaryPersonaPane.this, + PersonaDetailsMode.VIEW, persona, new PersonaViewCallbackImpl()); + } + } + + /** + * Callback method for the view mode of the PersonaDetailsDialog + */ + private final class PersonaViewCallbackImpl implements PersonaDetailsDialogCallback { + + @Override + public void callback(Persona persona) { + // nothing to do + } + } + + /** + * Callback class to handle the creation of a new person for the given + * account + */ + private final class PersonaCreateCallbackImpl implements PersonaDetailsDialogCallback { + + @Override + public void callback(Persona persona) { + if (persona != null) { + List list = new ArrayList<>(); + list.add(persona); + updatePersonaList(null, list); + } + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + personaScrollPane = new javax.swing.JScrollPane(); + messagePane = new javax.swing.JPanel(); + messageLabel = new javax.swing.JLabel(); + createPersonaPanel = new javax.swing.JPanel(); + noPersonaLabel = new javax.swing.JLabel(); + createButton = new javax.swing.JButton(); + + setLayout(new java.awt.CardLayout()); + + personaScrollPane.setBorder(null); + add(personaScrollPane, "persona"); + + messagePane.setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(messageLabel, org.openide.util.NbBundle.getMessage(SummaryPersonaPane.class, "SummaryPersonaPane.messageLabel.text")); // NOI18N + messageLabel.setEnabled(false); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.weighty = 1.0; + messagePane.add(messageLabel, gridBagConstraints); + + add(messagePane, "message"); + + createPersonaPanel.setPreferredSize(new java.awt.Dimension(200, 100)); + createPersonaPanel.setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(noPersonaLabel, org.openide.util.NbBundle.getMessage(SummaryPersonaPane.class, "SummaryPersonaPane.noPersonaLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(7, 5, 0, 0); + createPersonaPanel.add(noPersonaLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(createButton, org.openide.util.NbBundle.getMessage(SummaryPersonaPane.class, "SummaryPersonaPane.createButton.text")); // NOI18N + createButton.setMargin(new java.awt.Insets(0, 5, 0, 5)); + createButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + createButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridheight = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 0); + createPersonaPanel.add(createButton, gridBagConstraints); + + add(createPersonaPanel, "create"); + }// //GEN-END:initComponents + + private void createButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createButtonActionPerformed + PersonaDetailsDialog createPersonaDialog = new PersonaDetailsDialog(SummaryPersonaPane.this, + PersonaDetailsMode.CREATE, null, new PersonaCreateCallbackImpl(), false); + + // Pre populate the persona name and accounts if we have them. + PersonaDetailsPanel personaPanel = createPersonaDialog.getDetailsPanel(); + personaPanel.addAccount(currentAccount, "", Persona.Confidence.HIGH); + + // display the dialog now + createPersonaDialog.display(); + }//GEN-LAST:event_createButtonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton createButton; + private javax.swing.JPanel createPersonaPanel; + private javax.swing.JLabel messageLabel; + private javax.swing.JPanel messagePane; + private javax.swing.JLabel noPersonaLabel; + private javax.swing.JScrollPane personaScrollPane; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form index ec718e9bd1..24c162bc19 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form @@ -265,7 +265,7 @@ - + @@ -281,7 +281,7 @@ - + @@ -363,5 +363,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java index 94fb1bca86..61be1f1307 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2019-2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,13 +19,11 @@ package org.sleuthkit.autopsy.communications.relationships; import java.awt.CardLayout; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.DefaultListModel; import javax.swing.JPanel; -import javax.swing.SwingWorker; import org.netbeans.swing.outline.DefaultOutlineModel; import org.netbeans.swing.outline.Outline; import org.openide.explorer.view.OutlineView; @@ -33,11 +31,10 @@ import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.util.Lookup; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.Account; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; +import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.AccountFileInstance; /** * Account Summary View Panel. This panel shows a list of various counts related @@ -47,6 +44,8 @@ import org.sleuthkit.datamodel.AccountFileInstance; */ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsViewer { + private static final long serialVersionUID = 1L; + private final Lookup lookup; private final DefaultListModel fileRefListModel; @@ -62,7 +61,8 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi "SummaryViewer_Device_Account_Description=This account was referenced by a device in the case.", "SummaryViewer_Account_Description=This account represents a device in the case.", "SummaryViewer_Account_Description_MuliSelect=Summary information is not available when multiple accounts are selected.", - "SummaryViewer_Country_Code=Country: " + "SummaryViewer_Country_Code=Country: ", + "SummaryViewer_Select_account_for_persona= -SummaryViewer.personaPanel.border.title=Persona +SummaryViewer.personaPanel.border.title=Personas PersonaPanel.personaIDLabel.text=jLabel1 SummaryPersonaPane.noPersonaLabel.text=No personas found SummaryPersonaPane.messageLabel.text= diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED index eb2f602819..23f9c73448 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -77,7 +77,7 @@ SummaryViewer.contactsLabel.text=Book Entries: SummaryViewer.accountCountry.text= SummaryViewer.fileRefPane.border.title=File References in Current Case SummaryViewer.selectAccountFileRefLabel.text=