From 1f20b4e2cb37956a7bdada872eefd77ec9f9c384 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 16 Nov 2020 09:20:52 -0500 Subject: [PATCH 01/30] adding popup menu for items --- .../datamodel/UserActivitySummary.java | 155 ++++++++++-------- .../ui/UserActivityPanel.java | 76 +++++++-- .../uiutils/CellModelTableCellRenderer.java | 79 ++++++++- .../uiutils/JTablePanel.java | 83 ++++++++-- .../uiutils/ViewArtifactAction.java | 51 ++++++ .../datamodel/UserActivitySummaryTest.java | 24 +-- 6 files changed, 356 insertions(+), 112 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ViewArtifactAction.java diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java index 16da6f5c4b..7391b0ef8c 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java @@ -108,8 +108,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { private static final String NTOS_BOOT_IDENTIFIER = "NTOSBOOT"; private static final String WINDOWS_PREFIX = "/WINDOWS"; - private static final Comparator TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccess().compareTo(b.getLastAccess()); - private static final Comparator TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getDateAccessed().compareTo(b.getDateAccessed()); + private static final Comparator TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccessed().compareTo(b.getLastAccessed()); + private static final Comparator TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccessed().compareTo(b.getLastAccessed()); /** * Sorts TopProgramsResults pushing highest run time count then most recent @@ -126,8 +126,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { // second priority for sorting is the last run date // if non-0, this is the return value for the comparator int lastRunCompare = nullableCompare( - a.getLastRun() == null ? null : a.getLastRun().getTime(), - b.getLastRun() == null ? null : b.getLastRun().getTime()); + a.getLastAccessed() == null ? null : a.getLastAccessed().getTime(), + b.getLastAccessed() == null ? null : b.getLastAccessed().getTime()); if (lastRunCompare != 0) { return -lastRunCompare; @@ -219,14 +219,14 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { return Collections.emptyList(); } - Pair>> mostRecentAndGroups = getDomainGroupsAndMostRecent(dataSource); + Pair>>> mostRecentAndGroups = getDomainGroupsAndMostRecent(dataSource); // if no recent domains, return accordingly if (mostRecentAndGroups.getKey() == null || mostRecentAndGroups.getValue().size() == 0) { return Collections.emptyList(); } final long mostRecentMs = mostRecentAndGroups.getLeft(); - Map> groups = mostRecentAndGroups.getRight(); + Map>> groups = mostRecentAndGroups.getRight(); return groups.entrySet().stream() .map(entry -> getDomainsResult(entry.getKey(), entry.getValue(), mostRecentMs)) @@ -243,17 +243,20 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * within DOMAIN_WINDOW_MS of mostRecentMs. * * @param domain The domain. - * @param visits The number of visits. + * @param visits The list of the artifact and its associated time in milliseconds. * @param mostRecentMs The most recent visit of any domain. * * @return The TopDomainsResult or null if no visits to this domain within * 30 days of mostRecentMs. */ - private TopDomainsResult getDomainsResult(String domain, List visits, long mostRecentMs) { + private TopDomainsResult getDomainsResult(String domain, List> visits, long mostRecentMs) { long visitCount = 0; Long thisMostRecentMs = null; + BlackboardArtifact thisMostRecentArtifact = null; - for (Long visitMs : visits) { + for (Pair visitInstance : visits) { + BlackboardArtifact artifact = visitInstance.getLeft(); + long visitMs = visitInstance.getRight(); // make sure that visit is within window of mostRecentMS; otherwise skip it. if (visitMs + DOMAIN_WINDOW_MS < mostRecentMs) { continue; @@ -261,6 +264,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { // if visit is within window, increment the count and get most recent visitCount++; + if (visitMs > thisMostRecentMs) { + thisMostRecentMs = visitMs; + thisMostRecentArtifact = artifact; + } thisMostRecentMs = getMax(thisMostRecentMs, visitMs); } @@ -269,7 +276,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { return null; } else { // create a top domain result with the domain, count, and most recent visit date - return new TopDomainsResult(domain, visitCount, new Date(thisMostRecentMs)); + return new TopDomainsResult(domain, visitCount, new Date(thisMostRecentMs), thisMostRecentArtifact); } } @@ -282,17 +289,17 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * @return A tuple where the first value is the latest web history accessed * date in milliseconds and the second value maps normalized * (lowercase; trimmed) domain names to when those domains were - * visited. + * visited and the relevant artifact. * * @throws TskCoreException * @throws SleuthkitCaseProviderException */ - private Pair>> getDomainGroupsAndMostRecent(DataSource dataSource) throws TskCoreException, SleuthkitCaseProviderException { + private Pair>>> getDomainGroupsAndMostRecent(DataSource dataSource) throws TskCoreException, SleuthkitCaseProviderException { List artifacts = DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_WEB_HISTORY, dataSource, TYPE_DATETIME_ACCESSED, DataSourceInfoUtilities.SortOrder.DESCENDING, 0); Long mostRecentMs = null; - Map> domainVisits = new HashMap<>(); + Map>> domainVisits = new HashMap<>(); for (BlackboardArtifact art : artifacts) { Long artifactDateSecs = DataSourceInfoUtilities.getLongOrNull(art, TYPE_DATETIME_ACCESSED); @@ -313,13 +320,13 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { domain = domain.toLowerCase().trim(); // add this visit date to the list of dates for the domain - List domainVisitList = domainVisits.get(domain); + List> domainVisitList = domainVisits.get(domain); if (domainVisitList == null) { domainVisitList = new ArrayList<>(); domainVisits.put(domain, domainVisitList); } - domainVisitList.add(artifactDateMs); + domainVisitList.add(Pair.of(art, artifactDateMs)); } return Pair.of(mostRecentMs, domainVisits); @@ -355,7 +362,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT); Date dateAccessed = DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME_ACCESSED); return (StringUtils.isNotBlank(searchString) && dateAccessed != null) - ? new TopWebSearchResult(searchString, dateAccessed) + ? new TopWebSearchResult(searchString, dateAccessed, artifact) : null; } @@ -477,7 +484,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_ID), DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME), DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MAKE), - DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MODEL) + DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MODEL), + artifact ); }) // remove Root Hub identifier @@ -667,7 +675,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { programName, path, longCount, - DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME) + DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME), + artifact ); } @@ -762,11 +771,15 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { res -> Pair.of(res.getProgramName(), res.getProgramPath()), res -> res, (res1, res2) -> { + Long maxRunTimes = getMax(res1.getRunTimes(), res2.getRunTimes()); + Date maxDate = getMax(res1.getLastAccessed(), res2.getLastAccessed()); + TopProgramsResult maxResult = TOP_PROGRAMS_RESULT_COMPARE.compare(res1, res2) >= 0 ? res1 : res2; return new TopProgramsResult( - res1.getProgramName(), - res1.getProgramPath(), - getMax(res1.getRunTimes(), res2.getRunTimes()), - getMax(res1.getLastRun(), res2.getLastRun())); + maxResult.getProgramName(), + maxResult.getProgramPath(), + maxRunTimes, + maxDate, + maxResult.getArtifact()); })).values(); List orderedResults = results.stream() @@ -779,7 +792,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { // if run times / last run information is available, the first item should have some value, // and then the items should be limited accordingly. if (isPositiveNum(topResult.getRunTimes()) - || (topResult.getLastRun() != null && isPositiveNum(topResult.getLastRun().getTime()))) { + || (topResult.getLastAccessed() != null && isPositiveNum(topResult.getLastAccessed().getTime()))) { return orderedResults.stream().limit(count).collect(Collectors.toList()); } } @@ -787,25 +800,59 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { // otherwise return the alphabetized list with no limit applied. return orderedResults; } + + /** + * Base class including date of last access and the relevant blackboard artifact. + */ + public static class LastAccessedArtifact { + private final Date lastAccessed; + private final BlackboardArtifact artifact; + + /** + * Main constructor. + * @param lastAccessed The date of last access. + * @param artifact The relevant blackboard artifact. + */ + public LastAccessedArtifact(Date lastAccessed, BlackboardArtifact artifact) { + this.lastAccessed = lastAccessed; + this.artifact = artifact; + } + + /** + * @return The date of last access. + */ + public Date getLastAccessed() { + return lastAccessed; + } + + /** + * @return The associated artifact. + */ + public BlackboardArtifact getArtifact() { + return artifact; + } + } + /** * Object containing information about a web search artifact. */ - public static class TopWebSearchResult { + public static class TopWebSearchResult extends LastAccessedArtifact { private final String searchString; - private final Date dateAccessed; private String translatedResult; + /** * Main constructor. * * @param searchString The search string. * @param dateAccessed The latest date searched. + * @param artifact The relevant blackboard artifact. */ - public TopWebSearchResult(String searchString, Date dateAccessed) { + public TopWebSearchResult(String searchString, Date dateAccessed, BlackboardArtifact artifact) { + super(dateAccessed, artifact); this.searchString = searchString; - this.dateAccessed = dateAccessed; } /** @@ -830,22 +877,14 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { public String getSearchString() { return searchString; } - - /** - * @return The date for the search. - */ - public Date getDateAccessed() { - return dateAccessed; - } } /** * A record of a device attached. */ - public static class TopDeviceAttachedResult { + public static class TopDeviceAttachedResult extends LastAccessedArtifact { private final String deviceId; - private final Date dateAccessed; private final String deviceMake; private final String deviceModel; @@ -856,10 +895,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * @param dateAccessed The date last attached. * @param deviceMake The device make. * @param deviceModel The device model. + * @param artifact The relevant blackboard artifact. */ - public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel) { + public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, BlackboardArtifact artifact) { + super(dateAccessed, artifact); this.deviceId = deviceId; - this.dateAccessed = dateAccessed; this.deviceMake = deviceMake; this.deviceModel = deviceModel; } @@ -871,13 +911,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { return deviceId; } - /** - * @return The date last attached. - */ - public Date getDateAccessed() { - return dateAccessed; - } - /** * @return The device make. */ @@ -923,7 +956,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { /** * @return The date the account was last accessed. */ - public Date getLastAccess() { + public Date getLastAccessed() { return lastAccess; } } @@ -931,11 +964,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { /** * Describes a result of a program run on a datasource. */ - public static class TopDomainsResult { + public static class TopDomainsResult extends LastAccessedArtifact { private final String domain; private final Long visitTimes; - private final Date lastVisit; /** * Describes a top domain result. @@ -943,11 +975,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * @param domain The domain. * @param visitTimes The number of times it was visited. * @param lastVisit The date of the last visit. + * @param artifact The relevant blackboard artifact. */ - public TopDomainsResult(String domain, Long visitTimes, Date lastVisit) { + public TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact) { + super(lastVisit, artifact); this.domain = domain; this.visitTimes = visitTimes; - this.lastVisit = lastVisit; } /** @@ -963,24 +996,16 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { public Long getVisitTimes() { return visitTimes; } - - /** - * @return The date of the last visit. - */ - public Date getLastVisit() { - return lastVisit; - } } /** * Describes a result of a program run on a datasource. */ - public static class TopProgramsResult { + public static class TopProgramsResult extends LastAccessedArtifact { private final String programName; private final String programPath; private final Long runTimes; - private final Date lastRun; /** * Main constructor. @@ -988,12 +1013,13 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * @param programName The name of the program. * @param programPath The path of the program. * @param runTimes The number of runs. + * @param artifact The relevant blackboard artifact. */ - TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun) { + TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun, BlackboardArtifact artifact) { + super(lastRun, artifact); this.programName = programName; this.programPath = programPath; this.runTimes = runTimes; - this.lastRun = lastRun; } /** @@ -1016,12 +1042,5 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { public Long getRunTimes() { return runTimes; } - - /** - * @return The last time the program was run or null if not present. - */ - public Date getLastRun() { - return lastRun; - } } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java index baab54217c..9ed422845a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java @@ -29,16 +29,21 @@ import org.apache.commons.lang.StringUtils; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.LastAccessedArtifact; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultMenuItem; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.ViewArtifactAction; +import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.DataSource; /** @@ -61,7 +66,8 @@ import org.sleuthkit.datamodel.DataSource; "UserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header=Last Accessed", "UserActivityPanel_TopAccountTableModel_accountType_header=Account Type", "UserActivityPanel_TopAccountTableModel_lastAccess_header=Last Accessed", - "UserActivityPanel_noDataExists=No communication data exists"}) + "UserActivityPanel_noDataExists=No communication data exists", + "UserActivityPanel_goToArtifact=Go to Artifact"}) public class UserActivityPanel extends BaseDataSourceSummaryPanel { private static final long serialVersionUID = 1L; @@ -74,6 +80,14 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { private static final String ANDROID_FACTORY = "org.python.proxies.module$AndroidModuleFactory"; private static final String ANDROID_MODULE_NAME = "Android Analyzer"; + private static List getArtifactPopup(BlackboardArtifact artifact) { + return artifact == null ? null : Arrays.asList(new DefaultMenuItem(Bundle.UserActivityPanel_goToArtifact(), new ViewArtifactAction(artifact))); + } + + private static List getPopup(LastAccessedArtifact record) { + return record == null ? null : getArtifactPopup(record.getArtifact()); + } + /** * Gets a string formatted date or returns empty string if the date is null. * @@ -92,7 +106,8 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopProgramsTableModel_name_header(), (prog) -> { return new DefaultCellModel(prog.getProgramName()) - .setTooltip(prog.getProgramPath()); + .setTooltip(prog.getProgramPath()) + .setPopupMenu(getPopup(prog)); }, 250), // program folder column @@ -103,7 +118,8 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { getShortFolderName( prog.getProgramPath(), prog.getProgramName())) - .setTooltip(prog.getProgramPath()); + .setTooltip(prog.getProgramPath()) + .setPopupMenu(getPopup(prog)); }, 150), // run count column @@ -111,13 +127,17 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopProgramsTableModel_count_header(), (prog) -> { String runTimes = prog.getRunTimes() == null ? "" : Long.toString(prog.getRunTimes()); - return new DefaultCellModel(runTimes); + return new DefaultCellModel(runTimes) + .setPopupMenu(getPopup(prog)); }, 80), // last run date column new ColumnModel<>( Bundle.UserActivityPanel_TopProgramsTableModel_lastrun_header(), - (prog) -> new DefaultCellModel(getFormatted(prog.getLastRun())), + (prog) -> { + return new DefaultCellModel(getFormatted(prog.getLastAccessed())) + .setPopupMenu(getPopup(prog)); + }, 150) )) .setKeyFunction((prog) -> prog.getProgramPath() + ":" + prog.getProgramName()); @@ -127,20 +147,24 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { // domain column new ColumnModel( Bundle.UserActivityPanel_TopDomainsTableModel_domain_header(), - (recentDomain) -> new DefaultCellModel(recentDomain.getDomain()), + (recentDomain) -> { + return new DefaultCellModel(recentDomain.getDomain()) + .setPopupMenu(getPopup(recentDomain)); + }, 250), // count column new ColumnModel<>( Bundle.UserActivityPanel_TopDomainsTableModel_count_header(), (recentDomain) -> { String visitTimes = recentDomain.getVisitTimes() == null ? "" : Long.toString(recentDomain.getVisitTimes()); - return new DefaultCellModel(visitTimes); + return new DefaultCellModel(visitTimes) + .setPopupMenu(getPopup(recentDomain)); }, 100), // last accessed column new ColumnModel<>( Bundle.UserActivityPanel_TopDomainsTableModel_lastAccess_header(), - (recentDomain) -> new DefaultCellModel(getFormatted(recentDomain.getLastVisit())), + (recentDomain) -> new DefaultCellModel(getFormatted(recentDomain.getLastAccessed())), 150) )) .setKeyFunction((domain) -> domain.getDomain()); @@ -150,19 +174,28 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { // search string column new ColumnModel( Bundle.UserActivityPanel_TopWebSearchTableModel_searchString_header(), - (webSearch) -> new DefaultCellModel(webSearch.getSearchString()), + (webSearch) -> { + return new DefaultCellModel(webSearch.getSearchString()) + .setPopupMenu(getPopup(webSearch)); + }, 250 ), // last accessed new ColumnModel<>( Bundle.UserActivityPanel_TopWebSearchTableModel_dateAccessed_header(), - (webSearch) -> new DefaultCellModel(getFormatted(webSearch.getDateAccessed())), + (webSearch) -> { + return new DefaultCellModel(getFormatted(webSearch.getLastAccessed())) + .setPopupMenu(getPopup(webSearch)); + }, 150 ), // translated value new ColumnModel<>( Bundle.UserActivityPanel_TopWebSearchTableModel_translatedResult_header(), - (webSearch) -> new DefaultCellModel(webSearch.getTranslatedResult()), + (webSearch) -> { + return new DefaultCellModel(webSearch.getTranslatedResult()) + .setPopupMenu(getPopup(webSearch)); + }, 250 ) )) @@ -173,13 +206,19 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { // device id column new ColumnModel( Bundle.UserActivityPanel_TopDeviceAttachedTableModel_deviceId_header(), - (device) -> new DefaultCellModel(device.getDeviceId()), + (device) -> { + return new DefaultCellModel(device.getDeviceId()) + .setPopupMenu(getPopup(device)); + }, 250 ), // last accessed new ColumnModel<>( Bundle.UserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header(), - (device) -> new DefaultCellModel(getFormatted(device.getDateAccessed())), + (device) -> { + return new DefaultCellModel(getFormatted(device.getLastAccessed())) + .setPopupMenu(getPopup(device)); + }, 150 ), // make and model @@ -191,7 +230,8 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { String makeModelString = (make.isEmpty() || model.isEmpty()) ? make + model : String.format("%s - %s", make, model); - return new DefaultCellModel(makeModelString); + return new DefaultCellModel(makeModelString) + .setPopupMenu(getPopup(device)); }, 250 ) @@ -209,7 +249,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { // last accessed new ColumnModel<>( Bundle.UserActivityPanel_TopAccountTableModel_lastAccess_header(), - (account) -> new DefaultCellModel(getFormatted(account.getLastAccess())), + (account) -> new DefaultCellModel(getFormatted(account.getLastAccessed())), 150 ) )) @@ -227,7 +267,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { private final List> dataFetchComponents; private final UserActivitySummary userActivityData; - + /** * Creates a new UserActivityPanel. */ @@ -239,7 +279,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { * Creates a new UserActivityPanel. * * @param userActivityData Class from which to obtain remaining user - * activity data. + * activity data. */ public UserActivityPanel(UserActivitySummary userActivityData) { super(userActivityData); @@ -295,7 +335,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { /** * Queries DataSourceTopProgramsSummary instance for short folder name. * - * @param path The path for the application. + * @param path The path for the application. * @param appName The application name. * * @return The underlying short folder name if one exists. diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java index 7131dff6c2..02d6d6b34b 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java @@ -20,6 +20,9 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.awt.Component; import java.awt.Insets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JTable; @@ -49,7 +52,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { * Constructor for a HorizontalAlign enum. * * @param jlabelAlignment The corresponding JLabel horizontal alignment - * number. + * number. */ HorizontalAlign(int jlabelAlignment) { this.jlabelAlignment = jlabelAlignment; @@ -57,13 +60,57 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { /** * @return The corresponding JLabel horizontal alignment (i.e. - * JLabel.LEFT). + * JLabel.LEFT). */ int getJLabelAlignment() { return this.jlabelAlignment; } } + /** + * A menu item to be used within a popup menu. + */ + public interface MenuItem { + + /** + * @return The title for that popup menu item. + */ + String getTitle(); + + /** + * @return The action if that popup menu item is clicked. + */ + Runnable getAction(); + } + + /** + * Default implementation of a menu item. + */ + public static class DefaultMenuItem implements MenuItem { + + private final String title; + private final Runnable action; + + /** + * Main constructor. + * @param title The title for the menu item. + * @param action The action should the menu item be clicked. + */ + public DefaultMenuItem(String title, Runnable action) { + this.title = title; + this.action = action; + } + + public String getTitle() { + return title; + } + + public Runnable getAction() { + return action; + } + + } + /** * Basic interface for a cell model. */ @@ -88,6 +135,12 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { * @return The insets for the cell text. */ Insets getInsets(); + + /** + * @return The popup menu associated with this cell or null if no popup + * menu should be shown for this cell. + */ + List getPopupMenu(); } /** @@ -99,6 +152,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { private String tooltip; private HorizontalAlign horizontalAlignment; private Insets insets; + private List popupMenu; /** * Main constructor. @@ -166,6 +220,23 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { return this; } + @Override + public List getPopupMenu() { + return popupMenu == null ? null : Collections.unmodifiableList(popupMenu); + } + + /** + * Sets the list of items for a popup menu + * @param popupMenu + * @return As a utility, returns this. + */ + public DefaultCellModel setPopupMenu(List popupMenu) { + this.popupMenu = popupMenu == null ? null : new ArrayList<>(popupMenu); + return this; + } + + + @Override public String toString() { return getText(); @@ -192,8 +263,8 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { * Customizes the jlabel to match the column model and cell model provided. * * @param defaultCell The cell to customize that will be displayed in the - * jtable. - * @param cellModel The cell model for this cell. + * jtable. + * @param cellModel The cell model for this cell. * * @return The provided defaultCell. */ diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java index 550f334d7a..ed6e5f14ad 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java @@ -20,19 +20,26 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.awt.BorderLayout; import java.awt.Graphics; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.util.Collections; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; import javax.swing.JComponent; import javax.swing.JLayer; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.plaf.LayerUI; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; +import org.apache.commons.collections.CollectionUtils; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.CellModel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.MenuItem; /** * A table that displays a list of items and also can display messages for @@ -91,9 +98,9 @@ public class JTablePanel extends AbstractLoadableComponent> { /** * Constructor for a DataResultColumnModel. * - * @param headerTitle The title for the column. + * @param headerTitle The title for the column. * @param cellRenderer The method that generates a CellModel for the - * column based on the data. + * column based on the data. */ public ColumnModel(String headerTitle, Function cellRenderer) { this(headerTitle, cellRenderer, null); @@ -102,10 +109,10 @@ public class JTablePanel extends AbstractLoadableComponent> { /** * Constructor for a DataResultColumnModel. * - * @param headerTitle The title for the column. + * @param headerTitle The title for the column. * @param cellRenderer The method that generates a CellModel for the - * column based on the data. - * @param width The preferred width of the column. + * column based on the data. + * @param width The preferred width of the column. */ public ColumnModel(String headerTitle, Function cellRenderer, Integer width) { this.headerTitle = headerTitle; @@ -122,7 +129,7 @@ public class JTablePanel extends AbstractLoadableComponent> { /** * @return The method that generates a CellModel for the column based on - * the data. + * the data. */ public Function getCellRenderer() { return cellRenderer; @@ -187,6 +194,62 @@ public class JTablePanel extends AbstractLoadableComponent> { return new DefaultListTableModel(columnRenderers); } + /** + * Sets up a table mouse listener to trigger a popup menu if the cell + * clicked has a popup menu. + * + * @param tablePanel Th JTablePanel instance. + */ + private static void setPopupListener(final JTablePanel tablePanel) { + final JTable table = tablePanel.table; + final ListTableModel tableModel = tablePanel.tableModel; + + // add mouse listener to table for popup menu item click + tablePanel.table.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + // make sure click event isn't primary button and table is present + if (e.getButton() != MouseEvent.BUTTON1 && tablePanel.table != null) { + int row = table.rowAtPoint(e.getPoint()); + int col = table.columnAtPoint(e.getPoint()); + + // make sure there is a value at the row,col of click event. + if (tableModel != null + && row > 0 && row < tableModel.getRowCount() + && col > 0 && col < tableModel.getColumnCount()) { + + // select the row + table.setRowSelectionInterval(row, row); + + Object cellModelObj = tableModel.getValueAt(row, col); + // if the object at that cell is a cell model object. + if (cellModelObj instanceof CellModel) { + CellModel cellModel = (CellModel) cellModelObj; + List menuItems = cellModel.getPopupMenu(); + + // if there are menu items, show a popup menu for + // this item with all the menu items. + if (CollectionUtils.isNotEmpty(menuItems)) { + final JPopupMenu popupMenu = new JPopupMenu(); + for (MenuItem mItem : menuItems) { + JMenuItem jMenuItem = new JMenuItem(mItem.getTitle()); + if (mItem.getAction() != null) { + jMenuItem.addActionListener((evt) -> mItem.getAction().run()); + } + popupMenu.add(jMenuItem); + } + popupMenu.show(table, e.getX(), e.getY()); + } + } + + } + + } + } + + }); + } + /** * Generates a JTablePanel corresponding to the provided column definitions * where 'T' is the object representing each row. @@ -198,6 +261,7 @@ public class JTablePanel extends AbstractLoadableComponent> { public static JTablePanel getJTablePanel(List> columns) { ListTableModel tableModel = getTableModel(columns); JTablePanel resultTable = new JTablePanel<>(tableModel); + setPopupListener(resultTable); return resultTable.setColumnModel(getTableColumnModel(columns)); } @@ -206,6 +270,7 @@ public class JTablePanel extends AbstractLoadableComponent> { private ListTableModel tableModel; private JTable table; private Function keyFunction = (rowItem) -> rowItem; + private MouseListener tableMouseListener = null; /** * Panel constructor. @@ -263,8 +328,7 @@ public class JTablePanel extends AbstractLoadableComponent> { /** * @return The function for determining the key for a data row. This key is - * used to maintain current selection in the table despite changing - * rows. + * used to maintain current selection in the table despite changing rows. */ public Function getKeyFunction() { return keyFunction; @@ -295,7 +359,7 @@ public class JTablePanel extends AbstractLoadableComponent> { T prevValue = (tableRows != null && prevSelectedRow >= 0 && prevSelectedRow < tableRows.size()) ? this.tableModel.getDataRows().get(prevSelectedRow) : null; - + Object prevKeyValue = (prevValue == null) ? null : this.keyFunction.apply(prevValue); // set the list of data to be shown as either the data or an empty list @@ -330,7 +394,6 @@ public class JTablePanel extends AbstractLoadableComponent> { private void initComponents() { table = new JTable(); table.getTableHeader().setReorderingAllowed(false); - overlayLayer = new Overlay(); tableScrollPane = new JScrollPane(table); JLayer dualLayer = new JLayer<>(tableScrollPane, overlayLayer); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ViewArtifactAction.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ViewArtifactAction.java new file mode 100644 index 0000000000..4d7f896984 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ViewArtifactAction.java @@ -0,0 +1,51 @@ +/* + * 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 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.datasourcesummary.uiutils; + +import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; +import org.sleuthkit.datamodel.BlackboardArtifact; + +/** + * Action that navigates to an artifact in a tree. + */ +public class ViewArtifactAction implements Runnable { + + private final BlackboardArtifact artifact; + + /** + * Main constructor for this action. + * + * @param artifact The artifact that will be displayed in tree + * DirectoryTreeTopComponent. + */ + public ViewArtifactAction(BlackboardArtifact artifact) { + this.artifact = artifact; + } + + @Override + public void run() { + final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance(); + + // Navigate to the source context artifact. + if (dtc != null && artifact != null) { + dtc.viewArtifact(artifact); + } + } + +} diff --git a/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummaryTest.java b/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummaryTest.java index 0aff8a56b5..dbb32b1124 100644 --- a/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummaryTest.java +++ b/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummaryTest.java @@ -296,7 +296,7 @@ public class UserActivitySummaryTest { Assert.assertEquals(acceptedDevice, results.get(0).getDeviceModel()); Assert.assertEquals("MAKE " + acceptedDevice, results.get(0).getDeviceMake()); Assert.assertEquals("ID " + acceptedDevice, results.get(0).getDeviceId()); - Assert.assertEquals(time, results.get(0).getDateAccessed().getTime() / 1000); + Assert.assertEquals(time, results.get(0).getLastAccessed().getTime() / 1000); } /** @@ -377,9 +377,9 @@ public class UserActivitySummaryTest { Assert.assertEquals("Expected two different search queries", 2, results.size()); Assert.assertTrue(query1.equalsIgnoreCase(results.get(0).getSearchString())); - Assert.assertEquals(DAY_SECONDS * 5, results.get(0).getDateAccessed().getTime() / 1000); + Assert.assertEquals(DAY_SECONDS * 5, results.get(0).getLastAccessed().getTime() / 1000); Assert.assertTrue(query2.equalsIgnoreCase(results.get(1).getSearchString())); - Assert.assertEquals(DAY_SECONDS * 3, results.get(1).getDateAccessed().getTime() / 1000); + Assert.assertEquals(DAY_SECONDS * 3, results.get(1).getLastAccessed().getTime() / 1000); } private void webSearchTranslationTest(List queries, boolean hasProvider, String translationSuffix) @@ -573,11 +573,11 @@ public class UserActivitySummaryTest { Assert.assertEquals(2, domains.size()); Assert.assertTrue("Expected " + domain1 + " to be first domain", domain1.equalsIgnoreCase(domains.get(0).getDomain())); - Assert.assertEquals(DAY_SECONDS * DOMAIN_WINDOW_DAYS * 2, domains.get(0).getLastVisit().getTime() / 1000); + Assert.assertEquals(DAY_SECONDS * DOMAIN_WINDOW_DAYS * 2, domains.get(0).getLastAccessed().getTime() / 1000); Assert.assertEquals((Long) 2L, domains.get(0).getVisitTimes()); Assert.assertTrue("Expected " + domain3 + " to be second domain", domain3.equalsIgnoreCase(domains.get(1).getDomain())); - Assert.assertEquals(DAY_SECONDS * DOMAIN_WINDOW_DAYS, domains.get(1).getLastVisit().getTime() / 1000); + Assert.assertEquals(DAY_SECONDS * DOMAIN_WINDOW_DAYS, domains.get(1).getLastAccessed().getTime() / 1000); Assert.assertEquals((Long) 1L, domains.get(1).getVisitTimes()); } @@ -617,7 +617,7 @@ public class UserActivitySummaryTest { Assert.assertEquals(1, domains.size()); Assert.assertTrue("Expected " + domain1 + " to be most recent domain", domain1.equalsIgnoreCase(domains.get(0).getDomain())); - Assert.assertEquals(DAY_SECONDS, domains.get(0).getLastVisit().getTime() / 1000); + Assert.assertEquals(DAY_SECONDS, domains.get(0).getLastAccessed().getTime() / 1000); } /** @@ -654,11 +654,11 @@ public class UserActivitySummaryTest { Assert.assertEquals(2, domains.size()); Assert.assertTrue(domain1.equalsIgnoreCase(domains.get(1).getDomain())); - Assert.assertEquals(6L, domains.get(1).getLastVisit().getTime() / 1000); + Assert.assertEquals(6L, domains.get(1).getLastAccessed().getTime() / 1000); Assert.assertEquals((Long) 2L, domains.get(1).getVisitTimes()); Assert.assertTrue(domain2.equalsIgnoreCase(domains.get(0).getDomain())); - Assert.assertEquals(4L, domains.get(0).getLastVisit().getTime() / 1000); + Assert.assertEquals(4L, domains.get(0).getLastAccessed().getTime() / 1000); Assert.assertEquals((Long) 3L, domains.get(0).getVisitTimes()); } @@ -843,7 +843,7 @@ public class UserActivitySummaryTest { // since this may be somewhat variable Assert.assertTrue(expectedItem.getAccountType().equalsIgnoreCase(receivedItem.getAccountType())); - Assert.assertEquals(expectedItem.getLastAccess().getTime(), receivedItem.getLastAccess().getTime()); + Assert.assertEquals(expectedItem.getLastAccessed().getTime(), receivedItem.getLastAccessed().getTime()); } } @@ -1130,17 +1130,17 @@ public class UserActivitySummaryTest { Assert.assertTrue("program1.exe".equalsIgnoreCase(results.get(0).getProgramName())); Assert.assertTrue("/Program Files/another/".equalsIgnoreCase(results.get(0).getProgramPath())); Assert.assertEquals((Long) 31L, results.get(0).getRunTimes()); - Assert.assertEquals((Long) 31L, (Long) (results.get(0).getLastRun().getTime() / 1000)); + Assert.assertEquals((Long) 31L, (Long) (results.get(0).getLastAccessed().getTime() / 1000)); Assert.assertTrue("program1.exe".equalsIgnoreCase(results.get(1).getProgramName())); Assert.assertTrue("/Program Files/etc/".equalsIgnoreCase(results.get(1).getProgramPath())); Assert.assertEquals((Long) 21L, results.get(1).getRunTimes()); - Assert.assertEquals((Long) 31L, (Long) (results.get(1).getLastRun().getTime() / 1000)); + Assert.assertEquals((Long) 31L, (Long) (results.get(1).getLastAccessed().getTime() / 1000)); Assert.assertTrue("program2.exe".equalsIgnoreCase(results.get(2).getProgramName())); Assert.assertTrue("/Program Files/another/".equalsIgnoreCase(results.get(2).getProgramPath())); Assert.assertEquals((Long) 10L, results.get(2).getRunTimes()); - Assert.assertEquals((Long) 22L, (Long) (results.get(2).getLastRun().getTime() / 1000)); + Assert.assertEquals((Long) 22L, (Long) (results.get(2).getLastAccessed().getTime() / 1000)); } private void assertProgramOrder(DataSource ds1, List artifacts, List programNamesReturned) From 3146b97ccbd8e8bee0bacfd1751245ff843889b5 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 16 Nov 2020 13:25:08 -0500 Subject: [PATCH 02/30] closes window if in dialog --- .../datamodel/UserActivitySummary.java | 98 +++++++++---------- .../ui/BaseDataSourceSummaryPanel.java | 52 ++++++---- .../ui/Bundle.properties-MERGED | 1 + .../ui/DataSourceSummaryDialog.java | 11 ++- .../ui/DataSourceSummaryTabbedPane.java | 56 ++++++++--- .../ui/UserActivityPanel.java | 12 ++- .../uiutils/JTablePanel.java | 4 +- .../uiutils/ViewArtifactAction.java | 2 +- 8 files changed, 145 insertions(+), 91 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java index 7391b0ef8c..293dd3c3fc 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java @@ -137,7 +137,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { return (a.getProgramName() == null ? "" : a.getProgramName()) .compareToIgnoreCase((b.getProgramName() == null ? "" : b.getProgramName())); }; - + private static final Set ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList( ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(), ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(), @@ -172,9 +172,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * is designed with unit testing in mind since mocked dependencies can be * utilized. * - * @param provider The object providing the current SleuthkitCase. + * @param provider The object providing the current SleuthkitCase. * @param translationService The translation service. - * @param logger The logger to use. + * @param logger The logger to use. */ public UserActivitySummary( SleuthkitCaseProvider provider, @@ -206,7 +206,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * Gets a list of recent domains based on the datasource. * * @param dataSource The datasource to query for recent domains. - * @param count The max count of items to return. + * @param count The max count of items to return. * * @return The list of items retrieved from the database. * @@ -219,14 +219,14 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { return Collections.emptyList(); } - Pair>>> mostRecentAndGroups = getDomainGroupsAndMostRecent(dataSource); + Pair>>> mostRecentAndGroups = getDomainGroupsAndMostRecent(dataSource); // if no recent domains, return accordingly if (mostRecentAndGroups.getKey() == null || mostRecentAndGroups.getValue().size() == 0) { return Collections.emptyList(); } final long mostRecentMs = mostRecentAndGroups.getLeft(); - Map>> groups = mostRecentAndGroups.getRight(); + Map>> groups = mostRecentAndGroups.getRight(); return groups.entrySet().stream() .map(entry -> getDomainsResult(entry.getKey(), entry.getValue(), mostRecentMs)) @@ -242,12 +242,13 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * Creates a TopDomainsResult from data or null if no visit date exists * within DOMAIN_WINDOW_MS of mostRecentMs. * - * @param domain The domain. - * @param visits The list of the artifact and its associated time in milliseconds. + * @param domain The domain. + * @param visits The list of the artifact and its associated time in + * milliseconds. * @param mostRecentMs The most recent visit of any domain. * * @return The TopDomainsResult or null if no visits to this domain within - * 30 days of mostRecentMs. + * 30 days of mostRecentMs. */ private TopDomainsResult getDomainsResult(String domain, List> visits, long mostRecentMs) { long visitCount = 0; @@ -256,15 +257,15 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { for (Pair visitInstance : visits) { BlackboardArtifact artifact = visitInstance.getLeft(); - long visitMs = visitInstance.getRight(); + Long visitMs = visitInstance.getRight(); // make sure that visit is within window of mostRecentMS; otherwise skip it. - if (visitMs + DOMAIN_WINDOW_MS < mostRecentMs) { + if (visitMs == null || visitMs + DOMAIN_WINDOW_MS < mostRecentMs) { continue; } // if visit is within window, increment the count and get most recent visitCount++; - if (visitMs > thisMostRecentMs) { + if (thisMostRecentMs == null || visitMs > thisMostRecentMs) { thisMostRecentMs = visitMs; thisMostRecentArtifact = artifact; } @@ -287,14 +288,14 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * @param dataSource The datasource. * * @return A tuple where the first value is the latest web history accessed - * date in milliseconds and the second value maps normalized - * (lowercase; trimmed) domain names to when those domains were - * visited and the relevant artifact. + * date in milliseconds and the second value maps normalized (lowercase; + * trimmed) domain names to when those domains were visited and the relevant + * artifact. * * @throws TskCoreException * @throws SleuthkitCaseProviderException */ - private Pair>>> getDomainGroupsAndMostRecent(DataSource dataSource) throws TskCoreException, SleuthkitCaseProviderException { + private Pair>>> getDomainGroupsAndMostRecent(DataSource dataSource) throws TskCoreException, SleuthkitCaseProviderException { List artifacts = DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_WEB_HISTORY, dataSource, TYPE_DATETIME_ACCESSED, DataSourceInfoUtilities.SortOrder.DESCENDING, 0); @@ -356,7 +357,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * @param artifact The artifact. * * @return The TopWebSearchResult or null if the search string or date - * accessed cannot be determined. + * accessed cannot be determined. */ private static TopWebSearchResult getWebSearchResult(BlackboardArtifact artifact) { String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT); @@ -371,11 +372,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * term. * * @param dataSource The data source. - * @param count The maximum number of records to be shown (must be > - * 0). + * @param count The maximum number of records to be shown (must be > 0). * * @return The list of most recent web searches where most recent search - * appears first. + * appears first. * * @throws * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException @@ -431,7 +431,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * @param original The original text. * * @return The translated text or null if no translation can be determined - * or exists. + * or exists. */ private String getTranslationOrNull(String original) { if (!translationService.hasProvider() || StringUtils.isBlank(original)) { @@ -459,11 +459,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * Retrieves most recent devices used by most recent date attached. * * @param dataSource The data source. - * @param count The maximum number of records to be shown (must be > - * 0). + * @param count The maximum number of records to be shown (must be > 0). * * @return The list of most recent devices attached where most recent device - * attached appears first. + * attached appears first. * * @throws * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException @@ -503,7 +502,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * @param artifact The artifact. * * @return The TopAccountResult or null if the account type or message date - * cannot be determined. + * cannot be determined. */ private static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact) { String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE); @@ -517,12 +516,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * Obtains a TopAccountResult from a blackboard artifact. The date is * maximum of any found dates for attribute types provided. * - * @param artifact The artifact. + * @param artifact The artifact. * @param messageType The type of message this is. - * @param dateAttrs The date attribute types. + * @param dateAttrs The date attribute types. * * @return The TopAccountResult or null if the account type or max date are - * not provided. + * not provided. */ private static TopAccountResult getAccountResult(BlackboardArtifact artifact, String messageType, BlackboardAttribute.Type... dateAttrs) { String type = messageType; @@ -546,11 +545,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * sent. * * @param dataSource The data source. - * @param count The maximum number of records to be shown (must be > - * 0). + * @param count The maximum number of records to be shown (must be > 0). * * @return The list of most recent accounts used where the most recent - * account by last message sent occurs first. + * account by last message sent occurs first. * * @throws * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException @@ -616,7 +614,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { /** * Determines a short folder name if any. Otherwise, returns empty string. * - * @param strPath The string path. + * @param strPath The string path. * @param applicationName The application name. * * @return The short folder name or empty string if not found. @@ -667,7 +665,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { if (StringUtils.startsWithIgnoreCase(path, WINDOWS_PREFIX)) { return null; } - + Integer count = DataSourceInfoUtilities.getIntOrNull(artifact, TYPE_COUNT); Long longCount = (count == null) ? null : (long) count; @@ -705,7 +703,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * @param long2 Second possibly null long. * * @return Returns the compare value: 1,0,-1 favoring the higher non-null - * value. + * value. */ private static int nullableCompare(Long long1, Long long2) { if (long1 == null && long2 == null) { @@ -730,7 +728,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { return longNum != null && longNum > 0; } - /** * Retrieves the top programs results for the given data source limited to * the count provided as a parameter. The highest run times are at the top @@ -740,12 +737,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * be ignored and all items will be returned. * * @param dataSource The datasource. If the datasource is null, an empty - * list will be returned. - * @param count The number of results to return. This value must be > 0 - * or an IllegalArgumentException will be thrown. + * list will be returned. + * @param count The number of results to return. This value must be > 0 or + * an IllegalArgumentException will be thrown. * * @return The sorted list and limited to the count if last run or run count - * information is available on any item. + * information is available on any item. * * @throws SleuthkitCaseProviderException * @throws TskCoreException @@ -800,16 +797,19 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { // otherwise return the alphabetized list with no limit applied. return orderedResults; } - + /** - * Base class including date of last access and the relevant blackboard artifact. + * Base class including date of last access and the relevant blackboard + * artifact. */ public static class LastAccessedArtifact { + private final Date lastAccessed; private final BlackboardArtifact artifact; /** * Main constructor. + * * @param lastAccessed The date of last access. * @param artifact The relevant blackboard artifact. */ @@ -832,7 +832,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { return artifact; } } - /** * Object containing information about a web search artifact. @@ -842,7 +841,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { private final String searchString; private String translatedResult; - /** * Main constructor. * @@ -891,10 +889,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { /** * Main constructor. * - * @param deviceId The device id. + * @param deviceId The device id. * @param dateAccessed The date last attached. - * @param deviceMake The device make. - * @param deviceModel The device model. + * @param deviceMake The device make. + * @param deviceModel The device model. * @param artifact The relevant blackboard artifact. */ public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, BlackboardArtifact artifact) { @@ -939,7 +937,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * Main constructor. * * @param accountType The account type. - * @param lastAccess The date the account was last accessed. + * @param lastAccess The date the account was last accessed. */ public TopAccountResult(String accountType, Date lastAccess) { this.accountType = accountType; @@ -972,9 +970,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { /** * Describes a top domain result. * - * @param domain The domain. + * @param domain The domain. * @param visitTimes The number of times it was visited. - * @param lastVisit The date of the last visit. + * @param lastVisit The date of the last visit. * @param artifact The relevant blackboard artifact. */ public TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact) { @@ -1012,7 +1010,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * * @param programName The name of the program. * @param programPath The path of the program. - * @param runTimes The number of runs. + * @param runTimes The number of runs. * @param artifact The relevant blackboard artifact. */ TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun, BlackboardArtifact artifact) { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java index 35f3b921fd..9414ffc37b 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java @@ -64,6 +64,8 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { private final EventUpdateHandler updateHandler; private final List governors; + private Runnable notifyParentClose = null; + private DataSource dataSource; /** @@ -77,7 +79,7 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { * Checks to see if artifact is from a datasource. * * @param art The artifact. - * @param ds The datasource. + * @param ds The datasource. * * @return True if in datasource; false if not or exception. */ @@ -219,7 +221,7 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { * Main constructor. * * @param governors The items governing when this panel should receive - * updates. + * updates. */ protected BaseDataSourceSummaryPanel(UpdateGovernor... governors) { this.governors = (governors == null) ? Collections.emptyList() : Arrays.asList(governors); @@ -246,6 +248,24 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { onNewDataSource(this.dataSource); } + /** + * Sends event that parent should close. + */ + protected void notifyParentClose() { + if (notifyParentClose != null) { + notifyParentClose.run(); + } + } + + /** + * Sets the listener for parent close events. + * + * @param action The action to run when parent is to close. + */ + void setParentCloseListener(Runnable action) { + notifyParentClose = action; + } + /** * @return The current data source. */ @@ -287,7 +307,7 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { * argument and data fetch components and then submits them to run. * * @param dataFetchComponents The components to be run. - * @param dataSource The data source argument. + * @param dataSource The data source argument. */ protected void fetchInformation(List> dataFetchComponents, DataSource dataSource) { if (dataSource == null || !Case.isCaseOpen()) { @@ -320,8 +340,8 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { * argument and submits them to be executed. * * @param dataFetchComponents The components to register. - * @param loadableComponents The components to set to a loading screen. - * @param dataSource The data source argument. + * @param loadableComponents The components to set to a loading screen. + * @param dataSource The data source argument. */ protected void onNewDataSource( List> dataFetchComponents, @@ -371,12 +391,11 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { * message indicating the unrun ingest module will be shown. Otherwise, the * default LoadableComponent.showDataFetchResult behavior will be used. * - * @param component The component. - * @param result The data result. + * @param component The component. + * @param result The data result. * @param factoryClass The fully qualified class name of the relevant - * factory. - * @param moduleName The name of the ingest module (i.e. 'Keyword - * Search'). + * factory. + * @param moduleName The name of the ingest module (i.e. 'Keyword Search'). */ protected void showResultWithModuleCheck(LoadableComponent> component, DataFetchResult> result, String factoryClass, String moduleName) { Predicate> hasResults = (lst) -> lst != null && !lst.isEmpty(); @@ -389,14 +408,13 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { * message indicating the unrun ingest module will be shown. Otherwise, the * default LoadableComponent.showDataFetchResult behavior will be used. * - * @param component The component. - * @param result The data result. - * @param hasResults Given the data type, will provide whether or not the - * data contains any actual results. + * @param component The component. + * @param result The data result. + * @param hasResults Given the data type, will provide whether or not the + * data contains any actual results. * @param factoryClass The fully qualified class name of the relevant - * factory. - * @param moduleName The name of the ingest module (i.e. 'Keyword - * Search'). + * factory. + * @param moduleName The name of the ingest module (i.e. 'Keyword Search'). */ protected void showResultWithModuleCheck(LoadableComponent component, DataFetchResult result, Predicate hasResults, String factoryClass, String moduleName) { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index 4997d067f1..517bf7a410 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -103,6 +103,7 @@ PastCasesPanel.notableFileLabel.text=Cases with Common Items That Were Tagged as PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected. TimelinePanel.activityRangeLabel.text=Activity Range +UserActivityPanel_goToArtifact=Go to Artifact UserActivityPanel_noDataExists=No communication data exists UserActivityPanel_tab_title=User Activity UserActivityPanel_TopAccountTableModel_accountType_header=Account Type diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.java index 5269bdff67..b506437887 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.java @@ -40,7 +40,7 @@ import org.sleuthkit.datamodel.IngestJobInfo; * Dialog for displaying the Data Sources Summary information */ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Observer { - + private static final long serialVersionUID = 1L; private static final Set INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED); private final DataSourceBrowser dataSourcesPanel; @@ -61,6 +61,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser Map fileCountsMap = CaseDataSourcesSummary.getCountsOfFiles(); dataSourcesPanel = new DataSourceBrowser(usageMap, fileCountsMap); dataSourceSummaryTabbedPane = new DataSourceSummaryTabbedPane(); + dataSourceSummaryTabbedPane.setParentCloseListener(() -> DataSourceSummaryDialog.this.dispose()); initComponents(); dataSourceSummarySplitPane.setLeftComponent(dataSourcesPanel); dataSourcesPanel.addListSelectionListener((ListSelectionEvent e) -> { @@ -70,7 +71,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser this.repaint(); } }); - + ingestEventListener = (PropertyChangeEvent evt) -> { if (evt instanceof DataSourceAnalysisCompletedEvent) { DataSourceAnalysisCompletedEvent dsEvent = (DataSourceAnalysisCompletedEvent) evt; @@ -90,7 +91,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); this.pack(); } - + @Override public void dispose() { IngestManager.getInstance().removeIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestEventListener); @@ -104,7 +105,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser void enableObserver() { dataSourcesPanel.addObserver(this); } - + @Override public void update(Observable o, Object arg) { this.dispose(); @@ -169,7 +170,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser * source matches the dataSourceID it will select the first datasource. * * @param dataSourceID the ID of the datasource to select, null will cause - * the first datasource to be selected + * the first datasource to be selected */ void selectDataSource(Long dataSourceId) { dataSourcesPanel.selectDataSource(dataSourceId); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java index 3670124fa2..0dde613158 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java @@ -47,7 +47,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { * Records of tab information (i.e. title, component, function to call on * new data source). */ - private static class DataSourceTab { + private class DataSourceTab { private final String tabTitle; private final Component component; @@ -74,8 +74,12 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { * * @param tabTitle The title of the tab. * @param panel The component to be displayed in the tab. + * @param notifyParentClose Notifies parent to trigger a close. */ DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) { + + panel.setParentCloseListener(() -> notifyParentClose()); + this.tabTitle = tabTitle; this.component = panel; this.onDataSource = panel::setDataSource; @@ -116,20 +120,10 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { private static final String TABBED_PANE = "tabbedPane"; private static final String NO_DATASOURCE_PANE = "noDataSourcePane"; + private Runnable notifyParentClose = null; private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel(); - private final List tabs = Arrays.asList( - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_typesTab_title(), new TypesPanel()), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new UserActivityPanel()), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_analysisTab_title(), new AnalysisPanel()), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_pastCasesTab_title(), new PastCasesPanel()), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_timelineTab_title(), new TimelinePanel()), - // do nothing on closing - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource, () -> { - }), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel()) - ); + private final List tabs; private DataSource dataSource = null; private CardLayout cardLayout; @@ -138,10 +132,46 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { * Creates new form TabPane */ public DataSourceSummaryTabbedPane() { + this.tabs = getTabs(); initComponents(); postInit(); } + /** + * Sends event that parent should close. + */ + private void notifyParentClose() { + if (notifyParentClose != null) { + notifyParentClose.run(); + } + } + + /** + * Sets the listener for parent close events. + * + * @param parentCloseAction The observer. + */ + void setParentCloseListener(Runnable parentCloseAction) { + notifyParentClose = parentCloseAction; + } + + private List getTabs() { + List tabs = Arrays.asList( + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_typesTab_title(), new TypesPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new UserActivityPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_analysisTab_title(), new AnalysisPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_pastCasesTab_title(), new PastCasesPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_timelineTab_title(), new TimelinePanel()), + // do nothing on closing + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource, () -> { + }), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel()) + ); + + return tabs; + } + /** * Method called right after initComponents during initialization. */ diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java index 9ed422845a..6b94c9e6b8 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java @@ -80,11 +80,17 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { private static final String ANDROID_FACTORY = "org.python.proxies.module$AndroidModuleFactory"; private static final String ANDROID_MODULE_NAME = "Android Analyzer"; - private static List getArtifactPopup(BlackboardArtifact artifact) { - return artifact == null ? null : Arrays.asList(new DefaultMenuItem(Bundle.UserActivityPanel_goToArtifact(), new ViewArtifactAction(artifact))); + private List getArtifactPopup(BlackboardArtifact artifact) { + return artifact == null ? null : Arrays.asList( + new DefaultMenuItem( + Bundle.UserActivityPanel_goToArtifact(), + () -> { + new ViewArtifactAction(artifact).run(); + notifyParentClose(); + })); } - private static List getPopup(LastAccessedArtifact record) { + private List getPopup(LastAccessedArtifact record) { return record == null ? null : getArtifactPopup(record.getArtifact()); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java index ed6e5f14ad..b3db4d1d26 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java @@ -215,8 +215,8 @@ public class JTablePanel extends AbstractLoadableComponent> { // make sure there is a value at the row,col of click event. if (tableModel != null - && row > 0 && row < tableModel.getRowCount() - && col > 0 && col < tableModel.getColumnCount()) { + && row >= 0 && row < tableModel.getRowCount() + && col >= 0 && col < tableModel.getColumnCount()) { // select the row table.setRowSelectionInterval(row, row); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ViewArtifactAction.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ViewArtifactAction.java index 4d7f896984..b4f82e3475 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ViewArtifactAction.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ViewArtifactAction.java @@ -44,7 +44,7 @@ public class ViewArtifactAction implements Runnable { // Navigate to the source context artifact. if (dtc != null && artifact != null) { - dtc.viewArtifact(artifact); + dtc.viewArtifactContent(artifact); } } From 418cffa979a86424bcabf6a00ba1b5b27d55b093 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 16 Nov 2020 16:06:23 -0500 Subject: [PATCH 03/30] refactoring --- .../datamodel/AnalysisSummary.java | 81 +++++++--- .../datamodel/RecentFilesSummary.java | 65 ++++---- .../datamodel/UserActivitySummary.java | 19 +-- .../datasourcesummary/ui/AnalysisPanel.java | 36 +++-- .../ui/BaseDataSourceSummaryPanel.java | 49 ++++++ .../ui/RecentFilesPanel.java | 37 ++++- .../ui/UserActivityPanel.java | 43 ++--- .../uiutils/CellModelTableCellRenderer.java | 42 ++++- .../uiutils/JTablePanel.java | 152 ++++++++++-------- .../uiutils/ViewArtifactAction.java | 51 ------ 10 files changed, 356 insertions(+), 219 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ViewArtifactAction.java diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java index 9d35e3d28d..55d0c0b716 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java @@ -94,7 +94,7 @@ public class AnalysisSummary implements DefaultArtifactUpdateGovernor { * @throws SleuthkitCaseProviderException * @throws TskCoreException */ - public List> getHashsetCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + public List getHashsetCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { return getCountsData(dataSource, TYPE_SET_NAME, ARTIFACT_TYPE.TSK_HASHSET_HIT); } @@ -108,10 +108,10 @@ public class AnalysisSummary implements DefaultArtifactUpdateGovernor { * @throws SleuthkitCaseProviderException * @throws TskCoreException */ - public List> getKeywordCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + public List getKeywordCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { return getCountsData(dataSource, TYPE_SET_NAME, ARTIFACT_TYPE.TSK_KEYWORD_HIT).stream() // make sure we have a valid set and that that set does not belong to the set of excluded items - .filter((pair) -> pair != null && pair.getKey() != null && !EXCLUDED_KEYWORD_SEARCH_ITEMS.contains(pair.getKey().toUpperCase().trim())) + .filter((record) -> record != null && record.getIdentifier()!= null && !EXCLUDED_KEYWORD_SEARCH_ITEMS.contains(record.getIdentifier().toUpperCase().trim())) .collect(Collectors.toList()); } @@ -119,33 +119,32 @@ public class AnalysisSummary implements DefaultArtifactUpdateGovernor { * Gets counts for interesting item hits. * * @param dataSource The datasource for which to identify interesting item - * hits. + * hits. * * @return The interesting item set name with the number of hits in - * descending order. + * descending order. * * @throws SleuthkitCaseProviderException * @throws TskCoreException */ - public List> getInterestingItemCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + public List getInterestingItemCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { return getCountsData(dataSource, TYPE_SET_NAME, ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT); } /** * Get counts for the artifact of the specified type. * - * @param dataSource The datasource. - * @param keyType The attribute to use as the key type. + * @param dataSource The datasource. + * @param keyType The attribute to use as the key type. * @param artifactTypes The types of artifacts for which to query. * - * @return A list of key value pairs where the key is the attribute type - * value and the value is the count of items found. This list is - * sorted by the count descending max to min. + * @return A list of AnalysisCountRecord. This list is sorted by the count + * descending max to min. * * @throws SleuthkitCaseProviderException * @throws TskCoreException */ - private List> getCountsData(DataSource dataSource, BlackboardAttribute.Type keyType, ARTIFACT_TYPE... artifactTypes) + private List getCountsData(DataSource dataSource, BlackboardAttribute.Type keyType, ARTIFACT_TYPE... artifactTypes) throws SleuthkitCaseProviderException, TskCoreException { if (dataSource == null) { @@ -161,18 +160,64 @@ public class AnalysisSummary implements DefaultArtifactUpdateGovernor { } // group those based on the value of the attribute type that should serve as a key - Map countedKeys = artifacts.stream() + Map countedKeys = artifacts.stream() .map((art) -> { String key = DataSourceInfoUtilities.getStringOrNull(art, keyType); - return (StringUtils.isBlank(key)) ? null : key; + return (StringUtils.isBlank(key)) ? null : Pair.of(key, art); }) .filter((key) -> key != null) - .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); + .collect(Collectors.toMap( + (r) -> r.getLeft(), + (r) -> new AnalysisCountRecord(r.getLeft(), 1, r.getRight()), + (r1, r2) -> new AnalysisCountRecord(r1.getIdentifier(), r1.getCount() + r2.getCount(), r1.getArtifact()))); // sort from max to min counts - return countedKeys.entrySet().stream() - .map((e) -> Pair.of(e.getKey(), e.getValue())) - .sorted((a, b) -> -a.getValue().compareTo(b.getValue())) + return countedKeys.values().stream() + .sorted((a, b) -> -Long.compare(a.getCount(), b.getCount())) .collect(Collectors.toList()); } + + /** + * A record for an analysis item and its count. + */ + public static class AnalysisCountRecord { + + private final String identifier; + private final long count; + private final BlackboardArtifact artifact; + + /** + * Main constructor. + * + * @param identifier The identifier. + * @param count The count for how many times found. + * @param artifact The artifact. + */ + AnalysisCountRecord(String identifier, long count, BlackboardArtifact artifact) { + this.identifier = identifier; + this.count = count; + this.artifact = artifact; + } + + /** + * @return The identifier for this analysis record. + */ + public String getIdentifier() { + return identifier; + } + + /** + * @return How many times found. + */ + public long getCount() { + return count; + } + + /** + * @return The relevant artifact. + */ + public BlackboardArtifact getArtifact() { + return artifact; + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java index 6c4601af88..c46db82de6 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java @@ -98,11 +98,11 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * TSK_RECENT_OBJECT artifact. * * @param dataSource The data source to query. - * @param maxCount The maximum number of results to return, pass 0 to get - * a list of all results. + * @param maxCount The maximum number of results to return, pass 0 to get a + * list of all results. * * @return A list RecentFileDetails representing the most recently opened - * documents or an empty list if none were found. + * documents or an empty list if none were found. * * @throws SleuthkitCaseProviderException * @throws TskCoreException @@ -137,7 +137,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { } if (accessedTime != null && accessedTime != 0) { - fileDetails.add(new RecentFileDetails(path, accessedTime)); + fileDetails.add(new RecentFileDetails(artifact, path, accessedTime)); } } @@ -149,11 +149,11 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * artifact TSK_DATETIME_ACCESSED attribute. * * @param dataSource Data source to query. - * @param maxCount Maximum number of results to return, passing 0 will - * return all results. + * @param maxCount Maximum number of results to return, passing 0 will + * return all results. * * @return A list of RecentFileDetails objects or empty list if none were - * found. + * found. * * @throws TskCoreException * @throws SleuthkitCaseProviderException @@ -190,7 +190,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { } } if (accessedTime != null && accessedTime != 0L) { - fileDetails.add(new RecentDownloadDetails(path, accessedTime, domain)); + fileDetails.add(new RecentDownloadDetails(artifact, path, accessedTime, domain)); } } @@ -201,8 +201,8 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * Returns a list of the most recent message attachments. * * @param dataSource Data source to query. - * @param maxCount Maximum number of results to return, passing 0 will - * return all results. + * @param maxCount Maximum number of results to return, passing 0 will + * return all results. * * @return A list of RecentFileDetails of the most recent attachments. * @@ -227,7 +227,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * @param dataSource Data source to query. * * @return Returns a SortedMap of details objects returned in descending - * order. + * order. * * @throws SleuthkitCaseProviderException * @throws TskCoreException @@ -272,7 +272,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { list = new ArrayList<>(); sortedMap.put(date, list); } - RecentAttachmentDetails details = new RecentAttachmentDetails(path, date, sender); + RecentAttachmentDetails details = new RecentAttachmentDetails(messageArtifact, path, date, sender); if (!list.contains(details)) { list.add(details); } @@ -288,10 +288,10 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * size. * * @param sortedMap A Map of attachment details sorted by date. - * @param maxCount Maximum number of values to return. + * @param maxCount Maximum number of values to return. * * @return A list of the details of the most recent attachments or empty - * list if none where found. + * list if none where found. */ private List createListFromMap(SortedMap> sortedMap, int maxCount) { List fileList = new ArrayList<>(); @@ -322,7 +322,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * Is the given artifact a message. * * @param nodeArtifact An artifact that might be a message. Must not be - * null. + * null. * * @return True if the given artifact is a message artifact */ @@ -339,14 +339,17 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { private final String path; private final long date; + private final BlackboardArtifact artifact; /** * Constructor for files with just a path and date. * + * @param artifact The relevant artifact. * @param path File path. * @param date File access date\time in seconds with java epoch */ - RecentFileDetails(String path, long date) { + RecentFileDetails(BlackboardArtifact artifact, String path, long date) { + this.artifact = artifact; this.path = path; this.date = date; } @@ -379,6 +382,12 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { return path; } + /** + * @return The pertinent artifact for this recent file hit. + */ + public BlackboardArtifact getArtifact() { + return artifact; + } } /** @@ -391,12 +400,13 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { /** * Constructor for files with just a path and date. * - * @param path File path. - * @param date File access date\time in seconds with java epoch. + * @param artifact The relevant artifact. + * @param path File path. + * @param date File access date\time in seconds with java epoch. * @param webDomain The webdomain from which the file was downloaded. */ - RecentDownloadDetails(String path, long date, String webDomain) { - super(path, date); + RecentDownloadDetails(BlackboardArtifact artifact, String path, long date, String webDomain) { + super(artifact, path, date); this.webDomain = webDomain; } @@ -404,7 +414,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * Returns the web domain. * * @return The web domain or empty string if not available or - * applicable. + * applicable. */ public String getWebDomain() { return webDomain; @@ -422,13 +432,14 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * Constructor for recent download files which have a path, date and * domain value. * - * @param path File path. - * @param date File crtime. + * @param artifact The relevant artifact. + * @param path File path. + * @param date File crtime. * @param sender The sender of the message from which the file was - * attached. + * attached. */ - RecentAttachmentDetails(String path, long date, String sender) { - super(path, date); + RecentAttachmentDetails(BlackboardArtifact artifact, String path, long date, String sender) { + super(artifact, path, date); this.sender = sender; } @@ -436,7 +447,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { * Return the sender of the attached file. * * @return The sender of the attached file or empty string if not - * available. + * available. */ public String getSender() { return sender; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java index 293dd3c3fc..d05a680a66 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummary.java @@ -508,7 +508,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE); Date date = DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME); return (StringUtils.isNotBlank(type) && date != null) - ? new TopAccountResult(type, date) + ? new TopAccountResult(type, date, artifact) : null; } @@ -536,7 +536,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { } return (StringUtils.isNotBlank(type) && latestDate != null) - ? new TopAccountResult(type, latestDate) + ? new TopAccountResult(type, latestDate, artifact) : null; } @@ -928,20 +928,20 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { * A record of an account and the last time it was used determined by * messages. */ - public static class TopAccountResult { + public static class TopAccountResult extends LastAccessedArtifact { private final String accountType; - private final Date lastAccess; /** * Main constructor. * * @param accountType The account type. * @param lastAccess The date the account was last accessed. + * @param artifact The artifact indicating last access. */ - public TopAccountResult(String accountType, Date lastAccess) { + public TopAccountResult(String accountType, Date lastAccess, BlackboardArtifact artifact) { + super(lastAccess, artifact); this.accountType = accountType; - this.lastAccess = lastAccess; } /** @@ -950,13 +950,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor { public String getAccountType() { return accountType; } - - /** - * @return The date the account was last accessed. - */ - public Date getLastAccessed() { - return lastAccess; - } } /** diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java index 34dfa97a13..8721cb0ef0 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java @@ -21,10 +21,11 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.util.Arrays; import java.util.List; import java.util.function.Function; -import org.apache.commons.lang3.tuple.Pair; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.datasourcesummary.datamodel.AnalysisSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.AnalysisSummary.AnalysisCountRecord; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; @@ -58,30 +59,30 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { /** * Default Column definitions for each table */ - private static final List>> DEFAULT_COLUMNS = Arrays.asList( + private final List> DEFAULT_COLUMNS = Arrays.asList( new ColumnModel<>( Bundle.AnalysisPanel_keyColumn_title(), - (pair) -> new DefaultCellModel(pair.getKey()), + (r) -> new DefaultCellModel(r.getIdentifier()).setPopupMenu(getPopup(r)), 300 ), new ColumnModel<>( Bundle.AnalysisPanel_countColumn_title(), - (pair) -> new DefaultCellModel(String.valueOf(pair.getValue())), + (r) -> new DefaultCellModel(String.valueOf(Long.toString(r.getCount()))).setPopupMenu(getPopup(r)), 100 ) ); - private static final Function, String> DEFAULT_KEY_PROVIDER = (pair) -> pair.getKey(); + private final Function DEFAULT_KEY_PROVIDER = (pair) -> pair.getIdentifier(); - private final JTablePanel> hashsetHitsTable + private final JTablePanel hashsetHitsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS) .setKeyFunction(DEFAULT_KEY_PROVIDER); - private final JTablePanel> keywordHitsTable + private final JTablePanel keywordHitsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS) .setKeyFunction(DEFAULT_KEY_PROVIDER); - private final JTablePanel> interestingItemsTable + private final JTablePanel interestingItemsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS) .setKeyFunction(DEFAULT_KEY_PROVIDER); @@ -90,9 +91,8 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { keywordHitsTable, interestingItemsTable ); - + private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); - /** * All of the components necessary for data fetch swing workers to load data @@ -129,14 +129,24 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { initComponents(); } - + /** + * Takes a base class of AnalysisCountRecord and provides the pertinent menu + * items. going to artifact. + * + * @param record The AnalysisCountRecord instance. + * @return The menu items list containing one action or navigating to the + * appropriate artifact and closing the data source summary dialog if open. + */ + private List getPopup(AnalysisCountRecord record) { + return record == null ? null : navigateToArtifactPopup(record.getArtifact()); + } + @Override public void close() { ingestRunningLabel.unregister(); super.close(); } - - + @Override protected void fetchInformation(DataSource dataSource) { fetchInformation(dataFetchComponents, dataSource); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java index 9414ffc37b..39af255c64 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java @@ -33,6 +33,7 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; @@ -41,6 +42,7 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler; import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent; import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialExecutor; import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor; +import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; import org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent; import org.sleuthkit.autopsy.ingest.ModuleContentEvent; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; @@ -53,6 +55,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Base class from which other tabs in data source summary derive. */ +@Messages({"UserActivityPanel_goToArtifact=Go to Artifact"}) abstract class BaseDataSourceSummaryPanel extends JPanel { private static final long serialVersionUID = 1L; @@ -229,6 +232,52 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { this.updateHandler.register(); } + /** + * Given the relevant artifact, navigates to the artifact in the tree and + * closes data source summary dialog if open. + * + * @param artifact The artifact. + * @return The menu item list for a go to artifact menu item. + */ + protected List navigateToArtifactPopup(BlackboardArtifact artifact) { + return artifact == null ? null : Arrays.asList( + new CellModelTableCellRenderer.DefaultMenuItem( + Bundle.UserActivityPanel_goToArtifact(), + () -> { + final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance(); + + // Navigate to the source context artifact. + if (dtc != null && artifact != null) { + dtc.viewArtifact(artifact); + } + + notifyParentClose(); + })); + } + + /** + * Given the relevant artifact, navigates to the artifact's content in the + * tree and closes data source summary dialog if open. + * + * @param artifact The artifact. + * @return The menu item list for a go to artifact menu item. + */ + protected List navigateToArtifactContentPopup(BlackboardArtifact artifact) { + return artifact == null ? null : Arrays.asList( + new CellModelTableCellRenderer.DefaultMenuItem( + Bundle.UserActivityPanel_goToArtifact(), + () -> { + final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance(); + + // Navigate to the source context artifact. + if (dtc != null && artifact != null) { + dtc.viewArtifactContent(artifact); + } + + notifyParentClose(); + })); + } + /** * Closes listeners and resources. */ diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java index 1b675032d0..f4fca025f5 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java @@ -28,6 +28,7 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.Rece import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; @@ -77,6 +78,18 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { initalizeTables(); } + /** + * Takes a base class of RecentFileDetails and provides the pertinent menu + * items. going to artifact. + * + * @param record The RecentFileDetails instance. + * @return The menu items list containing one action or navigating to the + * appropriate artifact and closing the data source summary dialog if open. + */ + private List getPopup(RecentFileDetails record) { + return record == null ? null : navigateToArtifactPopup(record.getArtifact()); + } + @Override protected void fetchInformation(DataSource dataSource) { fetchInformation(dataFetchComponents, dataSource); @@ -113,11 +126,13 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { List> list = Arrays.asList( new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), (prog) -> { - return new DefaultCellModel(prog.getPath()); + return new DefaultCellModel(prog.getPath()) + .setPopupMenu(getPopup(prog)); }, 250), new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(), (prog) -> { - return new DefaultCellModel(prog.getDateAsString()); + return new DefaultCellModel(prog.getDateAsString()) + .setPopupMenu(getPopup(prog)); }, 80)); ListTableModel tableModel = JTablePanel.getTableModel(list); @@ -148,15 +163,18 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { List> list = Arrays.asList( new ColumnModel<>(Bundle.RecentFilePanel_col_header_domain(), (prog) -> { - return new DefaultCellModel(prog.getWebDomain()); + return new DefaultCellModel(prog.getWebDomain()) + .setPopupMenu(getPopup(prog)); }, 100), new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), (prog) -> { - return new DefaultCellModel(prog.getPath()); + return new DefaultCellModel(prog.getPath()) + .setPopupMenu(getPopup(prog)); }, 250), new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(), (prog) -> { - return new DefaultCellModel(prog.getDateAsString()); + return new DefaultCellModel(prog.getDateAsString()) + .setPopupMenu(getPopup(prog)); }, 80)); ListTableModel tableModel = JTablePanel.getTableModel(list); @@ -187,15 +205,18 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { List> list = Arrays.asList( new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), (prog) -> { - return new DefaultCellModel(prog.getPath()); + return new DefaultCellModel(prog.getPath()) + .setPopupMenu(getPopup(prog)); }, 250), new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(), (prog) -> { - return new DefaultCellModel(prog.getDateAsString()); + return new DefaultCellModel(prog.getDateAsString()) + .setPopupMenu(getPopup(prog)); }, 80), new ColumnModel<>(Bundle.RecentFilePanel_col_header_sender(), (prog) -> { - return new DefaultCellModel(prog.getSender()); + return new DefaultCellModel(prog.getSender()) + .setPopupMenu(getPopup(prog)); }, 150)); ListTableModel tableModel = JTablePanel.getTableModel(list); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java index 6b94c9e6b8..ad9d7a2db2 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java @@ -36,14 +36,11 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.Top import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultMenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.ViewArtifactAction; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.DataSource; /** @@ -66,8 +63,7 @@ import org.sleuthkit.datamodel.DataSource; "UserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header=Last Accessed", "UserActivityPanel_TopAccountTableModel_accountType_header=Account Type", "UserActivityPanel_TopAccountTableModel_lastAccess_header=Last Accessed", - "UserActivityPanel_noDataExists=No communication data exists", - "UserActivityPanel_goToArtifact=Go to Artifact"}) + "UserActivityPanel_noDataExists=No communication data exists"}) public class UserActivityPanel extends BaseDataSourceSummaryPanel { private static final long serialVersionUID = 1L; @@ -80,20 +76,6 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { private static final String ANDROID_FACTORY = "org.python.proxies.module$AndroidModuleFactory"; private static final String ANDROID_MODULE_NAME = "Android Analyzer"; - private List getArtifactPopup(BlackboardArtifact artifact) { - return artifact == null ? null : Arrays.asList( - new DefaultMenuItem( - Bundle.UserActivityPanel_goToArtifact(), - () -> { - new ViewArtifactAction(artifact).run(); - notifyParentClose(); - })); - } - - private List getPopup(LastAccessedArtifact record) { - return record == null ? null : getArtifactPopup(record.getArtifact()); - } - /** * Gets a string formatted date or returns empty string if the date is null. * @@ -249,13 +231,19 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { // account type column new ColumnModel( Bundle.UserActivityPanel_TopAccountTableModel_accountType_header(), - (account) -> new DefaultCellModel(account.getAccountType()), + (account) -> { + return new DefaultCellModel(account.getAccountType()) + .setPopupMenu(getPopup(account)); + }, 250 ), // last accessed new ColumnModel<>( Bundle.UserActivityPanel_TopAccountTableModel_lastAccess_header(), - (account) -> new DefaultCellModel(getFormatted(account.getLastAccessed())), + (account) -> { + return new DefaultCellModel(getFormatted(account.getLastAccessed())) + .setPopupMenu(getPopup(account)); + }, 150 ) )) @@ -338,6 +326,19 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { initComponents(); } + /** + * Takes a base class of LastAccessedArtifact and provides the pertinent + * menu items. going to artifact. + * + * @param record The LastAccessedArtifact instance. + * @param navigateToArtifact Navigate right tot the artifact. + * @return The menu items list containing one action or navigating to the + * appropriate artifact and closing the data source summary dialog if open. + */ + private List getPopup(LastAccessedArtifact record) { + return record == null ? null : navigateToArtifactPopup(record.getArtifact()); + } + /** * Queries DataSourceTopProgramsSummary instance for short folder name. * diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java index 02d6d6b34b..8ae72c89c5 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java @@ -20,15 +20,22 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.awt.Component; import java.awt.Insets; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.swing.BorderFactory; import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; import javax.swing.JTable; import javax.swing.border.Border; import javax.swing.table.DefaultTableCellRenderer; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.CellMouseEvent; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.CellMouseListener; /** * A Table cell renderer that renders a cell of a table based off of the @@ -93,6 +100,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { /** * Main constructor. + * * @param title The title for the menu item. * @param action The action should the menu item be clicked. */ @@ -222,11 +230,12 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { @Override public List getPopupMenu() { - return popupMenu == null ? null : Collections.unmodifiableList(popupMenu); + return popupMenu == null ? null : Collections.unmodifiableList(popupMenu); } /** * Sets the list of items for a popup menu + * * @param popupMenu * @return As a utility, returns this. */ @@ -234,8 +243,6 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { this.popupMenu = popupMenu == null ? null : new ArrayList<>(popupMenu); return this; } - - @Override public String toString() { @@ -302,4 +309,33 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { return defaultCell; } + + private static final CellMouseListener DEFAULT_CELL_MOUSE_LISTENER = new CellMouseListener() { + + @Override + public void mouseClicked(CellMouseEvent cellEvent) { + if (cellEvent.getCellValue() instanceof CellModel) { + CellModel cellModel = (CellModel) cellEvent.getCellValue(); + List menuItems = cellModel.getPopupMenu(); + + // if there are menu items, show a popup menu for + // this item with all the menu items. + if (CollectionUtils.isNotEmpty(menuItems)) { + final JPopupMenu popupMenu = new JPopupMenu(); + for (MenuItem mItem : menuItems) { + JMenuItem jMenuItem = new JMenuItem(mItem.getTitle()); + if (mItem.getAction() != null) { + jMenuItem.addActionListener((evt) -> mItem.getAction().run()); + } + popupMenu.add(jMenuItem); + } + popupMenu.show(cellEvent.getTable(), cellEvent.getMouseEvent().getX(), cellEvent.getMouseEvent().getY()); + } + } + } + }; + + public static CellMouseListener getMouseListener() { + return DEFAULT_CELL_MOUSE_LISTENER; + } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java index b3db4d1d26..ec055a42b9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java @@ -22,24 +22,19 @@ import java.awt.BorderLayout; import java.awt.Graphics; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; import java.util.Collections; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; import javax.swing.JComponent; import javax.swing.JLayer; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.plaf.LayerUI; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; -import org.apache.commons.collections.CollectionUtils; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.CellModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.MenuItem; /** * A table that displays a list of items and also can display messages for @@ -47,6 +42,55 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRendere */ public class JTablePanel extends AbstractLoadableComponent> { + public static class CellMouseEvent { + private final MouseEvent e; + private final JTable table; + private final int row; + private final int col; + private final Object cellValue; + + public CellMouseEvent(MouseEvent e, JTable table, int row, int col, Object cellValue) { + this.e = e; + this.table = table; + this.row = row; + this.col = col; + this.cellValue = cellValue; + } + + public MouseEvent getMouseEvent() { + return e; + } + + public JTable getTable() { + return table; + } + + public int getRow() { + return row; + } + + public int getCol() { + return col; + } + + public Object getCellValue() { + return cellValue; + } + } + + /** + * Handles mouse events for cells in the table. + */ + public interface CellMouseListener { + + /** + * Handles mouse events at a cell level for the table. + * + * @param e The event containing information about the cell, the mouse event, and the table. + */ + void mouseClicked(CellMouseEvent e); + } + /** * JTables don't allow displaying messages. So this LayerUI is used to * display the contents of a child JLabel. Inspired by TableWaitLayerTest @@ -194,62 +238,6 @@ public class JTablePanel extends AbstractLoadableComponent> { return new DefaultListTableModel(columnRenderers); } - /** - * Sets up a table mouse listener to trigger a popup menu if the cell - * clicked has a popup menu. - * - * @param tablePanel Th JTablePanel instance. - */ - private static void setPopupListener(final JTablePanel tablePanel) { - final JTable table = tablePanel.table; - final ListTableModel tableModel = tablePanel.tableModel; - - // add mouse listener to table for popup menu item click - tablePanel.table.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - // make sure click event isn't primary button and table is present - if (e.getButton() != MouseEvent.BUTTON1 && tablePanel.table != null) { - int row = table.rowAtPoint(e.getPoint()); - int col = table.columnAtPoint(e.getPoint()); - - // make sure there is a value at the row,col of click event. - if (tableModel != null - && row >= 0 && row < tableModel.getRowCount() - && col >= 0 && col < tableModel.getColumnCount()) { - - // select the row - table.setRowSelectionInterval(row, row); - - Object cellModelObj = tableModel.getValueAt(row, col); - // if the object at that cell is a cell model object. - if (cellModelObj instanceof CellModel) { - CellModel cellModel = (CellModel) cellModelObj; - List menuItems = cellModel.getPopupMenu(); - - // if there are menu items, show a popup menu for - // this item with all the menu items. - if (CollectionUtils.isNotEmpty(menuItems)) { - final JPopupMenu popupMenu = new JPopupMenu(); - for (MenuItem mItem : menuItems) { - JMenuItem jMenuItem = new JMenuItem(mItem.getTitle()); - if (mItem.getAction() != null) { - jMenuItem.addActionListener((evt) -> mItem.getAction().run()); - } - popupMenu.add(jMenuItem); - } - popupMenu.show(table, e.getX(), e.getY()); - } - } - - } - - } - } - - }); - } - /** * Generates a JTablePanel corresponding to the provided column definitions * where 'T' is the object representing each row. @@ -260,17 +248,19 @@ public class JTablePanel extends AbstractLoadableComponent> { */ public static JTablePanel getJTablePanel(List> columns) { ListTableModel tableModel = getTableModel(columns); - JTablePanel resultTable = new JTablePanel<>(tableModel); - setPopupListener(resultTable); - return resultTable.setColumnModel(getTableColumnModel(columns)); + JTablePanel resultTable = new JTablePanel<>(tableModel) + .setColumnModel(getTableColumnModel(columns)) + .setCellListener(CellModelTableCellRenderer.getMouseListener()); + + return resultTable; } private JScrollPane tableScrollPane; private Overlay overlayLayer; private ListTableModel tableModel; private JTable table; + private CellMouseListener cellListener = null; private Function keyFunction = (rowItem) -> rowItem; - private MouseListener tableMouseListener = null; /** * Panel constructor. @@ -287,6 +277,26 @@ public class JTablePanel extends AbstractLoadableComponent> { */ public JTablePanel() { initComponents(); + this.table.addMouseListener(new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent e) { + // make sure click event isn't primary button and table is present + if (cellListener != null) { + int row = table.rowAtPoint(e.getPoint()); + int col = table.columnAtPoint(e.getPoint()); + + // make sure there is a value at the row,col of click event. + if (tableModel != null + && row >= 0 && row < tableModel.getRowCount() + && col >= 0 && col < tableModel.getColumnCount()) { + + Object cellValue = tableModel.getValueAt(row, col); + cellListener.mouseClicked(new CellMouseEvent(e, table, row, col, cellValue)); + } + } + } + }); } /** @@ -307,6 +317,18 @@ public class JTablePanel extends AbstractLoadableComponent> { return this; } + + public CellMouseListener getCellListener() { + return cellListener; + } + + public JTablePanel setCellListener(CellMouseListener cellListener) { + this.cellListener = cellListener; + return this; + } + + + /** * @return The underlying JTable's column model. */ diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ViewArtifactAction.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ViewArtifactAction.java deleted file mode 100644 index b4f82e3475..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ViewArtifactAction.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 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.datasourcesummary.uiutils; - -import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; -import org.sleuthkit.datamodel.BlackboardArtifact; - -/** - * Action that navigates to an artifact in a tree. - */ -public class ViewArtifactAction implements Runnable { - - private final BlackboardArtifact artifact; - - /** - * Main constructor for this action. - * - * @param artifact The artifact that will be displayed in tree - * DirectoryTreeTopComponent. - */ - public ViewArtifactAction(BlackboardArtifact artifact) { - this.artifact = artifact; - } - - @Override - public void run() { - final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance(); - - // Navigate to the source context artifact. - if (dtc != null && artifact != null) { - dtc.viewArtifactContent(artifact); - } - } - -} From b7ba4d64a012cbb08b7c44effc72ee488bd24931 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 16 Nov 2020 16:09:47 -0500 Subject: [PATCH 04/30] quick fix --- .../datasourcesummary/uiutils/CellModelTableCellRenderer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java index 8ae72c89c5..3511101da4 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java @@ -314,7 +314,8 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { @Override public void mouseClicked(CellMouseEvent cellEvent) { - if (cellEvent.getCellValue() instanceof CellModel) { + if (cellEvent.getCellValue() instanceof CellModel && cellEvent.getMouseEvent().getButton() != MouseEvent.BUTTON1) { + cellEvent.getTable().setRowSelectionInterval(cellEvent.getRow(), cellEvent.getRow()); CellModel cellModel = (CellModel) cellEvent.getCellValue(); List menuItems = cellModel.getPopupMenu(); From 73adb2244195d9a0d297780fe6afd3b6407dbe7a Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 17 Nov 2020 11:07:36 -0500 Subject: [PATCH 05/30] get timeline click listener working --- .../datamodel/TimelineSummary.java | 45 +++++++++++-------- .../datasourcesummary/ui/AnalysisPanel.java | 2 +- .../ui/BaseDataSourceSummaryPanel.java | 9 ++-- .../ui/Bundle.properties-MERGED | 1 + .../ui/RecentFilesPanel.java | 2 +- .../datasourcesummary/ui/TimelinePanel.java | 43 ++++++++++++++++++ .../ui/UserActivityPanel.java | 2 +- .../uiutils/BarChartPanel.java | 39 ++++++++++++++++ 8 files changed, 117 insertions(+), 26 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java index 1d634211be..c892c7eec8 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java @@ -44,13 +44,24 @@ import org.sleuthkit.datamodel.TimelineFilter.RootFilter; import org.sleuthkit.datamodel.TimelineManager; import org.sleuthkit.datamodel.TskCoreException; import java.util.function.Supplier; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.timeline.TimeLineController; +import org.sleuthkit.autopsy.timeline.TimeLineModule; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; +import org.sleuthkit.autopsy.timeline.utils.FilterUtils; /** * Provides data source summary information pertaining to Timeline data. */ public class TimelineSummary implements DefaultUpdateGovernor { + + public interface DataSourceFilterFunction { + RootFilter apply(DataSource dataSource) throws NoCurrentCaseException, TskCoreException; + } + private static final long DAY_SECS = 24 * 60 * 60; private static final Set INGEST_JOB_EVENTS = new HashSet<>( Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED)); @@ -64,12 +75,15 @@ public class TimelineSummary implements DefaultUpdateGovernor { private final SleuthkitCaseProvider caseProvider; private final Supplier timeZoneProvider; - + private final DataSourceFilterFunction filterFunction; + /** * Default constructor. */ public TimelineSummary() { - this(SleuthkitCaseProvider.DEFAULT, () -> TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays())); + this(SleuthkitCaseProvider.DEFAULT, + () -> TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()), + (ds) -> TimelineDataSourceUtils.getInstance().getDataSourceFilter(ds)); } /** @@ -77,10 +91,12 @@ public class TimelineSummary implements DefaultUpdateGovernor { * * @param caseProvider SleuthkitCaseProvider provider, cannot be null. * @param timeZoneProvider The timezone provider, cannot be null. + * @param defaulteStateSupplier Provides the default root filter */ - public TimelineSummary(SleuthkitCaseProvider caseProvider, Supplier timeZoneProvider) { + public TimelineSummary(SleuthkitCaseProvider caseProvider, Supplier timeZoneProvider, DataSourceFilterFunction filterFunction) { this.caseProvider = caseProvider; this.timeZoneProvider = timeZoneProvider; + this.filterFunction = filterFunction; } @Override @@ -113,8 +129,9 @@ public class TimelineSummary implements DefaultUpdateGovernor { * @return The retrieved data. * @throws SleuthkitCaseProviderException * @throws TskCoreException + * @throws NoCurrentCaseException */ - public TimelineSummaryData getData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException { + public TimelineSummaryData getData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException, NoCurrentCaseException { TimeZone timeZone = this.timeZoneProvider.get(); TimelineManager timelineManager = this.caseProvider.get().getTimelineManager(); @@ -181,25 +198,15 @@ public class TimelineSummary implements DefaultUpdateGovernor { * belongs. * @return A Map mapping days from epoch to the activity for that day. * @throws TskCoreException + * @throws NoCurrentCaseException */ - private Map getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone) throws TskCoreException { - - DataSourcesFilter dataSourceFilter = new DataSourcesFilter(); - dataSourceFilter.addSubFilter(new TimelineFilter.DataSourceFilter(dataSource.getName(), dataSource.getId())); - - RootFilter dataSourceRootFilter = new RootFilter( - null, - null, - null, - null, - null, - dataSourceFilter, - null, - Collections.emptySet()); + private Map getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone) + throws TskCoreException, NoCurrentCaseException { + RootFilter rootFilter = this.filterFunction.apply(dataSource); // get events for data source long curRunTime = System.currentTimeMillis(); - List events = timelineManager.getEvents(new Interval(1, curRunTime), dataSourceRootFilter); + List events = timelineManager.getEvents(new Interval(1, curRunTime), rootFilter); // get counts of events per day (left is file system events, right is everything else) Map dateCounts = new HashMap<>(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java index 8721cb0ef0..d0f14f390d 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java @@ -138,7 +138,7 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { * appropriate artifact and closing the data source summary dialog if open. */ private List getPopup(AnalysisCountRecord record) { - return record == null ? null : navigateToArtifactPopup(record.getArtifact()); + return record == null ? null : getNavigateToArtifactPopup(record.getArtifact()); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java index 39af255c64..d4ff6ef081 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java @@ -55,7 +55,8 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Base class from which other tabs in data source summary derive. */ -@Messages({"UserActivityPanel_goToArtifact=Go to Artifact"}) +@Messages({"UserActivityPanel_goToArtifact=Go to Artifact", + "UserActivityPanel_goToArtifactContent=Go to File"}) abstract class BaseDataSourceSummaryPanel extends JPanel { private static final long serialVersionUID = 1L; @@ -239,7 +240,7 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { * @param artifact The artifact. * @return The menu item list for a go to artifact menu item. */ - protected List navigateToArtifactPopup(BlackboardArtifact artifact) { + protected List getNavigateToArtifactPopup(BlackboardArtifact artifact) { return artifact == null ? null : Arrays.asList( new CellModelTableCellRenderer.DefaultMenuItem( Bundle.UserActivityPanel_goToArtifact(), @@ -262,10 +263,10 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { * @param artifact The artifact. * @return The menu item list for a go to artifact menu item. */ - protected List navigateToArtifactContentPopup(BlackboardArtifact artifact) { + protected List getNavigateToArtifactContentPopup(BlackboardArtifact artifact) { return artifact == null ? null : Arrays.asList( new CellModelTableCellRenderer.DefaultMenuItem( - Bundle.UserActivityPanel_goToArtifact(), + Bundle.UserActivityPanel_goToArtifactContent(), () -> { final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index 517bf7a410..4902a46385 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -104,6 +104,7 @@ PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected. TimelinePanel.activityRangeLabel.text=Activity Range UserActivityPanel_goToArtifact=Go to Artifact +UserActivityPanel_goToArtifactContent=Go to File UserActivityPanel_noDataExists=No communication data exists UserActivityPanel_tab_title=User Activity UserActivityPanel_TopAccountTableModel_accountType_header=Account Type diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java index f4fca025f5..fc87d54d2c 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java @@ -87,7 +87,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { * appropriate artifact and closing the data source summary dialog if open. */ private List getPopup(RecentFileDetails record) { - return record == null ? null : navigateToArtifactPopup(record.getArtifact()); + return record == null ? null : getNavigateToArtifactContentPopup(record.getArtifact()); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java index 87f170ccab..2bc9eff126 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java @@ -19,15 +19,23 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; +import java.util.logging.Level; import org.apache.commons.collections.CollectionUtils; import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineDataSourceUtils; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData; @@ -41,7 +49,17 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetch import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent; import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel; +import org.sleuthkit.autopsy.timeline.OpenTimelineAction; +import org.sleuthkit.autopsy.timeline.TimeLineController; +import org.sleuthkit.autopsy.timeline.TimeLineModule; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; +import org.sleuthkit.autopsy.timeline.utils.FilterUtils; import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TimelineEventType; +import org.sleuthkit.datamodel.TimelineFilter; +import org.sleuthkit.datamodel.TimelineFilter.DataSourceFilter; +import org.sleuthkit.datamodel.TskCoreException; /** * A tab shown in data source summary displaying information about a data @@ -55,6 +73,7 @@ import org.sleuthkit.datamodel.DataSource; "TimlinePanel_last30DaysChart_artifactEvts_title=Artifact Events",}) public class TimelinePanel extends BaseDataSourceSummaryPanel { + private static final Logger logger = Logger.getLogger(TimelinePanel.class.getName()); private static final long serialVersionUID = 1L; private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat("MMM d, yyyy"); private static final DateFormat CHART_FORMAT = getUtcFormat("MMM d"); @@ -75,6 +94,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { private final LoadableLabel earliestLabel = new LoadableLabel(Bundle.TimelinePanel_earliestLabel_title()); private final LoadableLabel latestLabel = new LoadableLabel(Bundle.TimelinePanel_latestLabel_title()); private final BarChartPanel last30DaysChart = new BarChartPanel(Bundle.TimlinePanel_last30DaysChart_title(), "", ""); + private final OpenTimelineAction openTimelineAction = new OpenTimelineAction(); + private final TimelineDataSourceUtils timelineUtils = TimelineDataSourceUtils.getInstance(); // all loadable components on this tab private final List> loadableComponents = Arrays.asList(earliestLabel, latestLabel, last30DaysChart); @@ -98,6 +119,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { ); initComponents(); + setupChartClickListener(); } /** @@ -257,6 +279,27 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { ); }// //GEN-END:initComponents + private void setupChartClickListener() { + this.last30DaysChart.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + this.last30DaysChart.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + + TimelinePanel.this.notifyParentClose(); + openTimelineAction.performAction(); + try { + TimeLineController controller = TimeLineModule.getController(); + DataSource dataSource = getDataSource(); + if (dataSource != null) { + controller.pushFilters(timelineUtils.getDataSourceFilterState(dataSource)); + } + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.WARNING, "Unable to open Timeline view", ex); + } + } + }); + } + // Variables declaration - do not modify//GEN-BEGIN:variables // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java index ad9d7a2db2..18ca93cf13 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java @@ -336,7 +336,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { * appropriate artifact and closing the data source summary dialog if open. */ private List getPopup(LastAccessedArtifact record) { - return record == null ? null : navigateToArtifactPopup(record.getArtifact()); + return record == null ? null : getNavigateToArtifactPopup(record.getArtifact()); } /** diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java index 3f3822f7c2..6e1db485f5 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java @@ -21,9 +21,12 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.util.Collections; import java.util.List; import javax.swing.JLabel; +import javax.swing.JPanel; import org.apache.commons.collections4.CollectionUtils; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; @@ -252,10 +255,44 @@ public class BarChartPanel extends AbstractLoadableComponent Date: Tue, 17 Nov 2020 11:07:47 -0500 Subject: [PATCH 06/30] get timeline click listener working --- .../datamodel/TimelineDataSourceUtils.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java new file mode 100644 index 0000000000..3e094300bf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java @@ -0,0 +1,95 @@ +/* + * 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 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.datasourcesummary.datamodel; + +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.timeline.TimeLineController; +import org.sleuthkit.autopsy.timeline.TimeLineModule; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; +import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TimelineFilter; +import org.sleuthkit.datamodel.TimelineFilter.DataSourceFilter; +import org.sleuthkit.datamodel.TimelineFilter.RootFilter; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Utilities for interacting with Timeline in relation to data sources. + */ +public class TimelineDataSourceUtils { + + private static TimelineDataSourceUtils instance = null; + + /** + * @return Singleton instance of this class. + */ + public static TimelineDataSourceUtils getInstance() { + if (instance == null) { + instance = new TimelineDataSourceUtils(); + } + + return instance; + } + + /** + * Main constructor. Should be instantiated through getInstance(). + */ + private TimelineDataSourceUtils() { + } + + /** + * Retrieves a RootFilter based on the default filter state but only the + * specified dataSource is selected. + * + * @param dataSource The data source. + * @return The root filter representing a default filter with only this data + * source selected. + * @throws NoCurrentCaseException + * @throws TskCoreException + */ + public RootFilter getDataSourceFilter(DataSource dataSource) throws NoCurrentCaseException, TskCoreException { + RootFilterState filterState = getDataSourceFilterState(dataSource); + return filterState == null ? null : filterState.getActiveFilter(); + } + + /** + * Retrieves a TimeLineController based on the default filter state but only + * the specified dataSource is selected. + * + * @param dataSource The data source. + * @return The root filter state representing a default filter with only + * this data source selected. + * @throws NoCurrentCaseException + * @throws TskCoreException + */ + public RootFilterState getDataSourceFilterState(DataSource dataSource) throws NoCurrentCaseException, TskCoreException { + TimeLineController controller = TimeLineModule.getController(); + RootFilterState dataSourceState = controller.getEventsModel().getDefaultEventFilterState().copyOf(); + + for (FilterState filterState : dataSourceState.getDataSourcesFilterState().getSubFilterStates()) { + DataSourceFilter dsFilter = filterState.getFilter(); + if (dsFilter != null) { + filterState.setSelected(dsFilter.getDataSourceID() == dataSource.getId()); + } + + } + + return dataSourceState; + } +} From db6eafb6a9e4beedfbb4ad8383613fdc1d1a8dcd Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 17 Nov 2020 11:44:42 -0500 Subject: [PATCH 07/30] right click for more options --- .../datasourcesummary/ui/AnalysisPanel.form | 21 +++++++++ .../datasourcesummary/ui/AnalysisPanel.java | 15 +++++++ .../datasourcesummary/ui/Bundle.properties | 12 +++++ .../ui/Bundle.properties-MERGED | 12 +++++ .../ui/RecentFilesPanel.form | 44 +++++++++++++++++-- .../ui/RecentFilesPanel.java | 42 ++++++++++++++++-- .../datasourcesummary/ui/TimelinePanel.form | 9 +++- .../datasourcesummary/ui/TimelinePanel.java | 19 +++++--- .../ui/UserActivityPanel.form | 35 +++++++++++++++ .../ui/UserActivityPanel.java | 25 +++++++++++ 10 files changed, 218 insertions(+), 16 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.form index 402b709b38..631c10fa38 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.form +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.form @@ -128,6 +128,13 @@ + + + + + + + @@ -196,6 +203,13 @@ + + + + + + + @@ -264,6 +278,13 @@ + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java index d0f14f390d..2f4f52be3f 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java @@ -172,14 +172,17 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { javax.swing.JLabel hashsetHitsLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2)); javax.swing.JPanel hashSetHitsPanel = hashsetHitsTable; + rightClickForMoreOptions1 = new javax.swing.JLabel(); javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(32767, 20)); javax.swing.JLabel keywordHitsLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler4 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2)); javax.swing.JPanel keywordHitsPanel = keywordHitsTable; + rightClickForMoreOptions2 = new javax.swing.JLabel(); javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(32767, 20)); javax.swing.JLabel interestingItemLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler6 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2)); javax.swing.JPanel interestingItemPanel = interestingItemsTable; + rightClickForMoreOptions3 = new javax.swing.JLabel(); javax.swing.Box.Filler filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); mainContentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); @@ -202,6 +205,9 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { hashSetHitsPanel.setMinimumSize(new java.awt.Dimension(10, 106)); hashSetHitsPanel.setPreferredSize(new java.awt.Dimension(10, 106)); mainContentPanel.add(hashSetHitsPanel); + + org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions1, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.rightClickForMoreOptions1.text")); // NOI18N + mainContentPanel.add(rightClickForMoreOptions1); mainContentPanel.add(filler2); org.openide.awt.Mnemonics.setLocalizedText(keywordHitsLabel, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.keywordHitsLabel.text")); // NOI18N @@ -213,6 +219,9 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { keywordHitsPanel.setMinimumSize(new java.awt.Dimension(10, 106)); keywordHitsPanel.setPreferredSize(new java.awt.Dimension(10, 106)); mainContentPanel.add(keywordHitsPanel); + + org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions2, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.rightClickForMoreOptions2.text")); // NOI18N + mainContentPanel.add(rightClickForMoreOptions2); mainContentPanel.add(filler5); org.openide.awt.Mnemonics.setLocalizedText(interestingItemLabel, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.interestingItemLabel.text")); // NOI18N @@ -224,6 +233,9 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { interestingItemPanel.setMinimumSize(new java.awt.Dimension(10, 106)); interestingItemPanel.setPreferredSize(new java.awt.Dimension(10, 106)); mainContentPanel.add(interestingItemPanel); + + org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions3, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.rightClickForMoreOptions3.text")); // NOI18N + mainContentPanel.add(rightClickForMoreOptions3); mainContentPanel.add(filler3); mainScrollPane.setViewportView(mainContentPanel); @@ -242,5 +254,8 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel rightClickForMoreOptions1; + private javax.swing.JLabel rightClickForMoreOptions2; + private javax.swing.JLabel rightClickForMoreOptions3; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties index 5d9e6adf0c..d7e91d6775 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties @@ -43,3 +43,15 @@ PastCasesPanel.notableFileLabel.text=Cases with Common Items That Were Tagged as PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected. TimelinePanel.activityRangeLabel.text=Activity Range +RecentFilesPanel.rightClickForMoreOptions1.text=Right click on row for more options +RecentFilesPanel.rightClickForMoreOptions2.text=Right click on row for more options +RecentFilesPanel.rightClickForMoreOptions3.text=Right click on row for more options +AnalysisPanel.rightClickForMoreOptions1.text=Right click on row for more options +AnalysisPanel.rightClickForMoreOptions2.text=Right click on row for more options +AnalysisPanel.rightClickForMoreOptions3.text=Right click on row for more options +UserActivityPanel.rightClickForMoreOptions1.text=Right click on row for more options +UserActivityPanel.rightClickForMoreOptions2.text=Right click on row for more options +UserActivityPanel.rightClickForMoreOptions3.text=Right click on row for more options +UserActivityPanel.rightClickForMoreOptions4.text=Right click on row for more options +UserActivityPanel.rightClickForMoreOptions5.text=Right click on row for more options +TimelinePanel.clickToNavLabel.text=Click to view data source in the timeline diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index 4902a46385..a08dff4289 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -103,6 +103,18 @@ PastCasesPanel.notableFileLabel.text=Cases with Common Items That Were Tagged as PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected. TimelinePanel.activityRangeLabel.text=Activity Range +RecentFilesPanel.rightClickForMoreOptions1.text=Right click on row for more options +RecentFilesPanel.rightClickForMoreOptions2.text=Right click on row for more options +RecentFilesPanel.rightClickForMoreOptions3.text=Right click on row for more options +AnalysisPanel.rightClickForMoreOptions1.text=Right click on row for more options +AnalysisPanel.rightClickForMoreOptions2.text=Right click on row for more options +AnalysisPanel.rightClickForMoreOptions3.text=Right click on row for more options +UserActivityPanel.rightClickForMoreOptions1.text=Right click on row for more options +UserActivityPanel.rightClickForMoreOptions2.text=Right click on row for more options +UserActivityPanel.rightClickForMoreOptions3.text=Right click on row for more options +UserActivityPanel.rightClickForMoreOptions4.text=Right click on row for more options +UserActivityPanel.rightClickForMoreOptions5.text=Right click on row for more options +TimelinePanel.clickToNavLabel.text=Click to view data source in the timeline UserActivityPanel_goToArtifact=Go to Artifact UserActivityPanel_goToArtifactContent=Go to File UserActivityPanel_noDataExists=No communication data exists diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.form index 150abf2ed0..8a8ae70a58 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.form +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.form @@ -94,7 +94,7 @@ - + @@ -106,7 +106,7 @@ - + @@ -140,7 +140,7 @@ - + @@ -156,7 +156,43 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java index fc87d54d2c..ec4dd3fde2 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java @@ -27,6 +27,7 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; @@ -141,6 +142,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { pane.setModel(tableModel); pane.setColumnModel(JTablePanel.getTableColumnModel(list)); pane.setKeyFunction((recentFile) -> recentFile.getPath()); + pane.setCellListener(CellModelTableCellRenderer.getMouseListener()); tablePanelList.add(pane); DataFetchWorker.DataFetchComponents> worker @@ -183,6 +185,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { pane.setModel(tableModel); pane.setKeyFunction((download) -> download.getPath()); pane.setColumnModel(JTablePanel.getTableColumnModel(list)); + pane.setCellListener(CellModelTableCellRenderer.getMouseListener()); tablePanelList.add(pane); DataFetchWorker.DataFetchComponents> worker @@ -225,6 +228,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { pane.setModel(tableModel); pane.setKeyFunction((attachment) -> attachment.getPath()); pane.setColumnModel(JTablePanel.getTableColumnModel(list)); + pane.setCellListener(CellModelTableCellRenderer.getMouseListener()); tablePanelList.add(pane); DataFetchWorker.DataFetchComponents> worker @@ -255,6 +259,9 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { javax.swing.JLabel openDocsLabel = new javax.swing.JLabel(); javax.swing.JLabel downloadLabel = new javax.swing.JLabel(); javax.swing.JLabel attachmentLabel = new javax.swing.JLabel(); + rightClickForMoreOptions1 = new javax.swing.JLabel(); + rightClickForMoreOptions2 = new javax.swing.JLabel(); + rightClickForMoreOptions3 = new javax.swing.JLabel(); setLayout(new java.awt.BorderLayout()); @@ -284,7 +291,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { tablePanel.add(openedDocPane, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 4; + gridBagConstraints.gridy = 5; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; gridBagConstraints.weightx = 1.0; @@ -293,7 +300,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { tablePanel.add(downloadsPane, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 6; + gridBagConstraints.gridy = 8; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; gridBagConstraints.weightx = 1.0; @@ -312,7 +319,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { org.openide.awt.Mnemonics.setLocalizedText(downloadLabel, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.downloadLabel.text")); // NOI18N gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 3; + gridBagConstraints.gridy = 4; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.insets = new java.awt.Insets(20, 0, 0, 0); tablePanel.add(downloadLabel, gridBagConstraints); @@ -320,12 +327,36 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { org.openide.awt.Mnemonics.setLocalizedText(attachmentLabel, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.attachmentLabel.text")); // NOI18N gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 5; + gridBagConstraints.gridy = 7; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; gridBagConstraints.insets = new java.awt.Insets(20, 0, 0, 0); tablePanel.add(attachmentLabel, gridBagConstraints); + org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions1, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.rightClickForMoreOptions1.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + tablePanel.add(rightClickForMoreOptions1, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions2, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.rightClickForMoreOptions2.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 6; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + tablePanel.add(rightClickForMoreOptions2, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions3, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.rightClickForMoreOptions3.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 9; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + tablePanel.add(rightClickForMoreOptions3, gridBagConstraints); + scrollPane.setViewportView(tablePanel); add(scrollPane, java.awt.BorderLayout.CENTER); @@ -336,5 +367,8 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { private javax.swing.JPanel attachmentsPane; private javax.swing.JPanel downloadsPane; private javax.swing.JPanel openedDocPane; + private javax.swing.JLabel rightClickForMoreOptions1; + private javax.swing.JLabel rightClickForMoreOptions2; + private javax.swing.JLabel rightClickForMoreOptions3; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.form index e3493d7a0d..030472d3e9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.form +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.form @@ -171,7 +171,7 @@ - + @@ -193,6 +193,13 @@ + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java index 2bc9eff126..9c24b3ce94 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java @@ -220,7 +220,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { javax.swing.JPanel earliestLabelPanel = earliestLabel; javax.swing.JPanel latestLabelPanel = latestLabel; javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); - javax.swing.JPanel sameIdPanel = last30DaysChart; + javax.swing.JPanel last30DaysPanel = last30DaysChart; + clickToNavLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); mainContentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); @@ -255,12 +256,15 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { filler2.setAlignmentX(0.0F); mainContentPanel.add(filler2); - sameIdPanel.setAlignmentX(0.0F); - sameIdPanel.setMaximumSize(new java.awt.Dimension(600, 300)); - sameIdPanel.setMinimumSize(new java.awt.Dimension(600, 300)); - sameIdPanel.setPreferredSize(new java.awt.Dimension(600, 300)); - sameIdPanel.setVerifyInputWhenFocusTarget(false); - mainContentPanel.add(sameIdPanel); + last30DaysPanel.setAlignmentX(0.0F); + last30DaysPanel.setMaximumSize(new java.awt.Dimension(600, 300)); + last30DaysPanel.setMinimumSize(new java.awt.Dimension(600, 300)); + last30DaysPanel.setPreferredSize(new java.awt.Dimension(600, 300)); + last30DaysPanel.setVerifyInputWhenFocusTarget(false); + mainContentPanel.add(last30DaysPanel); + + org.openide.awt.Mnemonics.setLocalizedText(clickToNavLabel, org.openide.util.NbBundle.getMessage(TimelinePanel.class, "TimelinePanel.clickToNavLabel.text")); // NOI18N + mainContentPanel.add(clickToNavLabel); filler5.setAlignmentX(0.0F); mainContentPanel.add(filler5); @@ -302,5 +306,6 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel clickToNavLabel; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.form index 28560a3ec7..a1a04bbe17 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.form @@ -135,6 +135,13 @@ + + + + + + + @@ -204,6 +211,13 @@ + + + + + + + @@ -273,6 +287,13 @@ + + + + + + + @@ -342,6 +363,13 @@ + + + + + + + @@ -411,6 +439,13 @@ + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java index 18ca93cf13..aeb3f7d6e8 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java @@ -382,22 +382,27 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { javax.swing.JLabel programsRunLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); javax.swing.JPanel topProgramsTablePanel = topProgramsTable; + rightClickForMoreOptions1 = new javax.swing.JLabel(); javax.swing.Box.Filler filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); javax.swing.JLabel recentDomainsLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); javax.swing.JPanel recentDomainsTablePanel = recentDomainsTable; + rightClickForMoreOptions2 = new javax.swing.JLabel(); javax.swing.Box.Filler filler4 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); javax.swing.JLabel topWebSearchLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); javax.swing.JPanel topWebSearches = topWebSearchesTable; + rightClickForMoreOptions3 = new javax.swing.JLabel(); javax.swing.Box.Filler filler6 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); javax.swing.JLabel topDevicesAttachedLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler7 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); javax.swing.JPanel recentDevicesAttached = topDevicesAttachedTable; + rightClickForMoreOptions4 = new javax.swing.JLabel(); javax.swing.Box.Filler filler8 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); javax.swing.JLabel recentAccountsLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler9 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); javax.swing.JPanel topAccounts = topAccountsTable; + rightClickForMoreOptions5 = new javax.swing.JLabel(); setLayout(new java.awt.BorderLayout()); @@ -426,6 +431,9 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { topProgramsTablePanel.setMinimumSize(new java.awt.Dimension(10, 106)); topProgramsTablePanel.setPreferredSize(new java.awt.Dimension(10, 106)); contentPanel.add(topProgramsTablePanel); + + org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions1, org.openide.util.NbBundle.getMessage(UserActivityPanel.class, "UserActivityPanel.rightClickForMoreOptions1.text")); // NOI18N + contentPanel.add(rightClickForMoreOptions1); contentPanel.add(filler3); recentDomainsLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); @@ -438,6 +446,9 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { recentDomainsTablePanel.setMinimumSize(new java.awt.Dimension(10, 106)); recentDomainsTablePanel.setPreferredSize(new java.awt.Dimension(10, 106)); contentPanel.add(recentDomainsTablePanel); + + org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions2, org.openide.util.NbBundle.getMessage(UserActivityPanel.class, "UserActivityPanel.rightClickForMoreOptions2.text")); // NOI18N + contentPanel.add(rightClickForMoreOptions2); contentPanel.add(filler4); topWebSearchLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); @@ -450,6 +461,9 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { topWebSearches.setMinimumSize(new java.awt.Dimension(10, 106)); topWebSearches.setPreferredSize(new java.awt.Dimension(10, 106)); contentPanel.add(topWebSearches); + + org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions3, org.openide.util.NbBundle.getMessage(UserActivityPanel.class, "UserActivityPanel.rightClickForMoreOptions3.text")); // NOI18N + contentPanel.add(rightClickForMoreOptions3); contentPanel.add(filler6); topDevicesAttachedLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); @@ -462,6 +476,9 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { recentDevicesAttached.setMinimumSize(new java.awt.Dimension(10, 106)); recentDevicesAttached.setPreferredSize(new java.awt.Dimension(10, 106)); contentPanel.add(recentDevicesAttached); + + org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions4, org.openide.util.NbBundle.getMessage(UserActivityPanel.class, "UserActivityPanel.rightClickForMoreOptions4.text")); // NOI18N + contentPanel.add(rightClickForMoreOptions4); contentPanel.add(filler8); recentAccountsLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); @@ -475,11 +492,19 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { topAccounts.setPreferredSize(new java.awt.Dimension(10, 106)); contentPanel.add(topAccounts); + org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions5, org.openide.util.NbBundle.getMessage(UserActivityPanel.class, "UserActivityPanel.rightClickForMoreOptions5.text")); // NOI18N + contentPanel.add(rightClickForMoreOptions5); + contentScrollPane.setViewportView(contentPanel); add(contentScrollPane, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel rightClickForMoreOptions1; + private javax.swing.JLabel rightClickForMoreOptions2; + private javax.swing.JLabel rightClickForMoreOptions3; + private javax.swing.JLabel rightClickForMoreOptions4; + private javax.swing.JLabel rightClickForMoreOptions5; // End of variables declaration//GEN-END:variables } From bd5299242d3e7354a537849c5dbfbb2a80dc58aa Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 17 Nov 2020 12:06:09 -0500 Subject: [PATCH 08/30] preparing to update analysis tab --- .../sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties | 3 --- 1 file changed, 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties index d7e91d6775..a68f4b2b11 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties @@ -46,9 +46,6 @@ TimelinePanel.activityRangeLabel.text=Activity Range RecentFilesPanel.rightClickForMoreOptions1.text=Right click on row for more options RecentFilesPanel.rightClickForMoreOptions2.text=Right click on row for more options RecentFilesPanel.rightClickForMoreOptions3.text=Right click on row for more options -AnalysisPanel.rightClickForMoreOptions1.text=Right click on row for more options -AnalysisPanel.rightClickForMoreOptions2.text=Right click on row for more options -AnalysisPanel.rightClickForMoreOptions3.text=Right click on row for more options UserActivityPanel.rightClickForMoreOptions1.text=Right click on row for more options UserActivityPanel.rightClickForMoreOptions2.text=Right click on row for more options UserActivityPanel.rightClickForMoreOptions3.text=Right click on row for more options From 53f1cdf7a5df190eddba1f2159fce1e313f4ad9d Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 17 Nov 2020 12:07:42 -0500 Subject: [PATCH 09/30] revert analysis summary --- .../datamodel/AnalysisSummary.java | 81 +++++-------------- .../datasourcesummary/ui/AnalysisPanel.form | 21 ----- .../datasourcesummary/ui/AnalysisPanel.java | 51 +++--------- 3 files changed, 31 insertions(+), 122 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java index 55d0c0b716..9d35e3d28d 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/AnalysisSummary.java @@ -94,7 +94,7 @@ public class AnalysisSummary implements DefaultArtifactUpdateGovernor { * @throws SleuthkitCaseProviderException * @throws TskCoreException */ - public List getHashsetCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + public List> getHashsetCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { return getCountsData(dataSource, TYPE_SET_NAME, ARTIFACT_TYPE.TSK_HASHSET_HIT); } @@ -108,10 +108,10 @@ public class AnalysisSummary implements DefaultArtifactUpdateGovernor { * @throws SleuthkitCaseProviderException * @throws TskCoreException */ - public List getKeywordCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + public List> getKeywordCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { return getCountsData(dataSource, TYPE_SET_NAME, ARTIFACT_TYPE.TSK_KEYWORD_HIT).stream() // make sure we have a valid set and that that set does not belong to the set of excluded items - .filter((record) -> record != null && record.getIdentifier()!= null && !EXCLUDED_KEYWORD_SEARCH_ITEMS.contains(record.getIdentifier().toUpperCase().trim())) + .filter((pair) -> pair != null && pair.getKey() != null && !EXCLUDED_KEYWORD_SEARCH_ITEMS.contains(pair.getKey().toUpperCase().trim())) .collect(Collectors.toList()); } @@ -119,32 +119,33 @@ public class AnalysisSummary implements DefaultArtifactUpdateGovernor { * Gets counts for interesting item hits. * * @param dataSource The datasource for which to identify interesting item - * hits. + * hits. * * @return The interesting item set name with the number of hits in - * descending order. + * descending order. * * @throws SleuthkitCaseProviderException * @throws TskCoreException */ - public List getInterestingItemCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + public List> getInterestingItemCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { return getCountsData(dataSource, TYPE_SET_NAME, ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT); } /** * Get counts for the artifact of the specified type. * - * @param dataSource The datasource. - * @param keyType The attribute to use as the key type. + * @param dataSource The datasource. + * @param keyType The attribute to use as the key type. * @param artifactTypes The types of artifacts for which to query. * - * @return A list of AnalysisCountRecord. This list is sorted by the count - * descending max to min. + * @return A list of key value pairs where the key is the attribute type + * value and the value is the count of items found. This list is + * sorted by the count descending max to min. * * @throws SleuthkitCaseProviderException * @throws TskCoreException */ - private List getCountsData(DataSource dataSource, BlackboardAttribute.Type keyType, ARTIFACT_TYPE... artifactTypes) + private List> getCountsData(DataSource dataSource, BlackboardAttribute.Type keyType, ARTIFACT_TYPE... artifactTypes) throws SleuthkitCaseProviderException, TskCoreException { if (dataSource == null) { @@ -160,64 +161,18 @@ public class AnalysisSummary implements DefaultArtifactUpdateGovernor { } // group those based on the value of the attribute type that should serve as a key - Map countedKeys = artifacts.stream() + Map countedKeys = artifacts.stream() .map((art) -> { String key = DataSourceInfoUtilities.getStringOrNull(art, keyType); - return (StringUtils.isBlank(key)) ? null : Pair.of(key, art); + return (StringUtils.isBlank(key)) ? null : key; }) .filter((key) -> key != null) - .collect(Collectors.toMap( - (r) -> r.getLeft(), - (r) -> new AnalysisCountRecord(r.getLeft(), 1, r.getRight()), - (r1, r2) -> new AnalysisCountRecord(r1.getIdentifier(), r1.getCount() + r2.getCount(), r1.getArtifact()))); + .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); // sort from max to min counts - return countedKeys.values().stream() - .sorted((a, b) -> -Long.compare(a.getCount(), b.getCount())) + return countedKeys.entrySet().stream() + .map((e) -> Pair.of(e.getKey(), e.getValue())) + .sorted((a, b) -> -a.getValue().compareTo(b.getValue())) .collect(Collectors.toList()); } - - /** - * A record for an analysis item and its count. - */ - public static class AnalysisCountRecord { - - private final String identifier; - private final long count; - private final BlackboardArtifact artifact; - - /** - * Main constructor. - * - * @param identifier The identifier. - * @param count The count for how many times found. - * @param artifact The artifact. - */ - AnalysisCountRecord(String identifier, long count, BlackboardArtifact artifact) { - this.identifier = identifier; - this.count = count; - this.artifact = artifact; - } - - /** - * @return The identifier for this analysis record. - */ - public String getIdentifier() { - return identifier; - } - - /** - * @return How many times found. - */ - public long getCount() { - return count; - } - - /** - * @return The relevant artifact. - */ - public BlackboardArtifact getArtifact() { - return artifact; - } - } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.form index 631c10fa38..402b709b38 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.form +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.form @@ -128,13 +128,6 @@ - - - - - - - @@ -203,13 +196,6 @@ - - - - - - - @@ -278,13 +264,6 @@ - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java index 2f4f52be3f..34dfa97a13 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/AnalysisPanel.java @@ -21,11 +21,10 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.util.Arrays; import java.util.List; import java.util.function.Function; +import org.apache.commons.lang3.tuple.Pair; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.datasourcesummary.datamodel.AnalysisSummary; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.AnalysisSummary.AnalysisCountRecord; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; @@ -59,30 +58,30 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { /** * Default Column definitions for each table */ - private final List> DEFAULT_COLUMNS = Arrays.asList( + private static final List>> DEFAULT_COLUMNS = Arrays.asList( new ColumnModel<>( Bundle.AnalysisPanel_keyColumn_title(), - (r) -> new DefaultCellModel(r.getIdentifier()).setPopupMenu(getPopup(r)), + (pair) -> new DefaultCellModel(pair.getKey()), 300 ), new ColumnModel<>( Bundle.AnalysisPanel_countColumn_title(), - (r) -> new DefaultCellModel(String.valueOf(Long.toString(r.getCount()))).setPopupMenu(getPopup(r)), + (pair) -> new DefaultCellModel(String.valueOf(pair.getValue())), 100 ) ); - private final Function DEFAULT_KEY_PROVIDER = (pair) -> pair.getIdentifier(); + private static final Function, String> DEFAULT_KEY_PROVIDER = (pair) -> pair.getKey(); - private final JTablePanel hashsetHitsTable + private final JTablePanel> hashsetHitsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS) .setKeyFunction(DEFAULT_KEY_PROVIDER); - private final JTablePanel keywordHitsTable + private final JTablePanel> keywordHitsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS) .setKeyFunction(DEFAULT_KEY_PROVIDER); - private final JTablePanel interestingItemsTable + private final JTablePanel> interestingItemsTable = JTablePanel.getJTablePanel(DEFAULT_COLUMNS) .setKeyFunction(DEFAULT_KEY_PROVIDER); @@ -91,8 +90,9 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { keywordHitsTable, interestingItemsTable ); - + private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); + /** * All of the components necessary for data fetch swing workers to load data @@ -129,24 +129,14 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { initComponents(); } - /** - * Takes a base class of AnalysisCountRecord and provides the pertinent menu - * items. going to artifact. - * - * @param record The AnalysisCountRecord instance. - * @return The menu items list containing one action or navigating to the - * appropriate artifact and closing the data source summary dialog if open. - */ - private List getPopup(AnalysisCountRecord record) { - return record == null ? null : getNavigateToArtifactPopup(record.getArtifact()); - } - + @Override public void close() { ingestRunningLabel.unregister(); super.close(); } - + + @Override protected void fetchInformation(DataSource dataSource) { fetchInformation(dataFetchComponents, dataSource); @@ -172,17 +162,14 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { javax.swing.JLabel hashsetHitsLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2)); javax.swing.JPanel hashSetHitsPanel = hashsetHitsTable; - rightClickForMoreOptions1 = new javax.swing.JLabel(); javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(32767, 20)); javax.swing.JLabel keywordHitsLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler4 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2)); javax.swing.JPanel keywordHitsPanel = keywordHitsTable; - rightClickForMoreOptions2 = new javax.swing.JLabel(); javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(32767, 20)); javax.swing.JLabel interestingItemLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler6 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(32767, 2)); javax.swing.JPanel interestingItemPanel = interestingItemsTable; - rightClickForMoreOptions3 = new javax.swing.JLabel(); javax.swing.Box.Filler filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); mainContentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); @@ -205,9 +192,6 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { hashSetHitsPanel.setMinimumSize(new java.awt.Dimension(10, 106)); hashSetHitsPanel.setPreferredSize(new java.awt.Dimension(10, 106)); mainContentPanel.add(hashSetHitsPanel); - - org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions1, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.rightClickForMoreOptions1.text")); // NOI18N - mainContentPanel.add(rightClickForMoreOptions1); mainContentPanel.add(filler2); org.openide.awt.Mnemonics.setLocalizedText(keywordHitsLabel, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.keywordHitsLabel.text")); // NOI18N @@ -219,9 +203,6 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { keywordHitsPanel.setMinimumSize(new java.awt.Dimension(10, 106)); keywordHitsPanel.setPreferredSize(new java.awt.Dimension(10, 106)); mainContentPanel.add(keywordHitsPanel); - - org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions2, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.rightClickForMoreOptions2.text")); // NOI18N - mainContentPanel.add(rightClickForMoreOptions2); mainContentPanel.add(filler5); org.openide.awt.Mnemonics.setLocalizedText(interestingItemLabel, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.interestingItemLabel.text")); // NOI18N @@ -233,9 +214,6 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { interestingItemPanel.setMinimumSize(new java.awt.Dimension(10, 106)); interestingItemPanel.setPreferredSize(new java.awt.Dimension(10, 106)); mainContentPanel.add(interestingItemPanel); - - org.openide.awt.Mnemonics.setLocalizedText(rightClickForMoreOptions3, org.openide.util.NbBundle.getMessage(AnalysisPanel.class, "AnalysisPanel.rightClickForMoreOptions3.text")); // NOI18N - mainContentPanel.add(rightClickForMoreOptions3); mainContentPanel.add(filler3); mainScrollPane.setViewportView(mainContentPanel); @@ -254,8 +232,5 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel { // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JLabel rightClickForMoreOptions1; - private javax.swing.JLabel rightClickForMoreOptions2; - private javax.swing.JLabel rightClickForMoreOptions3; // End of variables declaration//GEN-END:variables } From 4367c6dc036594f086952a77bad71dc82b890524 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 17 Nov 2020 14:08:37 -0500 Subject: [PATCH 10/30] updates --- .../ui/BaseDataSourceSummaryPanel.java | 4 +-- .../ui/Bundle.properties-MERGED | 5 +-- .../ui/RecentFilesPanel.java | 2 +- .../ui/UserActivityPanel.java | 33 ++++++++++--------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java index d4ff6ef081..c3637df41a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java @@ -56,7 +56,7 @@ import org.sleuthkit.datamodel.TskCoreException; * Base class from which other tabs in data source summary derive. */ @Messages({"UserActivityPanel_goToArtifact=Go to Artifact", - "UserActivityPanel_goToArtifactContent=Go to File"}) + "UserActivityPanel_goToArtifactContent=Go to Artifact"}) abstract class BaseDataSourceSummaryPanel extends JPanel { private static final long serialVersionUID = 1L; @@ -263,7 +263,7 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { * @param artifact The artifact. * @return The menu item list for a go to artifact menu item. */ - protected List getNavigateToArtifactContentPopup(BlackboardArtifact artifact) { + protected List geNavigateToArtifactContentPopup(BlackboardArtifact artifact) { return artifact == null ? null : Arrays.asList( new CellModelTableCellRenderer.DefaultMenuItem( Bundle.UserActivityPanel_goToArtifactContent(), diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index a08dff4289..09b3c69e31 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -106,9 +106,6 @@ TimelinePanel.activityRangeLabel.text=Activity Range RecentFilesPanel.rightClickForMoreOptions1.text=Right click on row for more options RecentFilesPanel.rightClickForMoreOptions2.text=Right click on row for more options RecentFilesPanel.rightClickForMoreOptions3.text=Right click on row for more options -AnalysisPanel.rightClickForMoreOptions1.text=Right click on row for more options -AnalysisPanel.rightClickForMoreOptions2.text=Right click on row for more options -AnalysisPanel.rightClickForMoreOptions3.text=Right click on row for more options UserActivityPanel.rightClickForMoreOptions1.text=Right click on row for more options UserActivityPanel.rightClickForMoreOptions2.text=Right click on row for more options UserActivityPanel.rightClickForMoreOptions3.text=Right click on row for more options @@ -116,7 +113,7 @@ UserActivityPanel.rightClickForMoreOptions4.text=Right click on row for more opt UserActivityPanel.rightClickForMoreOptions5.text=Right click on row for more options TimelinePanel.clickToNavLabel.text=Click to view data source in the timeline UserActivityPanel_goToArtifact=Go to Artifact -UserActivityPanel_goToArtifactContent=Go to File +UserActivityPanel_goToArtifactContent=Go to Artifact UserActivityPanel_noDataExists=No communication data exists UserActivityPanel_tab_title=User Activity UserActivityPanel_TopAccountTableModel_accountType_header=Account Type diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java index ec4dd3fde2..021b1c4fa1 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java @@ -88,7 +88,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { * appropriate artifact and closing the data source summary dialog if open. */ private List getPopup(RecentFileDetails record) { - return record == null ? null : getNavigateToArtifactContentPopup(record.getArtifact()); + return record == null ? null : geNavigateToArtifactContentPopup(record.getArtifact()); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java index aeb3f7d6e8..32fce07859 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java @@ -95,7 +95,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { (prog) -> { return new DefaultCellModel(prog.getProgramName()) .setTooltip(prog.getProgramPath()) - .setPopupMenu(getPopup(prog)); + .setPopupMenu(getNavigateToArtifactPopup(prog.getArtifact())); }, 250), // program folder column @@ -107,7 +107,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { prog.getProgramPath(), prog.getProgramName())) .setTooltip(prog.getProgramPath()) - .setPopupMenu(getPopup(prog)); + .setPopupMenu(getNavigateToArtifactPopup(prog.getArtifact())); }, 150), // run count column @@ -116,7 +116,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { (prog) -> { String runTimes = prog.getRunTimes() == null ? "" : Long.toString(prog.getRunTimes()); return new DefaultCellModel(runTimes) - .setPopupMenu(getPopup(prog)); + .setPopupMenu(getNavigateToArtifactPopup(prog.getArtifact())); }, 80), // last run date column @@ -124,7 +124,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopProgramsTableModel_lastrun_header(), (prog) -> { return new DefaultCellModel(getFormatted(prog.getLastAccessed())) - .setPopupMenu(getPopup(prog)); + .setPopupMenu(getNavigateToArtifactPopup(prog.getArtifact())); }, 150) )) @@ -137,7 +137,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopDomainsTableModel_domain_header(), (recentDomain) -> { return new DefaultCellModel(recentDomain.getDomain()) - .setPopupMenu(getPopup(recentDomain)); + .setPopupMenu(getNavigateToArtifactPopup(recentDomain.getArtifact())); }, 250), // count column @@ -146,13 +146,16 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { (recentDomain) -> { String visitTimes = recentDomain.getVisitTimes() == null ? "" : Long.toString(recentDomain.getVisitTimes()); return new DefaultCellModel(visitTimes) - .setPopupMenu(getPopup(recentDomain)); + .setPopupMenu(getNavigateToArtifactPopup(recentDomain.getArtifact())); }, 100), // last accessed column new ColumnModel<>( Bundle.UserActivityPanel_TopDomainsTableModel_lastAccess_header(), - (recentDomain) -> new DefaultCellModel(getFormatted(recentDomain.getLastAccessed())), + (recentDomain) -> { + return new DefaultCellModel(getFormatted(recentDomain.getLastAccessed())) + .setPopupMenu(getNavigateToArtifactPopup(recentDomain.getArtifact())); + }, 150) )) .setKeyFunction((domain) -> domain.getDomain()); @@ -164,7 +167,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopWebSearchTableModel_searchString_header(), (webSearch) -> { return new DefaultCellModel(webSearch.getSearchString()) - .setPopupMenu(getPopup(webSearch)); + .setPopupMenu(getNavigateToArtifactPopup(webSearch.getArtifact())); }, 250 ), @@ -173,7 +176,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopWebSearchTableModel_dateAccessed_header(), (webSearch) -> { return new DefaultCellModel(getFormatted(webSearch.getLastAccessed())) - .setPopupMenu(getPopup(webSearch)); + .setPopupMenu(getNavigateToArtifactPopup(webSearch.getArtifact())); }, 150 ), @@ -182,7 +185,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopWebSearchTableModel_translatedResult_header(), (webSearch) -> { return new DefaultCellModel(webSearch.getTranslatedResult()) - .setPopupMenu(getPopup(webSearch)); + .setPopupMenu(getNavigateToArtifactPopup(webSearch.getArtifact())); }, 250 ) @@ -196,7 +199,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopDeviceAttachedTableModel_deviceId_header(), (device) -> { return new DefaultCellModel(device.getDeviceId()) - .setPopupMenu(getPopup(device)); + .setPopupMenu(getNavigateToArtifactPopup(device.getArtifact())); }, 250 ), @@ -205,7 +208,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header(), (device) -> { return new DefaultCellModel(getFormatted(device.getLastAccessed())) - .setPopupMenu(getPopup(device)); + .setPopupMenu(getNavigateToArtifactPopup(device.getArtifact())); }, 150 ), @@ -219,7 +222,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { ? make + model : String.format("%s - %s", make, model); return new DefaultCellModel(makeModelString) - .setPopupMenu(getPopup(device)); + .setPopupMenu(getNavigateToArtifactPopup(device.getArtifact())); }, 250 ) @@ -233,7 +236,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopAccountTableModel_accountType_header(), (account) -> { return new DefaultCellModel(account.getAccountType()) - .setPopupMenu(getPopup(account)); + .setPopupMenu(getNavigateToArtifactPopup(account.getArtifact())); }, 250 ), @@ -242,7 +245,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopAccountTableModel_lastAccess_header(), (account) -> { return new DefaultCellModel(getFormatted(account.getLastAccessed())) - .setPopupMenu(getPopup(account)); + .setPopupMenu(getNavigateToArtifactPopup(account.getArtifact())); }, 150 ) From 17848554858ca592f7d1ee1a2bd8c7cee332682e Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 19 Nov 2020 15:47:17 -0500 Subject: [PATCH 11/30] file navigation working --- .../ui/BaseDataSourceSummaryPanel.java | 111 ++++++++++++++---- .../ui/Bundle.properties-MERGED | 2 +- .../ui/RecentFilesPanel.java | 43 +++++-- .../ui/UserActivityPanel.java | 33 +++--- .../uiutils/CellModelTableCellRenderer.java | 23 +++- 5 files changed, 155 insertions(+), 57 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java index c3637df41a..155471c18c 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java @@ -19,17 +19,25 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.beans.PropertyChangeEvent; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.function.Predicate; import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.swing.JPanel; import javax.swing.SwingWorker; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; @@ -43,6 +51,7 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent; import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialExecutor; import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor; import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; +import org.sleuthkit.autopsy.directorytree.ViewContextAction; import org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent; import org.sleuthkit.autopsy.ingest.ModuleContentEvent; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; @@ -56,7 +65,7 @@ import org.sleuthkit.datamodel.TskCoreException; * Base class from which other tabs in data source summary derive. */ @Messages({"UserActivityPanel_goToArtifact=Go to Artifact", - "UserActivityPanel_goToArtifactContent=Go to Artifact"}) + "UserActivityPanel_goToFile=Go to File"}) abstract class BaseDataSourceSummaryPanel extends JPanel { private static final long serialVersionUID = 1L; @@ -238,22 +247,73 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { * closes data source summary dialog if open. * * @param artifact The artifact. - * @return The menu item list for a go to artifact menu item. + * @return The menu item for a go to artifact menu item. */ - protected List getNavigateToArtifactPopup(BlackboardArtifact artifact) { - return artifact == null ? null : Arrays.asList( - new CellModelTableCellRenderer.DefaultMenuItem( - Bundle.UserActivityPanel_goToArtifact(), - () -> { - final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance(); + protected CellModelTableCellRenderer.MenuItem getArtifactNavigateItem(BlackboardArtifact artifact) { + if (artifact == null) { + return null; + } - // Navigate to the source context artifact. - if (dtc != null && artifact != null) { - dtc.viewArtifact(artifact); - } + return new CellModelTableCellRenderer.DefaultMenuItem( + Bundle.UserActivityPanel_goToArtifact(), + () -> { + final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance(); - notifyParentClose(); - })); + // Navigate to the source context artifact. + if (dtc != null && artifact != null) { + dtc.viewArtifact(artifact); + } + + notifyParentClose(); + }); + } + + private static final Pattern windowsDrivePattern = Pattern.compile("^[A-Za-z]\\:(.*)$"); + + /** + * Normalizes the path for lookup in the sleuthkit database (unix endings; remove C:\). + * @param path The path to normalize. + * @return The normalized path. + */ + private String normalizePath(String path) { + if (path == null) { + return null; + } + + String trimmed = path.trim(); + Matcher match = windowsDrivePattern.matcher(trimmed); + if (match.find()) { + return FilenameUtils.normalize(match.group(1), true); + } else { + return FilenameUtils.normalize(trimmed, true); + } + } + + /** + * Creates a menu item to navigate to a file. + * + * @param path The path to the file. + * @return The menu item or null if file cannot be found in data source. + */ + protected CellModelTableCellRenderer.MenuItem getFileNavigateItem(String path) { + if (StringUtils.isNotBlank(path)) { + Path p = Paths.get(path); + String fileName = normalizePath(p.getFileName().toString()); + String directory = normalizePath(p.getParent().toString()); + + if (fileName != null && directory != null) { + try { + List files = Case.getCurrentCaseThrows().getSleuthkitCase().findFiles(getDataSource(), fileName, directory); + if (CollectionUtils.isNotEmpty(files)) { + return getFileNavigateItem(files.get(0)); + } + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "There was an error fetching file for path: " + path, ex); + } + } + } + + return null; } /** @@ -263,20 +323,19 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { * @param artifact The artifact. * @return The menu item list for a go to artifact menu item. */ - protected List geNavigateToArtifactContentPopup(BlackboardArtifact artifact) { - return artifact == null ? null : Arrays.asList( - new CellModelTableCellRenderer.DefaultMenuItem( - Bundle.UserActivityPanel_goToArtifactContent(), - () -> { - final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance(); + protected CellModelTableCellRenderer.MenuItem getFileNavigateItem(AbstractFile file) { + if (file == null) { + return null; + } - // Navigate to the source context artifact. - if (dtc != null && artifact != null) { - dtc.viewArtifactContent(artifact); - } + return new CellModelTableCellRenderer.DefaultMenuItem( + Bundle.UserActivityPanel_goToFile(), + () -> { + new ViewContextAction(Bundle.UserActivityPanel_goToFile(), file) + .actionPerformed(null); - notifyParentClose(); - })); + notifyParentClose(); + }); } /** diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index 09b3c69e31..fc3ebb09cd 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -113,7 +113,7 @@ UserActivityPanel.rightClickForMoreOptions4.text=Right click on row for more opt UserActivityPanel.rightClickForMoreOptions5.text=Right click on row for more options TimelinePanel.clickToNavLabel.text=Click to view data source in the timeline UserActivityPanel_goToArtifact=Go to Artifact -UserActivityPanel_goToArtifactContent=Go to Artifact +UserActivityPanel_goToFile=Go to File UserActivityPanel_noDataExists=No communication data exists UserActivityPanel_tab_title=User Activity UserActivityPanel_TopAccountTableModel_accountType_header=Account Type diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java index 021b1c4fa1..db708af9ec 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.Supplier; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary; @@ -81,14 +82,32 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { /** * Takes a base class of RecentFileDetails and provides the pertinent menu - * items. going to artifact. + * items. * * @param record The RecentFileDetails instance. * @return The menu items list containing one action or navigating to the - * appropriate artifact and closing the data source summary dialog if open. + * appropriate artifact/file and closing the data source summary dialog if + * open. */ - private List getPopup(RecentFileDetails record) { - return record == null ? null : geNavigateToArtifactContentPopup(record.getArtifact()); + private Supplier> getPopupFunct(RecentFileDetails record) { + return () -> { + if (record == null) { + return null; + } + + List toRet = new ArrayList<>(); + + MenuItem fileNav = getFileNavigateItem(record.getPath()); + if (fileNav != null) { + toRet.add(fileNav); + } + + if (record.getArtifact() != null) { + toRet.add(getArtifactNavigateItem(record.getArtifact())); + } + + return (toRet.size() > 0) ? toRet : null; + }; } @Override @@ -128,12 +147,12 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), (prog) -> { return new DefaultCellModel(prog.getPath()) - .setPopupMenu(getPopup(prog)); + .setPopupMenuRetriever(getPopupFunct(prog)); }, 250), new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(), (prog) -> { return new DefaultCellModel(prog.getDateAsString()) - .setPopupMenu(getPopup(prog)); + .setPopupMenuRetriever(getPopupFunct(prog)); }, 80)); ListTableModel tableModel = JTablePanel.getTableModel(list); @@ -166,17 +185,17 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { new ColumnModel<>(Bundle.RecentFilePanel_col_header_domain(), (prog) -> { return new DefaultCellModel(prog.getWebDomain()) - .setPopupMenu(getPopup(prog)); + .setPopupMenuRetriever(getPopupFunct(prog)); }, 100), new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), (prog) -> { return new DefaultCellModel(prog.getPath()) - .setPopupMenu(getPopup(prog)); + .setPopupMenuRetriever(getPopupFunct(prog)); }, 250), new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(), (prog) -> { return new DefaultCellModel(prog.getDateAsString()) - .setPopupMenu(getPopup(prog)); + .setPopupMenuRetriever(getPopupFunct(prog)); }, 80)); ListTableModel tableModel = JTablePanel.getTableModel(list); @@ -209,17 +228,17 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), (prog) -> { return new DefaultCellModel(prog.getPath()) - .setPopupMenu(getPopup(prog)); + .setPopupMenuRetriever(getPopupFunct(prog)); }, 250), new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(), (prog) -> { return new DefaultCellModel(prog.getDateAsString()) - .setPopupMenu(getPopup(prog)); + .setPopupMenuRetriever(getPopupFunct(prog)); }, 80), new ColumnModel<>(Bundle.RecentFilePanel_col_header_sender(), (prog) -> { return new DefaultCellModel(prog.getSender()) - .setPopupMenu(getPopup(prog)); + .setPopupMenuRetriever(getPopupFunct(prog)); }, 150)); ListTableModel tableModel = JTablePanel.getTableModel(list); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java index 32fce07859..afac5607e9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java @@ -41,6 +41,7 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetch import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel; +import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.DataSource; /** @@ -95,7 +96,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { (prog) -> { return new DefaultCellModel(prog.getProgramName()) .setTooltip(prog.getProgramPath()) - .setPopupMenu(getNavigateToArtifactPopup(prog.getArtifact())); + .setPopupMenu(getPopup(prog)); }, 250), // program folder column @@ -107,7 +108,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { prog.getProgramPath(), prog.getProgramName())) .setTooltip(prog.getProgramPath()) - .setPopupMenu(getNavigateToArtifactPopup(prog.getArtifact())); + .setPopupMenu(getPopup(prog)); }, 150), // run count column @@ -116,7 +117,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { (prog) -> { String runTimes = prog.getRunTimes() == null ? "" : Long.toString(prog.getRunTimes()); return new DefaultCellModel(runTimes) - .setPopupMenu(getNavigateToArtifactPopup(prog.getArtifact())); + .setPopupMenu(getPopup(prog)); }, 80), // last run date column @@ -124,7 +125,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopProgramsTableModel_lastrun_header(), (prog) -> { return new DefaultCellModel(getFormatted(prog.getLastAccessed())) - .setPopupMenu(getNavigateToArtifactPopup(prog.getArtifact())); + .setPopupMenu(getPopup(prog)); }, 150) )) @@ -137,7 +138,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopDomainsTableModel_domain_header(), (recentDomain) -> { return new DefaultCellModel(recentDomain.getDomain()) - .setPopupMenu(getNavigateToArtifactPopup(recentDomain.getArtifact())); + .setPopupMenu(getPopup(recentDomain)); }, 250), // count column @@ -146,7 +147,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { (recentDomain) -> { String visitTimes = recentDomain.getVisitTimes() == null ? "" : Long.toString(recentDomain.getVisitTimes()); return new DefaultCellModel(visitTimes) - .setPopupMenu(getNavigateToArtifactPopup(recentDomain.getArtifact())); + .setPopupMenu(getPopup(recentDomain)); }, 100), // last accessed column @@ -154,7 +155,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopDomainsTableModel_lastAccess_header(), (recentDomain) -> { return new DefaultCellModel(getFormatted(recentDomain.getLastAccessed())) - .setPopupMenu(getNavigateToArtifactPopup(recentDomain.getArtifact())); + .setPopupMenu(getPopup(recentDomain)); }, 150) )) @@ -167,7 +168,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopWebSearchTableModel_searchString_header(), (webSearch) -> { return new DefaultCellModel(webSearch.getSearchString()) - .setPopupMenu(getNavigateToArtifactPopup(webSearch.getArtifact())); + .setPopupMenu(getPopup(webSearch)); }, 250 ), @@ -176,7 +177,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopWebSearchTableModel_dateAccessed_header(), (webSearch) -> { return new DefaultCellModel(getFormatted(webSearch.getLastAccessed())) - .setPopupMenu(getNavigateToArtifactPopup(webSearch.getArtifact())); + .setPopupMenu(getPopup(webSearch)); }, 150 ), @@ -185,7 +186,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopWebSearchTableModel_translatedResult_header(), (webSearch) -> { return new DefaultCellModel(webSearch.getTranslatedResult()) - .setPopupMenu(getNavigateToArtifactPopup(webSearch.getArtifact())); + .setPopupMenu(getPopup(webSearch)); }, 250 ) @@ -199,7 +200,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopDeviceAttachedTableModel_deviceId_header(), (device) -> { return new DefaultCellModel(device.getDeviceId()) - .setPopupMenu(getNavigateToArtifactPopup(device.getArtifact())); + .setPopupMenu(getPopup(device)); }, 250 ), @@ -208,7 +209,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header(), (device) -> { return new DefaultCellModel(getFormatted(device.getLastAccessed())) - .setPopupMenu(getNavigateToArtifactPopup(device.getArtifact())); + .setPopupMenu(getPopup(device)); }, 150 ), @@ -222,7 +223,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { ? make + model : String.format("%s - %s", make, model); return new DefaultCellModel(makeModelString) - .setPopupMenu(getNavigateToArtifactPopup(device.getArtifact())); + .setPopupMenu(getPopup(device)); }, 250 ) @@ -236,7 +237,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopAccountTableModel_accountType_header(), (account) -> { return new DefaultCellModel(account.getAccountType()) - .setPopupMenu(getNavigateToArtifactPopup(account.getArtifact())); + .setPopupMenu(getPopup(account)); }, 250 ), @@ -245,7 +246,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { Bundle.UserActivityPanel_TopAccountTableModel_lastAccess_header(), (account) -> { return new DefaultCellModel(getFormatted(account.getLastAccessed())) - .setPopupMenu(getNavigateToArtifactPopup(account.getArtifact())); + .setPopupMenu(getPopup(account)); }, 150 ) @@ -339,7 +340,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { * appropriate artifact and closing the data source summary dialog if open. */ private List getPopup(LastAccessedArtifact record) { - return record == null ? null : getNavigateToArtifactPopup(record.getArtifact()); + return record == null ? null : Arrays.asList(getArtifactNavigateItem(record.getArtifact())); } /** diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java index 3511101da4..ed3f598a4e 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java @@ -20,11 +20,11 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.awt.Component; import java.awt.Insets; -import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Supplier; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JMenuItem; @@ -161,6 +161,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { private HorizontalAlign horizontalAlignment; private Insets insets; private List popupMenu; + private Supplier> menuItemSupplier; /** * Main constructor. @@ -230,7 +231,25 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { @Override public List getPopupMenu() { - return popupMenu == null ? null : Collections.unmodifiableList(popupMenu); + if (popupMenu != null) { + return Collections.unmodifiableList(popupMenu); + } + + if (menuItemSupplier != null) { + return this.menuItemSupplier.get(); + } + + return null; + } + + /** + * Sets a function to lazy load the popup menu items. + * @param menuItemSupplier The lazy load function for popup items. + * @return + */ + public DefaultCellModel setPopupMenuRetriever(Supplier> menuItemSupplier) { + this.menuItemSupplier = menuItemSupplier; + return this; } /** From bf32de479e40b5d5a9e3cd336ba765e37d216f81 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 20 Nov 2020 08:36:21 -0500 Subject: [PATCH 12/30] update for timeline button --- .../datamodel/TimelineSummary.java | 27 +++-- .../datasourcesummary/ui/Bundle.properties | 2 +- .../datasourcesummary/ui/TimelinePanel.form | 8 +- .../datasourcesummary/ui/TimelinePanel.java | 109 ++++++++++++------ 4 files changed, 95 insertions(+), 51 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java index c892c7eec8..46d4a0f827 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java @@ -38,19 +38,12 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.TimelineEvent; import org.sleuthkit.datamodel.TimelineEventType; -import org.sleuthkit.datamodel.TimelineFilter; -import org.sleuthkit.datamodel.TimelineFilter.DataSourcesFilter; import org.sleuthkit.datamodel.TimelineFilter.RootFilter; import org.sleuthkit.datamodel.TimelineManager; import org.sleuthkit.datamodel.TskCoreException; import java.util.function.Supplier; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.UserPreferences; -import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.TimeLineModule; -import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; -import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; -import org.sleuthkit.autopsy.timeline.utils.FilterUtils; /** * Provides data source summary information pertaining to Timeline data. @@ -89,9 +82,9 @@ public class TimelineSummary implements DefaultUpdateGovernor { /** * Construct object with given SleuthkitCaseProvider * - * @param caseProvider SleuthkitCaseProvider provider, cannot be null. - * @param timeZoneProvider The timezone provider, cannot be null. - * @param defaulteStateSupplier Provides the default root filter + * @param caseProvider SleuthkitCaseProvider provider; cannot be null. + * @param timeZoneProvider The timezone provider; cannot be null. + * @param defaulteStateSupplier Provides the default root filter function filtered to the data source; cannot be null. */ public TimelineSummary(SleuthkitCaseProvider caseProvider, Supplier timeZoneProvider, DataSourceFilterFunction filterFunction) { this.caseProvider = caseProvider; @@ -161,7 +154,7 @@ public class TimelineSummary implements DefaultUpdateGovernor { // get most recent days activity List mostRecentActivityAmt = getMostRecentActivityAmounts(dateCounts, minRecentDay, maxDay); - return new TimelineSummaryData(minDate, maxDate, mostRecentActivityAmt); + return new TimelineSummaryData(minDate, maxDate, mostRecentActivityAmt, dataSource); } /** @@ -240,6 +233,7 @@ public class TimelineSummary implements DefaultUpdateGovernor { private final Date minDate; private final Date maxDate; private final List histogramActivity; + private final DataSource dataSource; /** * Main constructor. @@ -247,12 +241,14 @@ public class TimelineSummary implements DefaultUpdateGovernor { * @param minDate Earliest usage date recorded for the data source. * @param maxDate Latest usage date recorded for the data source. * @param recentDaysActivity A list of activity prior to and including + * @param dataSource The data source for which this data applies. * the latest usage date by day. */ - TimelineSummaryData(Date minDate, Date maxDate, List recentDaysActivity) { + TimelineSummaryData(Date minDate, Date maxDate, List recentDaysActivity, DataSource dataSource) { this.minDate = minDate; this.maxDate = maxDate; this.histogramActivity = (recentDaysActivity == null) ? Collections.emptyList() : Collections.unmodifiableList(recentDaysActivity); + this.dataSource = dataSource; } /** @@ -276,6 +272,13 @@ public class TimelineSummary implements DefaultUpdateGovernor { public List getMostRecentDaysActivity() { return histogramActivity; } + + /** + * @return The data source that this data applies to. + */ + public DataSource getDataSource() { + return dataSource; + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties index a68f4b2b11..f71abb831e 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties @@ -51,4 +51,4 @@ UserActivityPanel.rightClickForMoreOptions2.text=Right click on row for more opt UserActivityPanel.rightClickForMoreOptions3.text=Right click on row for more options UserActivityPanel.rightClickForMoreOptions4.text=Right click on row for more options UserActivityPanel.rightClickForMoreOptions5.text=Right click on row for more options -TimelinePanel.clickToNavLabel.text=Click to view data source in the timeline +TimelinePanel.viewInTimelineBtn.text=View in Timeline diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.form index 030472d3e9..7fc0a1151c 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.form +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.form @@ -193,12 +193,16 @@ - + - + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java index 9c24b3ce94..12da8f60cf 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java @@ -19,19 +19,17 @@ package org.sleuthkit.autopsy.datasourcesummary.ui; import java.awt.Color; -import java.awt.Cursor; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.logging.Level; import org.apache.commons.collections.CollectionUtils; +import org.joda.time.DateTime; +import org.joda.time.Interval; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; @@ -52,13 +50,7 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel; import org.sleuthkit.autopsy.timeline.OpenTimelineAction; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineModule; -import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; -import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; -import org.sleuthkit.autopsy.timeline.utils.FilterUtils; import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.TimelineEventType; -import org.sleuthkit.datamodel.TimelineFilter; -import org.sleuthkit.datamodel.TimelineFilter.DataSourceFilter; import org.sleuthkit.datamodel.TskCoreException; /** @@ -119,7 +111,6 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { ); initComponents(); - setupChartClickListener(); } /** @@ -172,6 +163,9 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_fileEvts_title(), FILE_EVT_COLOR, fileEvtCounts), new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_artifactEvts_title(), ARTIFACT_EVT_COLOR, artifactEvtCounts)); } + + private final Object timelineBtnLock = new Object(); + private TimelineSummaryData curTimelineData = null; /** * Handles displaying the result for each displayable item in the @@ -185,6 +179,61 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT))); latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT))); last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity()))); + + if (result != null + && result.getResultType() == DataFetchResult.ResultType.SUCCESS + && result.getData() != null) { + + synchronized (this.timelineBtnLock) { + this.curTimelineData = result.getData(); + this.viewInTimelineBtn.setEnabled(true); + } + } else { + synchronized (this.timelineBtnLock) { + this.viewInTimelineBtn.setEnabled(false); + } + } + } + + /** + * Action that occurs when 'View in Timeline' button is pressed. + */ + private void openFilteredChart() { + DataSource dataSource = null; + Date minDate = null; + Date maxDate = null; + + + // get date from current timelineData if that data exists. + synchronized (this.timelineBtnLock) { + if (curTimelineData == null) { + return; + } + + dataSource = curTimelineData.getDataSource(); + minDate = curTimelineData.getMinDate(); + maxDate = curTimelineData.getMaxDate(); + } + + // notify dialog (if in dialog) should close. + TimelinePanel.this.notifyParentClose(); + + // open the timeline filtered to data source and zoomed in on interval + openTimelineAction.performAction(); + try { + TimeLineController controller = TimeLineModule.getController(); + if (dataSource != null) { + controller.pushFilters(timelineUtils.getDataSourceFilterState(dataSource)); + } + + if (minDate != null && maxDate != null) { + Interval timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate)); + controller.pushTimeRange(timeSpan); + } + + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.WARNING, "Unable to open Timeline view", ex); + } } @Override @@ -221,7 +270,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { javax.swing.JPanel latestLabelPanel = latestLabel; javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); javax.swing.JPanel last30DaysPanel = last30DaysChart; - clickToNavLabel = new javax.swing.JLabel(); + viewInTimelineBtn = new javax.swing.JButton(); javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); mainContentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); @@ -263,8 +312,14 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { last30DaysPanel.setVerifyInputWhenFocusTarget(false); mainContentPanel.add(last30DaysPanel); - org.openide.awt.Mnemonics.setLocalizedText(clickToNavLabel, org.openide.util.NbBundle.getMessage(TimelinePanel.class, "TimelinePanel.clickToNavLabel.text")); // NOI18N - mainContentPanel.add(clickToNavLabel); + org.openide.awt.Mnemonics.setLocalizedText(viewInTimelineBtn, org.openide.util.NbBundle.getMessage(TimelinePanel.class, "TimelinePanel.viewInTimelineBtn.text")); // NOI18N + viewInTimelineBtn.setEnabled(false); + viewInTimelineBtn.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + viewInTimelineBtnActionPerformed(evt); + } + }); + mainContentPanel.add(viewInTimelineBtn); filler5.setAlignmentX(0.0F); mainContentPanel.add(filler5); @@ -283,29 +338,11 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { ); }// //GEN-END:initComponents - private void setupChartClickListener() { - this.last30DaysChart.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - this.last30DaysChart.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - - TimelinePanel.this.notifyParentClose(); - openTimelineAction.performAction(); - try { - TimeLineController controller = TimeLineModule.getController(); - DataSource dataSource = getDataSource(); - if (dataSource != null) { - controller.pushFilters(timelineUtils.getDataSourceFilterState(dataSource)); - } - } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.WARNING, "Unable to open Timeline view", ex); - } - } - }); - } - + private void viewInTimelineBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewInTimelineBtnActionPerformed + openFilteredChart(); + }//GEN-LAST:event_viewInTimelineBtnActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JLabel clickToNavLabel; + private javax.swing.JButton viewInTimelineBtn; // End of variables declaration//GEN-END:variables } From 47b61790d33701f161f89828607aed912d0dd6c4 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 20 Nov 2020 09:21:37 -0500 Subject: [PATCH 13/30] some small fixes --- .../datamodel/RecentFilesSummary.java | 2 +- .../datasourcesummary/datamodel/TimelineSummary.java | 4 ++-- .../datasourcesummary/ui/Bundle.properties-MERGED | 2 +- .../autopsy/datasourcesummary/ui/TimelinePanel.java | 10 ++++++++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java index 5b158e61be..81e44f7db6 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java @@ -298,7 +298,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor { if (date == null || date == 0 || StringUtils.isBlank(path)) { return null; } else { - return new RecentAttachmentDetails(artifact, path, date, sender); + return new RecentAttachmentDetails(messageArtifact, path, date, sender); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java index 46d4a0f827..451e678153 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java @@ -240,7 +240,7 @@ public class TimelineSummary implements DefaultUpdateGovernor { * * @param minDate Earliest usage date recorded for the data source. * @param maxDate Latest usage date recorded for the data source. - * @param recentDaysActivity A list of activity prior to and including + * @param recentDaysActivity A list of activity prior to and including max date sorted by min to max date. * @param dataSource The data source for which this data applies. * the latest usage date by day. */ @@ -267,7 +267,7 @@ public class TimelineSummary implements DefaultUpdateGovernor { /** * @return A list of activity prior to and including the latest usage - * date by day. + * date by day sorted min to max date. */ public List getMostRecentDaysActivity() { return histogramActivity; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index fc3ebb09cd..5b7a37c275 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -111,7 +111,7 @@ UserActivityPanel.rightClickForMoreOptions2.text=Right click on row for more opt UserActivityPanel.rightClickForMoreOptions3.text=Right click on row for more options UserActivityPanel.rightClickForMoreOptions4.text=Right click on row for more options UserActivityPanel.rightClickForMoreOptions5.text=Right click on row for more options -TimelinePanel.clickToNavLabel.text=Click to view data source in the timeline +TimelinePanel.viewInTimelineBtn.text=View in Timeline UserActivityPanel_goToArtifact=Go to Artifact UserActivityPanel_goToFile=Go to File UserActivityPanel_noDataExists=No communication data exists diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java index 12da8f60cf..6936ce93bb 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java @@ -211,8 +211,14 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { } dataSource = curTimelineData.getDataSource(); - minDate = curTimelineData.getMinDate(); - maxDate = curTimelineData.getMaxDate(); + if (CollectionUtils.isNotEmpty(curTimelineData.getMostRecentDaysActivity())) { + minDate = curTimelineData.getMostRecentDaysActivity().get(0).getDay(); + maxDate = curTimelineData.getMostRecentDaysActivity().get(curTimelineData.getMostRecentDaysActivity().size() - 1).getDay(); + // set outer bound to end of day instead of beginning + if (maxDate != null) { + maxDate = new Date(maxDate.getTime() + 1000 * 60 * 60 * 24); + } + } } // notify dialog (if in dialog) should close. From 6a136ee28e1c02f784077cd191be6640868af3a1 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 20 Nov 2020 11:11:16 -0500 Subject: [PATCH 14/30] formatting --- .../datamodel/TimelineDataSourceUtils.java | 4 +- .../datamodel/TimelineSummary.java | 34 +++++++++---- .../ui/BaseDataSourceSummaryPanel.java | 4 +- .../ui/DataSourceSummaryDialog.java | 8 +-- .../ui/RecentFilesPanel.form | 12 +++++ .../ui/RecentFilesPanel.java | 9 ++-- .../datasourcesummary/ui/TimelinePanel.java | 17 +++---- .../ui/UserActivityPanel.form | 20 ++++++++ .../ui/UserActivityPanel.java | 16 ++---- .../uiutils/BarChartPanel.java | 40 +-------------- .../uiutils/CellModelTableCellRenderer.java | 19 +++++-- .../uiutils/JTablePanel.java | 50 ++++++++++++++++--- .../datamodel/UserActivitySummaryTest.java | 24 ++++----- 13 files changed, 153 insertions(+), 104 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java index 3e094300bf..40f76cce80 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineDataSourceUtils.java @@ -85,9 +85,9 @@ public class TimelineDataSourceUtils { for (FilterState filterState : dataSourceState.getDataSourcesFilterState().getSubFilterStates()) { DataSourceFilter dsFilter = filterState.getFilter(); if (dsFilter != null) { - filterState.setSelected(dsFilter.getDataSourceID() == dataSource.getId()); + filterState.setSelected(dsFilter.getDataSourceID() == dataSource.getId()); } - + } return dataSourceState; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java index 451e678153..a43d46764b 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/TimelineSummary.java @@ -50,11 +50,23 @@ import org.sleuthkit.autopsy.core.UserPreferences; */ public class TimelineSummary implements DefaultUpdateGovernor { - + /** + * A function for obtaining a Timeline RootFilter filtered to the specific + * data source. + */ public interface DataSourceFilterFunction { + + /** + * Obtains a Timeline RootFilter filtered to the specific data source. + * + * @param dataSource The data source. + * @return The timeline root filter. + * @throws NoCurrentCaseException + * @throws TskCoreException + */ RootFilter apply(DataSource dataSource) throws NoCurrentCaseException, TskCoreException; } - + private static final long DAY_SECS = 24 * 60 * 60; private static final Set INGEST_JOB_EVENTS = new HashSet<>( Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED)); @@ -69,13 +81,13 @@ public class TimelineSummary implements DefaultUpdateGovernor { private final SleuthkitCaseProvider caseProvider; private final Supplier timeZoneProvider; private final DataSourceFilterFunction filterFunction; - + /** * Default constructor. */ public TimelineSummary() { - this(SleuthkitCaseProvider.DEFAULT, - () -> TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()), + this(SleuthkitCaseProvider.DEFAULT, + () -> TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()), (ds) -> TimelineDataSourceUtils.getInstance().getDataSourceFilter(ds)); } @@ -84,7 +96,8 @@ public class TimelineSummary implements DefaultUpdateGovernor { * * @param caseProvider SleuthkitCaseProvider provider; cannot be null. * @param timeZoneProvider The timezone provider; cannot be null. - * @param defaulteStateSupplier Provides the default root filter function filtered to the data source; cannot be null. + * @param filterFunction Provides the default root filter function filtered + * to the data source; cannot be null. */ public TimelineSummary(SleuthkitCaseProvider caseProvider, Supplier timeZoneProvider, DataSourceFilterFunction filterFunction) { this.caseProvider = caseProvider; @@ -193,7 +206,7 @@ public class TimelineSummary implements DefaultUpdateGovernor { * @throws TskCoreException * @throws NoCurrentCaseException */ - private Map getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone) + private Map getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone) throws TskCoreException, NoCurrentCaseException { RootFilter rootFilter = this.filterFunction.apply(dataSource); @@ -240,9 +253,10 @@ public class TimelineSummary implements DefaultUpdateGovernor { * * @param minDate Earliest usage date recorded for the data source. * @param maxDate Latest usage date recorded for the data source. - * @param recentDaysActivity A list of activity prior to and including max date sorted by min to max date. - * @param dataSource The data source for which this data applies. - * the latest usage date by day. + * @param recentDaysActivity A list of activity prior to and including + * max date sorted by min to max date. + * @param dataSource The data source for which this data applies. the + * latest usage date by day. */ TimelineSummaryData(Date minDate, Date maxDate, List recentDaysActivity, DataSource dataSource) { this.minDate = minDate; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java index 155471c18c..62c804db84 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java @@ -271,7 +271,9 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { private static final Pattern windowsDrivePattern = Pattern.compile("^[A-Za-z]\\:(.*)$"); /** - * Normalizes the path for lookup in the sleuthkit database (unix endings; remove C:\). + * Normalizes the path for lookup in the sleuthkit database (unix endings; + * remove C:\). + * * @param path The path to normalize. * @return The normalized path. */ diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.java index b506437887..97ca3cc6f2 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDialog.java @@ -40,7 +40,7 @@ import org.sleuthkit.datamodel.IngestJobInfo; * Dialog for displaying the Data Sources Summary information */ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Observer { - + private static final long serialVersionUID = 1L; private static final Set INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED); private final DataSourceBrowser dataSourcesPanel; @@ -71,7 +71,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser this.repaint(); } }); - + ingestEventListener = (PropertyChangeEvent evt) -> { if (evt instanceof DataSourceAnalysisCompletedEvent) { DataSourceAnalysisCompletedEvent dsEvent = (DataSourceAnalysisCompletedEvent) evt; @@ -91,7 +91,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); this.pack(); } - + @Override public void dispose() { IngestManager.getInstance().removeIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, ingestEventListener); @@ -105,7 +105,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser void enableObserver() { dataSourcesPanel.addObserver(this); } - + @Override public void update(Observable o, Object arg) { this.dispose(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.form index 8a8ae70a58..77e6cd8262 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.form +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.form @@ -166,6 +166,10 @@ + + + + @@ -178,6 +182,10 @@ + + + + @@ -190,6 +198,10 @@ + + + + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java index db708af9ec..2f94f2d598 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java @@ -278,9 +278,9 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { javax.swing.JLabel openDocsLabel = new javax.swing.JLabel(); javax.swing.JLabel downloadLabel = new javax.swing.JLabel(); javax.swing.JLabel attachmentLabel = new javax.swing.JLabel(); - rightClickForMoreOptions1 = new javax.swing.JLabel(); - rightClickForMoreOptions2 = new javax.swing.JLabel(); - rightClickForMoreOptions3 = new javax.swing.JLabel(); + javax.swing.JLabel rightClickForMoreOptions1 = new javax.swing.JLabel(); + javax.swing.JLabel rightClickForMoreOptions2 = new javax.swing.JLabel(); + javax.swing.JLabel rightClickForMoreOptions3 = new javax.swing.JLabel(); setLayout(new java.awt.BorderLayout()); @@ -386,8 +386,5 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { private javax.swing.JPanel attachmentsPane; private javax.swing.JPanel downloadsPane; private javax.swing.JPanel openedDocPane; - private javax.swing.JLabel rightClickForMoreOptions1; - private javax.swing.JLabel rightClickForMoreOptions2; - private javax.swing.JLabel rightClickForMoreOptions3; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java index 6936ce93bb..490df9a481 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java @@ -163,7 +163,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_fileEvts_title(), FILE_EVT_COLOR, fileEvtCounts), new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_artifactEvts_title(), ARTIFACT_EVT_COLOR, artifactEvtCounts)); } - + private final Object timelineBtnLock = new Object(); private TimelineSummaryData curTimelineData = null; @@ -183,10 +183,10 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { if (result != null && result.getResultType() == DataFetchResult.ResultType.SUCCESS && result.getData() != null) { - + synchronized (this.timelineBtnLock) { this.curTimelineData = result.getData(); - this.viewInTimelineBtn.setEnabled(true); + this.viewInTimelineBtn.setEnabled(true); } } else { synchronized (this.timelineBtnLock) { @@ -202,14 +202,13 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { DataSource dataSource = null; Date minDate = null; Date maxDate = null; - // get date from current timelineData if that data exists. synchronized (this.timelineBtnLock) { if (curTimelineData == null) { return; } - + dataSource = curTimelineData.getDataSource(); if (CollectionUtils.isNotEmpty(curTimelineData.getMostRecentDaysActivity())) { minDate = curTimelineData.getMostRecentDaysActivity().get(0).getDay(); @@ -220,10 +219,10 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { } } } - + // notify dialog (if in dialog) should close. TimelinePanel.this.notifyParentClose(); - + // open the timeline filtered to data source and zoomed in on interval openTimelineAction.performAction(); try { @@ -231,12 +230,12 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { if (dataSource != null) { controller.pushFilters(timelineUtils.getDataSourceFilterState(dataSource)); } - + if (minDate != null && maxDate != null) { Interval timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate)); controller.pushTimeRange(timeSpan); } - + } catch (NoCurrentCaseException | TskCoreException ex) { logger.log(Level.WARNING, "Unable to open Timeline view", ex); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.form index a1a04bbe17..f3920eeba0 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.form @@ -141,6 +141,10 @@ + + + + @@ -217,6 +221,10 @@ + + + + @@ -293,6 +301,10 @@ + + + + @@ -369,6 +381,10 @@ + + + + @@ -445,6 +461,10 @@ + + + + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java index afac5607e9..68ff255b0c 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/UserActivityPanel.java @@ -41,7 +41,6 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetch import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.DataSource; /** @@ -386,27 +385,27 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { javax.swing.JLabel programsRunLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); javax.swing.JPanel topProgramsTablePanel = topProgramsTable; - rightClickForMoreOptions1 = new javax.swing.JLabel(); + javax.swing.JLabel rightClickForMoreOptions1 = new javax.swing.JLabel(); javax.swing.Box.Filler filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); javax.swing.JLabel recentDomainsLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); javax.swing.JPanel recentDomainsTablePanel = recentDomainsTable; - rightClickForMoreOptions2 = new javax.swing.JLabel(); + javax.swing.JLabel rightClickForMoreOptions2 = new javax.swing.JLabel(); javax.swing.Box.Filler filler4 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); javax.swing.JLabel topWebSearchLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); javax.swing.JPanel topWebSearches = topWebSearchesTable; - rightClickForMoreOptions3 = new javax.swing.JLabel(); + javax.swing.JLabel rightClickForMoreOptions3 = new javax.swing.JLabel(); javax.swing.Box.Filler filler6 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); javax.swing.JLabel topDevicesAttachedLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler7 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); javax.swing.JPanel recentDevicesAttached = topDevicesAttachedTable; - rightClickForMoreOptions4 = new javax.swing.JLabel(); + javax.swing.JLabel rightClickForMoreOptions4 = new javax.swing.JLabel(); javax.swing.Box.Filler filler8 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20), new java.awt.Dimension(0, 20)); javax.swing.JLabel recentAccountsLabel = new javax.swing.JLabel(); javax.swing.Box.Filler filler9 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2), new java.awt.Dimension(0, 2)); javax.swing.JPanel topAccounts = topAccountsTable; - rightClickForMoreOptions5 = new javax.swing.JLabel(); + javax.swing.JLabel rightClickForMoreOptions5 = new javax.swing.JLabel(); setLayout(new java.awt.BorderLayout()); @@ -505,10 +504,5 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel { }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JLabel rightClickForMoreOptions1; - private javax.swing.JLabel rightClickForMoreOptions2; - private javax.swing.JLabel rightClickForMoreOptions3; - private javax.swing.JLabel rightClickForMoreOptions4; - private javax.swing.JLabel rightClickForMoreOptions5; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java index 6e1db485f5..88ab51d563 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java @@ -21,12 +21,9 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; import java.util.Collections; import java.util.List; import javax.swing.JLabel; -import javax.swing.JPanel; import org.apache.commons.collections4.CollectionUtils; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; @@ -255,44 +252,10 @@ public class BarChartPanel extends AbstractLoadableComponent> menuItemSupplier) { this.menuItemSupplier = menuItemSupplier; @@ -329,6 +332,10 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { return defaultCell; } + /** + * The default cell mouse listener that triggers popups for non-primary + * button events. + */ private static final CellMouseListener DEFAULT_CELL_MOUSE_LISTENER = new CellMouseListener() { @Override @@ -355,6 +362,10 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { } }; + /** + * @return The default cell mouse listener that triggers popups for + * non-primary button events. + */ public static CellMouseListener getMouseListener() { return DEFAULT_CELL_MOUSE_LISTENER; } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java index ec055a42b9..3f694d78c9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java @@ -42,13 +42,27 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRendere */ public class JTablePanel extends AbstractLoadableComponent> { + /** + * An event that wraps a swing MouseEvent also providing context within the + * table cell. + */ public static class CellMouseEvent { + private final MouseEvent e; private final JTable table; private final int row; private final int col; private final Object cellValue; + /** + * Main constructor. + * + * @param e The underlying mouse event. + * @param table The table that was the target of the mouse event. + * @param row The row within the table that the event occurs. + * @param col The column within the table that the event occurs. + * @param cellValue The value within the cell. + */ public CellMouseEvent(MouseEvent e, JTable table, int row, int col, Object cellValue) { this.e = e; this.table = table; @@ -57,27 +71,42 @@ public class JTablePanel extends AbstractLoadableComponent> { this.cellValue = cellValue; } + /** + * @return The underlying mouse event. + */ public MouseEvent getMouseEvent() { return e; } + /** + * @return The table that was the target of the mouse event. + */ public JTable getTable() { return table; } + /** + * @return The row within the table that the event occurs. + */ public int getRow() { return row; } + /** + * @return The column within the table that the event occurs. + */ public int getCol() { return col; } + /** + * @return The value within the cell. + */ public Object getCellValue() { return cellValue; } } - + /** * Handles mouse events for cells in the table. */ @@ -86,7 +115,8 @@ public class JTablePanel extends AbstractLoadableComponent> { /** * Handles mouse events at a cell level for the table. * - * @param e The event containing information about the cell, the mouse event, and the table. + * @param e The event containing information about the cell, the mouse + * event, and the table. */ void mouseClicked(CellMouseEvent e); } @@ -251,7 +281,7 @@ public class JTablePanel extends AbstractLoadableComponent> { JTablePanel resultTable = new JTablePanel<>(tableModel) .setColumnModel(getTableColumnModel(columns)) .setCellListener(CellModelTableCellRenderer.getMouseListener()); - + return resultTable; } @@ -317,17 +347,25 @@ public class JTablePanel extends AbstractLoadableComponent> { return this; } - + /** + * @return The current listener for mouse events. The events provided to + * this listener will have cell and table context. + */ public CellMouseListener getCellListener() { return cellListener; } + /** + * Sets the current listener for mouse events. + * + * @param cellListener The event listener that will receive these events + * with cell and table context. + * @return + */ public JTablePanel setCellListener(CellMouseListener cellListener) { this.cellListener = cellListener; return this; } - - /** * @return The underlying JTable's column model. diff --git a/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummaryTest.java b/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummaryTest.java index 3c62dc95b1..47bdf71fc8 100644 --- a/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummaryTest.java +++ b/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummaryTest.java @@ -353,7 +353,7 @@ public class UserActivitySummaryTest { List results = summary.getRecentDevices(dataSource, 10); Assert.assertEquals(1, results.size()); - Assert.assertEquals((long) (DAY_SECONDS + 2), results.get(0).getDateAccessed().getTime() / 1000); + Assert.assertEquals((long) (DAY_SECONDS + 2), results.get(0).getLastAccessed().getTime() / 1000); Assert.assertTrue("ID1".equalsIgnoreCase(results.get(0).getDeviceId())); Assert.assertTrue("MAKE1".equalsIgnoreCase(results.get(0).getDeviceMake())); Assert.assertTrue("MODEL1".equalsIgnoreCase(results.get(0).getDeviceModel())); @@ -896,13 +896,13 @@ public class UserActivitySummaryTest { getRecentAccountsOneArtTest(ds1, email1, new TopAccountResult( Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(), - new Date(DAY_SECONDS * 1000))); + new Date(DAY_SECONDS * 1000), email1)); BlackboardArtifact email2 = getEmailArtifact(2, ds1, null, DAY_SECONDS); getRecentAccountsOneArtTest(ds1, email2, new TopAccountResult( Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(), - new Date(DAY_SECONDS * 1000))); + new Date(DAY_SECONDS * 1000), email2)); BlackboardArtifact email3 = getEmailArtifact(3, ds1, null, null); getRecentAccountsOneArtTest(ds1, email3, null); @@ -911,19 +911,19 @@ public class UserActivitySummaryTest { getRecentAccountsOneArtTest(ds1, email4, new TopAccountResult( Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(), - new Date(DAY_SECONDS * 2 * 1000))); + new Date(DAY_SECONDS * 2 * 1000), email4)); BlackboardArtifact callog1 = getCallogArtifact(11, ds1, DAY_SECONDS, null); getRecentAccountsOneArtTest(ds1, callog1, new TopAccountResult( Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(), - new Date(DAY_SECONDS * 1000))); + new Date(DAY_SECONDS * 1000), callog1)); BlackboardArtifact callog2 = getCallogArtifact(12, ds1, null, DAY_SECONDS); getRecentAccountsOneArtTest(ds1, callog2, new TopAccountResult( Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(), - new Date(DAY_SECONDS * 1000))); + new Date(DAY_SECONDS * 1000), callog2)); BlackboardArtifact callog3 = getCallogArtifact(13, ds1, null, null); getRecentAccountsOneArtTest(ds1, callog3, null); @@ -932,7 +932,7 @@ public class UserActivitySummaryTest { getRecentAccountsOneArtTest(ds1, callog4, new TopAccountResult( Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(), - new Date(DAY_SECONDS * 2 * 1000))); + new Date(DAY_SECONDS * 2 * 1000), callog4)); BlackboardArtifact message1 = getMessageArtifact(21, ds1, "Skype", null); getRecentAccountsOneArtTest(ds1, message1, null); @@ -944,7 +944,7 @@ public class UserActivitySummaryTest { getRecentAccountsOneArtTest(ds1, message3, null); BlackboardArtifact message4 = getMessageArtifact(24, ds1, "Skype", DAY_SECONDS); - getRecentAccountsOneArtTest(ds1, message4, new TopAccountResult("Skype", new Date(DAY_SECONDS * 1000))); + getRecentAccountsOneArtTest(ds1, message4, new TopAccountResult("Skype", new Date(DAY_SECONDS * 1000), message4)); } @@ -977,10 +977,10 @@ public class UserActivitySummaryTest { getRecentAccountsTest(ds1, 10, Arrays.asList(email1, email2, email3, callog1, callog2, message1a, message1b, message2a, message2b), Arrays.asList( - new TopAccountResult("Facebook", new Date((DAY_SECONDS + 42) * 1000)), - new TopAccountResult("Skype", new Date((DAY_SECONDS + 32) * 1000)), - new TopAccountResult(Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(), new Date((DAY_SECONDS + 22) * 1000)), - new TopAccountResult(Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(), new Date((DAY_SECONDS + 13) * 1000)) + new TopAccountResult("Facebook", new Date((DAY_SECONDS + 42) * 1000), message2b), + new TopAccountResult("Skype", new Date((DAY_SECONDS + 32) * 1000), message1b), + new TopAccountResult(Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(), new Date((DAY_SECONDS + 22) * 1000), callog2), + new TopAccountResult(Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(), new Date((DAY_SECONDS + 13) * 1000), email3) )); } From 27c6283959e257e52b2dafb8ec4e72f0258028c5 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 20 Nov 2020 11:20:30 -0500 Subject: [PATCH 15/30] formatting --- .../datasourcesummary/datamodel/UserActivitySummaryTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummaryTest.java b/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummaryTest.java index 47bdf71fc8..572d4f86b3 100644 --- a/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummaryTest.java +++ b/Core/test/unit/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/UserActivitySummaryTest.java @@ -870,6 +870,7 @@ public class UserActivitySummaryTest { // since this may be somewhat variable Assert.assertTrue(expectedItem.getAccountType().equalsIgnoreCase(receivedItem.getAccountType())); Assert.assertEquals(expectedItem.getLastAccessed().getTime(), receivedItem.getLastAccessed().getTime()); + Assert.assertEquals(expectedItem.getArtifact(), receivedItem.getArtifact()); } } From e015b93eb571de9e7e4bbce456715121724a211c Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 23 Nov 2020 13:32:49 -0500 Subject: [PATCH 16/30] timeline time range fix --- .../datasourcesummary/ui/TimelinePanel.java | 71 ++++++++++++------- .../autopsy/timeline/OpenTimelineAction.java | 42 +++++++---- .../autopsy/timeline/TimeLineController.java | 26 +++---- 3 files changed, 86 insertions(+), 53 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java index 490df9a481..cd56754579 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java @@ -31,6 +31,7 @@ import org.apache.commons.collections.CollectionUtils; import org.joda.time.DateTime; import org.joda.time.Interval; import org.openide.util.NbBundle.Messages; +import org.openide.util.actions.CallableSystemAction; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineDataSourceUtils; @@ -64,7 +65,7 @@ import org.sleuthkit.datamodel.TskCoreException; "TimlinePanel_last30DaysChart_fileEvts_title=File Events", "TimlinePanel_last30DaysChart_artifactEvts_title=Artifact Events",}) public class TimelinePanel extends BaseDataSourceSummaryPanel { - + private static final Logger logger = Logger.getLogger(TimelinePanel.class.getName()); private static final long serialVersionUID = 1L; private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat("MMM d, yyyy"); @@ -86,7 +87,6 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { private final LoadableLabel earliestLabel = new LoadableLabel(Bundle.TimelinePanel_earliestLabel_title()); private final LoadableLabel latestLabel = new LoadableLabel(Bundle.TimelinePanel_latestLabel_title()); private final BarChartPanel last30DaysChart = new BarChartPanel(Bundle.TimlinePanel_last30DaysChart_title(), "", ""); - private final OpenTimelineAction openTimelineAction = new OpenTimelineAction(); private final TimelineDataSourceUtils timelineUtils = TimelineDataSourceUtils.getInstance(); // all loadable components on this tab @@ -94,7 +94,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { // actions to load data for this tab private final List> dataFetchComponents; - + public TimelinePanel() { this(new TimelineSummary()); } @@ -109,7 +109,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { (dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT), (result) -> handleResult(result)) ); - + initComponents(); } @@ -125,7 +125,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { private static String formatDate(Date date, DateFormat formatter) { return date == null ? null : formatter.format(date); } - + private static final Color FILE_EVT_COLOR = new Color(228, 22, 28); private static final Color ARTIFACT_EVT_COLOR = new Color(21, 227, 100); @@ -145,25 +145,25 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { // Create a bar chart item for each recent days activity item List fileEvtCounts = new ArrayList<>(); List artifactEvtCounts = new ArrayList<>(); - + for (int i = 0; i < recentDaysActivity.size(); i++) { DailyActivityAmount curItem = recentDaysActivity.get(i); - + long fileAmt = curItem.getFileActivityCount(); long artifactAmt = curItem.getArtifactActivityCount() * 100; String formattedDate = (i == 0 || i == recentDaysActivity.size() - 1) ? formatDate(curItem.getDay(), CHART_FORMAT) : ""; - + OrderedKey thisKey = new OrderedKey(formattedDate, i); fileEvtCounts.add(new BarChartItem(thisKey, fileAmt)); artifactEvtCounts.add(new BarChartItem(thisKey, artifactAmt)); } - + return Arrays.asList( new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_fileEvts_title(), FILE_EVT_COLOR, fileEvtCounts), new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_artifactEvts_title(), ARTIFACT_EVT_COLOR, artifactEvtCounts)); } - + private final Object timelineBtnLock = new Object(); private TimelineSummaryData curTimelineData = null; @@ -179,11 +179,11 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT))); latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT))); last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity()))); - + if (result != null && result.getResultType() == DataFetchResult.ResultType.SUCCESS && result.getData() != null) { - + synchronized (this.timelineBtnLock) { this.curTimelineData = result.getData(); this.viewInTimelineBtn.setEnabled(true); @@ -208,7 +208,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { if (curTimelineData == null) { return; } - + dataSource = curTimelineData.getDataSource(); if (CollectionUtils.isNotEmpty(curTimelineData.getMostRecentDaysActivity())) { minDate = curTimelineData.getMostRecentDaysActivity().get(0).getDay(); @@ -219,38 +219,59 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { } } } + + openFilteredChart(dataSource, minDate, maxDate); + } + + /** + * Action that occurs when 'View in Timeline' button is pressed. + * + * @param dataSource The data source to filter to. + * @param minDate The min date for the zoom of the window. + * @param maxDate The max date for the zoom of the window. + */ + private void openFilteredChart(DataSource dataSource, Date minDate, Date maxDate) { + OpenTimelineAction openTimelineAction = CallableSystemAction.get(OpenTimelineAction.class); + if (openTimelineAction == null) { + logger.log(Level.WARNING, "No OpenTimelineAction provided by CallableSystemAction; taking no redirect action."); + } // notify dialog (if in dialog) should close. TimelinePanel.this.notifyParentClose(); - - // open the timeline filtered to data source and zoomed in on interval - openTimelineAction.performAction(); + + Interval timeSpan = null; + try { - TimeLineController controller = TimeLineModule.getController(); + final TimeLineController controller = TimeLineModule.getController(); + if (dataSource != null) { controller.pushFilters(timelineUtils.getDataSourceFilterState(dataSource)); } - + if (minDate != null && maxDate != null) { - Interval timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate)); - controller.pushTimeRange(timeSpan); + timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate)); } - } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.WARNING, "Unable to open Timeline view", ex); + logger.log(Level.WARNING, "Unable to view time range in Timeline view", ex); + } + + try { + openTimelineAction.showTimeline(timeSpan); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "An unexpected exception occurred while opening the timeline.", ex); } } - + @Override protected void fetchInformation(DataSource dataSource) { fetchInformation(dataFetchComponents, dataSource); } - + @Override protected void onNewDataSource(DataSource dataSource) { onNewDataSource(dataFetchComponents, loadableComponents, dataSource); } - + @Override public void close() { ingestRunningLabel.unregister(); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java index 202e521dc2..fdd45d14a1 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java @@ -24,6 +24,7 @@ import javafx.application.Platform; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JMenuItem; +import org.joda.time.Interval; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; @@ -46,13 +47,10 @@ import org.sleuthkit.datamodel.TskCoreException; * An Action that opens the Timeline window. Has methods to open the window in * various specific states (e.g., showing a specific artifact in the List View) */ - - @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline") @ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false) @ActionReferences(value = { - @ActionReference(path = "Menu/Tools", position = 104) - , + @ActionReference(path = "Menu/Tools", position = 104), @ActionReference(path = "Toolbars/Case", position = 104)}) public final class OpenTimelineAction extends CallableSystemAction { @@ -64,7 +62,6 @@ public final class OpenTimelineAction extends CallableSystemAction { private final JButton toolbarButton = new JButton(getName(), new ImageIcon(getClass().getResource("images/btn_icon_timeline_colorized_26.png"))); //NON-NLS - public OpenTimelineAction() { toolbarButton.addActionListener(actionEvent -> performAction()); menuItem = super.getMenuPresenter(); @@ -74,9 +71,10 @@ public final class OpenTimelineAction extends CallableSystemAction { @Override public boolean isEnabled() { /** - * We used to also check if Case.getCurrentOpenCase().hasData() was true. We - * disabled that check because if it is executed while a data source is - * being added, it blocks the edt. We still do that in ImageGallery. + * We used to also check if Case.getCurrentOpenCase().hasData() was + * true. We disabled that check because if it is executed while a data + * source is being added, it blocks the edt. We still do that in + * ImageGallery. */ return super.isEnabled() && Case.isCaseOpen() && Installer.isJavaFxInited(); } @@ -103,7 +101,7 @@ public final class OpenTimelineAction extends CallableSystemAction { @NbBundle.Messages({ "OpenTimelineAction.settingsErrorMessage=Failed to initialize timeline settings.", "OpenTimeLineAction.msgdlg.text=Could not create timeline, there are no data sources."}) - synchronized private void showTimeline(AbstractFile file, BlackboardArtifact artifact) throws TskCoreException { + synchronized private void showTimeline(AbstractFile file, BlackboardArtifact artifact, Interval timeSpan) throws TskCoreException { try { Case currentCase = Case.getCurrentCaseThrows(); if (currentCase.hasData() == false) { @@ -112,6 +110,15 @@ public final class OpenTimelineAction extends CallableSystemAction { return; } TimeLineController controller = TimeLineModule.getController(); + // if file or artifact not specified, specify the time range as either + // a) full range if timeSpan is null or b) the timeSpan + if (file == null && artifact == null) { + if (timeSpan == null) { + controller.showFullRange(); + } else { + controller.pushTimeRange(timeSpan); + } + } controller.showTimeLine(file, artifact); } catch (NoCurrentCaseException e) { //there is no case... Do nothing. @@ -123,7 +130,18 @@ public final class OpenTimelineAction extends CallableSystemAction { */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public void showTimeline() throws TskCoreException { - showTimeline(null, null); + showTimeline(null, null, null); + } + + /** + * Open timeline with the given timeSpan time range. + * + * @param timeSpan The time range to display. + * @throws TskCoreException + */ + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) + public void showTimeline(Interval timeSpan) throws TskCoreException { + showTimeline(null, null, timeSpan); } /** @@ -135,7 +153,7 @@ public final class OpenTimelineAction extends CallableSystemAction { */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public void showFileInTimeline(AbstractFile file) throws TskCoreException { - showTimeline(file, null); + showTimeline(file, null, null); } /** @@ -146,7 +164,7 @@ public final class OpenTimelineAction extends CallableSystemAction { */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public void showArtifactInTimeline(BlackboardArtifact artifact) throws TskCoreException { - showTimeline(null, artifact); + showTimeline(null, artifact, null); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 0c80cb5315..80dba865f5 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -62,7 +62,6 @@ import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; -import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import static org.sleuthkit.autopsy.casemodule.Case.Events.DATA_SOURCE_ADDED; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; @@ -348,7 +347,7 @@ public class TimeLineController { /** * Show the entire range of the timeline. */ - private boolean showFullRange() throws TskCoreException { + boolean showFullRange() throws TskCoreException { synchronized (filteredEvents) { return pushTimeRange(filteredEvents.getSpanningInterval()); } @@ -359,7 +358,7 @@ public class TimeLineController { * ViewInTimelineRequestedEvent in the List View. * * @param requestEvent Contains the ID of the requested events and the - * timerange to show. + * timerange to show. */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) private void showInListView(ViewInTimelineRequestedEvent requestEvent) throws TskCoreException { @@ -376,14 +375,13 @@ public class TimeLineController { } } - /** * Shuts down the task executor in charge of handling case events. */ void shutDownTimeLineListeners() { ThreadUtils.shutDownTaskExecutor(executor); } - + /** * "Shut down" Timeline. Close the timeline window. */ @@ -394,15 +392,13 @@ public class TimeLineController { topComponent = null; } } - - /** * Add the case and ingest listeners, prompt for rebuilding the database if * necessary, and show the timeline window. * - * @param file The AbstractFile from which to choose an event to show in - * the List View. + * @param file The AbstractFile from which to choose an event to show in the + * List View. * @param artifact The BlackboardArtifact to show in the List View. */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) @@ -421,9 +417,7 @@ public class TimeLineController { try { if (file == null && artifact == null) { SwingUtilities.invokeLater(TimeLineController.this::showWindow); - this.showFullRange(); } else { - //prompt user to pick specific event and time range ShowInTimelineDialog showInTimelineDilaog = (file == null) ? new ShowInTimelineDialog(this, artifact) @@ -453,7 +447,7 @@ public class TimeLineController { * around the middle of the currently viewed time range. * * @param period The period of time to show around the current center of the - * view. + * view. */ synchronized public void pushPeriod(ReadablePeriod period) throws TskCoreException { synchronized (filteredEvents) { @@ -496,8 +490,8 @@ public class TimeLineController { */ topComponent.requestActive(); } - - synchronized public TimeLineTopComponent getTopComponent(){ + + synchronized public TimeLineTopComponent getTopComponent() { return topComponent; } @@ -517,7 +511,7 @@ public class TimeLineController { * @param timeRange The Interval to view. * * @return True if the interval was changed. False if the interval was the - * same as the existing one and no change happened. + * same as the existing one and no change happened. */ synchronized public boolean pushTimeRange(Interval timeRange) throws TskCoreException { //clamp timerange to case @@ -709,7 +703,7 @@ public class TimeLineController { * Register the given object to receive events. * * @param listener The object to register. Must implement public methods - * annotated with Subscribe. + * annotated with Subscribe. */ synchronized public void registerForEvents(Object listener) { eventbus.register(listener); From 7844b79cb68b05ac10e175c3d2ff888978cd61c0 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 2 Dec 2020 08:57:45 -0500 Subject: [PATCH 17/30] travis shell script fix --- travis_build.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/travis_build.sh b/travis_build.sh index e40bd161f4..a561ccd21e 100755 --- a/travis_build.sh +++ b/travis_build.sh @@ -15,10 +15,7 @@ echo "Testing Autopsy..." && echo -en 'travis_fold:start:script.tests\\r' echo "Free Space:" echo `df -h .` -if [ "${TRAVIS_OS_NAME}" = "osx" ]; then - # if os x, just run it - # ant -q test-no-regression -elif [ "${TRAVIS_OS_NAME}" = "linux" ]; then +if [ "${TRAVIS_OS_NAME}" = "linux" ]; then # if linux use xvfb xvfb-run ant -q test-no-regression fi From b8d4f72cda660c2724e99657025a5ecc6f6f5336 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 2 Dec 2020 09:04:39 -0500 Subject: [PATCH 18/30] os x image fix --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index dd6b50f34a..6c06754d37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ jobs: - os: linux dist: bionic - os: osx + osx_image: xcode12.2 env: global: From 28dead68ad9d54d73d347c0e16fbf1872266ee79 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 2 Dec 2020 10:22:32 -0500 Subject: [PATCH 19/30] update to 12u --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6c06754d37..baeb5dd86a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ jobs: - os: linux dist: bionic - os: osx - osx_image: xcode12.2 + osx_image: xcode12u env: global: From 1d056f34d4e5a88f02895342bbe12784f234e585 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 2 Dec 2020 11:34:51 -0500 Subject: [PATCH 20/30] revert to 12.2 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index baeb5dd86a..6c06754d37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ jobs: - os: linux dist: bionic - os: osx - osx_image: xcode12u + osx_image: xcode12.2 env: global: From 1a5b0c2b14f110dffe63a0e077c817a310860a80 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 2 Dec 2020 15:34:55 -0500 Subject: [PATCH 21/30] Sorted context veiwer panels by date time --- .../contextviewer/ContextSourcePanel.java | 18 +++-- .../contextviewer/ContextUsagePanel.java | 17 +++-- .../contextviewer/ContextViewer.java | 71 +++++++++++++++++-- 3 files changed, 92 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextSourcePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextSourcePanel.java index 284a66cf04..02bd3f2ae6 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextSourcePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextSourcePanel.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.contentviewers.contextviewer; import java.util.ArrayList; import java.util.List; +import org.sleuthkit.autopsy.contentviewers.contextviewer.ContextViewer.DateTimePanel; import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; import org.sleuthkit.datamodel.BlackboardArtifact; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; @@ -29,27 +30,36 @@ import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOC * usage, if known. * */ -public final class ContextSourcePanel extends javax.swing.JPanel { +public final class ContextSourcePanel extends javax.swing.JPanel implements DateTimePanel { private static final long serialVersionUID = 1L; // defines a list of artifacts that provide context for a file private static final List SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>(); + static { SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT); } private final BlackboardArtifact sourceContextArtifact; + private final Long dateTime; + /** * Creates new form ContextViewer */ - public ContextSourcePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact) { + public ContextSourcePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact, Long dateTime) { initComponents(); sourceContextArtifact = associatedArtifact; setSourceName(sourceName); setSourceText(sourceText); + this.dateTime = dateTime; + } + + @Override + public Long getDateTime() { + return dateTime; } /** @@ -140,10 +150,10 @@ public final class ContextSourcePanel extends javax.swing.JPanel { private void showSourceText(boolean show) { jSourceTextLabel.setVisible(show); } - + private void showSourceButton(boolean show) { jSourceGoToResultButton.setVisible(show); - jSourceGoToResultButton.setEnabled(show); + jSourceGoToResultButton.setEnabled(show); } // Variables declaration - do not modify//GEN-BEGIN:variables diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextUsagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextUsagePanel.java index de87d7a5e5..4a96dfcffd 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextUsagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextUsagePanel.java @@ -29,27 +29,36 @@ import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOC * usage, if known. * */ -public final class ContextUsagePanel extends javax.swing.JPanel { +public final class ContextUsagePanel extends javax.swing.JPanel implements ContextViewer.DateTimePanel { private static final long serialVersionUID = 1L; // defines a list of artifacts that provide context for a file private static final List SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>(); + static { SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT); } private final BlackboardArtifact sourceContextArtifact; + private final Long dateTime; + /** * Creates new form ContextViewer */ - public ContextUsagePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact) { + public ContextUsagePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact, Long dateTime) { initComponents(); sourceContextArtifact = associatedArtifact; setUsageName(sourceName); setUsageText(sourceText); + this.dateTime = dateTime; + } + + @Override + public Long getDateTime() { + return dateTime; } /** @@ -138,10 +147,10 @@ public final class ContextUsagePanel extends javax.swing.JPanel { private void showUsageText(boolean show) { jUsageTextLabel.setVisible(show); } - + private void showUsageButton(boolean show) { jUsageGoToResultButton.setVisible(show); - jUsageGoToResultButton.setEnabled(show); + jUsageGoToResultButton.setEnabled(show); } // Variables declaration - do not modify//GEN-BEGIN:variables diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.java index 77cee389f6..c223f577b0 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.java @@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.contentviewers.contextviewer; import java.awt.Component; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -56,8 +58,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte // defines a list of artifacts that provide context for a file private static final List CONTEXT_ARTIFACTS = new ArrayList<>(); - private final List contextSourcePanels = new ArrayList<>(); - private final List contextUsagePanels = new ArrayList<>(); + private final List contextSourcePanels = new ArrayList<>(); + private final List contextUsagePanels = new ArrayList<>(); static { CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT); @@ -338,32 +340,36 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte "ContextViewer.programExecution=Program Execution: " }) private void addArtifactToPanels(BlackboardArtifact associatedArtifact) throws TskCoreException { + Long dateTime = getArtifactDateTime(associatedArtifact); if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID() || BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == associatedArtifact.getArtifactTypeID()) { String sourceName = Bundle.ContextViewer_attachmentSource(); String sourceText = msgArtifactToAbbreviatedString(associatedArtifact); - javax.swing.JPanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact); + ContextSourcePanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact, dateTime); contextSourcePanels.add(sourcePanel); } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID() || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == associatedArtifact.getArtifactTypeID()) { String sourceName = Bundle.ContextViewer_downloadSource(); String sourceText = webDownloadArtifactToString(associatedArtifact); - javax.swing.JPanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact); + ContextSourcePanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact, dateTime); contextSourcePanels.add(sourcePanel); } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID() == associatedArtifact.getArtifactTypeID()) { String sourceName = Bundle.ContextViewer_recentDocs(); String sourceText = recentDocArtifactToString(associatedArtifact); - javax.swing.JPanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact); + ContextUsagePanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact, dateTime); contextUsagePanels.add(usagePanel); } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() == associatedArtifact.getArtifactTypeID()) { String sourceName = Bundle.ContextViewer_programExecution(); String sourceText = programExecArtifactToString(associatedArtifact); - javax.swing.JPanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact); + ContextUsagePanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact, dateTime); contextUsagePanels.add(usagePanel); } + + Collections.sort(contextSourcePanels, new SortByDateTime()); + Collections.sort(contextUsagePanels, new SortByDateTime()); } /** @@ -532,6 +538,59 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte return attributeMap; } + + interface DateTimePanel { + /** + * Return the date time value for this panel. + * + * @return Date time value or null of one is not available. + */ + Long getDateTime(); + } + + /** + * Return the dateTime value for the given message artifact. + * + * @param artifact + * + * @return Long dateTime value or null if the attribute was not found. + * + * @throws TskCoreException + */ + private Long getArtifactDateTime(BlackboardArtifact artifact) throws TskCoreException { + BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)); + + if (BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == artifact.getArtifactTypeID()) { + attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); + } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == artifact.getArtifactTypeID() + || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == artifact.getArtifactTypeID()) { + attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED)); + } + return (attribute != null ? attribute.getValueLong() : null); + } + + /** + * Class for sorting lists of DateTimePanels. + */ + class SortByDateTime implements Comparator { + + @Override + public int compare(DateTimePanel panel1, DateTimePanel panel2) { + Long dateTime1 = panel1.getDateTime(); + Long dateTime2 = panel2.getDateTime(); + + if(dateTime1 == null && dateTime2 == null) { + return 0; + } else if(dateTime1 == null) { + return -1; + } else if(dateTime2 == null) { + return 1; + } + + return dateTime1.compareTo(dateTime2); + } + + } // Variables declaration - do not modify//GEN-BEGIN:variables From c2bcc99fcd32bc1767bb86982d9f5b9dd3c70aec Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 3 Dec 2020 08:18:55 -0500 Subject: [PATCH 22/30] move to private final var instead of function --- .../ui/DataSourceSummaryTabbedPane.java | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java index 0dde613158..5094e3416e 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java @@ -123,7 +123,18 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { private Runnable notifyParentClose = null; private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel(); - private final List tabs; + private final List tabs = Arrays.asList( + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_typesTab_title(), new TypesPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new UserActivityPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_analysisTab_title(), new AnalysisPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_pastCasesTab_title(), new PastCasesPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_timelineTab_title(), new TimelinePanel()), + // do nothing on closing + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource, () -> { + }), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel()) + ); private DataSource dataSource = null; private CardLayout cardLayout; @@ -132,7 +143,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { * Creates new form TabPane */ public DataSourceSummaryTabbedPane() { - this.tabs = getTabs(); initComponents(); postInit(); } @@ -155,23 +165,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { notifyParentClose = parentCloseAction; } - private List getTabs() { - List tabs = Arrays.asList( - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_typesTab_title(), new TypesPanel()), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new UserActivityPanel()), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_analysisTab_title(), new AnalysisPanel()), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_pastCasesTab_title(), new PastCasesPanel()), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_timelineTab_title(), new TimelinePanel()), - // do nothing on closing - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource, () -> { - }), - new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel()) - ); - - return tabs; - } - /** * Method called right after initComponents during initialization. */ From aa9900c3385787a44facf3665140a0423afddc14 Mon Sep 17 00:00:00 2001 From: apriestman Date: Thu, 3 Dec 2020 13:57:30 -0500 Subject: [PATCH 23/30] Fix health monitor upgrade --- .../org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java index c5c457c81b..f5bddca2d0 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java @@ -157,8 +157,8 @@ public final class HealthMonitor implements PropertyChangeListener { if (!databaseIsInitialized()) { initializeDatabaseSchema(); } - - if (!CURRENT_DB_SCHEMA_VERSION.equals(getVersion())) { + + if (getVersion().compareTo(CURRENT_DB_SCHEMA_VERSION) < 0) { upgradeDatabaseSchema(); } @@ -207,7 +207,7 @@ public final class HealthMonitor implements PropertyChangeListener { if (currentSchema.compareTo(new CaseDbSchemaVersionNumber(1, 2)) < 0) { // Add the user_data table - statement.execute("ALTER TABLE user_data ADD COLUMN username text"); + statement.execute("ALTER TABLE user_data ADD COLUMN IF NOT EXISTS username text"); } // Update the schema version From a2413676f9a6c2f2fe483f1201d5b3e2e0306819 Mon Sep 17 00:00:00 2001 From: apriestman Date: Thu, 3 Dec 2020 14:02:55 -0500 Subject: [PATCH 24/30] Add warning --- .../org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java index f5bddca2d0..2a2fc63463 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java @@ -187,6 +187,10 @@ public final class HealthMonitor implements PropertyChangeListener { try (Statement statement = conn.createStatement()) { conn.setAutoCommit(false); + // NOTE: Due to a bug in the upgrade code, earlier versions of Autopsy will erroneously + // run the upgrade if the database is a higher version than it expects. Therefore all + // table changes must account for the possiblility of running multiple times. + // Upgrade from 1.0 to 1.1 // Changes: user_data table added if (currentSchema.compareTo(new CaseDbSchemaVersionNumber(1, 1)) < 0) { From 1b2eae9ec8d1c395a648414c89d856b497046144 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 3 Dec 2020 15:14:15 -0500 Subject: [PATCH 25/30] update menu item text --- .../ui/BaseDataSourceSummaryPanel.java | 10 +++++----- .../datasourcesummary/ui/Bundle.properties-MERGED | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java index 62c804db84..2fe46822a3 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java @@ -64,8 +64,8 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Base class from which other tabs in data source summary derive. */ -@Messages({"UserActivityPanel_goToArtifact=Go to Artifact", - "UserActivityPanel_goToFile=Go to File"}) +@Messages({"BaseDataSourceSummaryPanel_goToArtifact=View Source Result", + "BaseDataSourceSummaryPanel_goToFile=View Source File in Directory"}) abstract class BaseDataSourceSummaryPanel extends JPanel { private static final long serialVersionUID = 1L; @@ -255,7 +255,7 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { } return new CellModelTableCellRenderer.DefaultMenuItem( - Bundle.UserActivityPanel_goToArtifact(), + Bundle.BaseDataSourceSummaryPanel_goToArtifact(), () -> { final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance(); @@ -331,9 +331,9 @@ abstract class BaseDataSourceSummaryPanel extends JPanel { } return new CellModelTableCellRenderer.DefaultMenuItem( - Bundle.UserActivityPanel_goToFile(), + Bundle.BaseDataSourceSummaryPanel_goToFile(), () -> { - new ViewContextAction(Bundle.UserActivityPanel_goToFile(), file) + new ViewContextAction(Bundle.BaseDataSourceSummaryPanel_goToFile(), file) .actionPerformed(null); notifyParentClose(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index 5b7a37c275..3f02cd37ed 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -3,6 +3,8 @@ AnalysisPanel_keyColumn_title=Name AnalysisPanel_keywordSearchModuleName=Keyword Search # {0} - module name BaseDataSourceSummaryPanel_defaultNotIngestMessage=The {0} ingest module has not been run on this data source. +BaseDataSourceSummaryPanel_goToArtifact=View Source Result +BaseDataSourceSummaryPanel_goToFile=View Source File in Directory ContainerPanel_setFieldsForNonImageDataSource_na=N/A CTL_DataSourceSummaryAction=Data Source Summary DataSourceSummaryDialog.closeButton.text=Close @@ -112,8 +114,6 @@ UserActivityPanel.rightClickForMoreOptions3.text=Right click on row for more opt UserActivityPanel.rightClickForMoreOptions4.text=Right click on row for more options UserActivityPanel.rightClickForMoreOptions5.text=Right click on row for more options TimelinePanel.viewInTimelineBtn.text=View in Timeline -UserActivityPanel_goToArtifact=Go to Artifact -UserActivityPanel_goToFile=Go to File UserActivityPanel_noDataExists=No communication data exists UserActivityPanel_tab_title=User Activity UserActivityPanel_TopAccountTableModel_accountType_header=Account Type From deaa670c0e9b4b867a843f89dc3ad71162d3ee66 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 3 Dec 2020 19:37:06 -0500 Subject: [PATCH 26/30] Improved the icon search algorithm by adding new types --- .../search/DomainSearchThumbnailLoader.java | 69 ++++++++++++++----- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchThumbnailLoader.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchThumbnailLoader.java index 4a17ad6434..fbcc2e2d55 100755 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchThumbnailLoader.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchThumbnailLoader.java @@ -22,6 +22,7 @@ import com.google.common.cache.CacheLoader; import java.awt.Image; import java.util.List; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -47,8 +48,12 @@ import org.openide.util.ImageUtilities; public class DomainSearchThumbnailLoader extends CacheLoader { private static final String UNSUPPORTED_IMAGE = "org/sleuthkit/autopsy/images/image-extraction-not-supported.png"; - private static final String JPG_EXTENSION = "jpg"; - private static final String JPG_MIME_TYPE = "image/jpeg"; + private static final List SUPPORTED_EXTENSIONS = Arrays.asList("jpg", "svg", "png", "webp", "ico", "gif"); + private static final List SUPPORTED_MIMETYPES = Arrays.asList( + "image/gif", "image/jpeg", "image/png", "image/webp", + "image/svg+xml", "image/vnd.microsoft.icon", "image/x-icon"); + private static final String ICO_EXTENSION = "ico"; + private static final List ICO_MIMETYPES = Arrays.asList("image/vnd.microsoft.icon", "image/x-icon"); private final DomainSearchArtifactsCache artifactsCache; /** @@ -75,8 +80,21 @@ public class DomainSearchThumbnailLoader extends CacheLoader webDownloads = artifactsCache.get(webDownloadsRequest); - final List webDownloadPictures = getJpegsFromWebDownload(caseDb, webDownloads); - Collections.sort(webDownloadPictures, (file1, file2) -> Long.compare(file1.getCrtime(), file2.getCrtime())); + final List webDownloadPictures = getCandidatesFromWebDownloads(caseDb, webDownloads); + Collections.sort(webDownloadPictures, (file1, file2) -> { + // Push ICO to the back of the sorted collection, so that ICO + // is a last resort matching type. + if (isIco(file1) && isIco(file2)) { + return Long.compare(file1.getCrtime(), file2.getCrtime()); + } else if (isIco(file1)) { + return 1; + } else if (isIco(file2)) { + return -1; + } else { + return Long.compare(file1.getCrtime(), file2.getCrtime()); + } + }); + for (int i = webDownloadPictures.size() - 1; i >= 0; i--) { if(Thread.currentThread().isInterrupted()) { throw new InterruptedException(); @@ -92,8 +110,20 @@ public class DomainSearchThumbnailLoader extends CacheLoader webCacheArtifacts = artifactsCache.get(webCacheRequest); - final List webCachePictures = getJpegsFromWebCache(caseDb, webCacheArtifacts); - Collections.sort(webCachePictures, (file1, file2) -> Long.compare(file1.getSize(), file2.getSize())); + final List webCachePictures = getCandidatesFromWebCache(caseDb, webCacheArtifacts); + Collections.sort(webCachePictures, (file1, file2) -> { + // Push ICO to the back of the sorted collection, so that ICO + // is a last resort matching type. + if (isIco(file1) && isIco(file2)) { + return Long.compare(file1.getSize(), file2.getSize()); + } else if (isIco(file1)) { + return 1; + } else if (isIco(file2)) { + return -1; + } else { + return Long.compare(file1.getSize(), file2.getSize()); + } + }); for (int i = webCachePictures.size() - 1; i >= 0; i--) { if(Thread.currentThread().isInterrupted()) { throw new InterruptedException(); @@ -119,16 +149,21 @@ public class DomainSearchThumbnailLoader extends CacheLoader getJpegsFromWebDownload(SleuthkitCase caseDb, List artifacts) throws TskCoreException, InterruptedException { - final List jpegs = new ArrayList<>(); + private List getCandidatesFromWebDownloads(SleuthkitCase caseDb, List artifacts) throws TskCoreException, InterruptedException { + final List candidates = new ArrayList<>(); for (BlackboardArtifact artifact : artifacts) { if(Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } final Content sourceContent = caseDb.getContentById(artifact.getObjectID()); - addIfJpeg(jpegs, sourceContent); + addIfSupported(candidates, sourceContent); } - return jpegs; + return candidates; + } + + private boolean isIco(AbstractFile file) { + return ICO_EXTENSION.equals(file.getNameExtension()) + || ICO_MIMETYPES.contains(file.getMIMEType()); } /** @@ -140,9 +175,9 @@ public class DomainSearchThumbnailLoader extends CacheLoader getJpegsFromWebCache(SleuthkitCase caseDb, List artifacts) throws TskCoreException, InterruptedException { + private List getCandidatesFromWebCache(SleuthkitCase caseDb, List artifacts) throws TskCoreException, InterruptedException { final BlackboardAttribute.Type TSK_PATH_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH_ID); - final List jpegs = new ArrayList<>(); + final List candidates = new ArrayList<>(); for (BlackboardArtifact artifact : artifacts) { if(Thread.currentThread().isInterrupted()) { throw new InterruptedException(); @@ -150,10 +185,10 @@ public class DomainSearchThumbnailLoader extends CacheLoader files, Content sourceContent) { + private void addIfSupported(List files, Content sourceContent) { if ((sourceContent instanceof AbstractFile) && !(sourceContent instanceof DataSource)) { final AbstractFile file = (AbstractFile) sourceContent; - if (JPG_EXTENSION.equals(file.getNameExtension()) - || JPG_MIME_TYPE.equals(file.getMIMEType())) { + if (SUPPORTED_EXTENSIONS.contains(file.getNameExtension()) + || SUPPORTED_MIMETYPES.contains(file.getMIMEType())) { files.add(file); } } From e2a337a9cbce8c62903e12d96668234be05175d6 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 3 Dec 2020 19:40:56 -0500 Subject: [PATCH 27/30] Updated comments --- .../search/DomainSearchThumbnailLoader.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchThumbnailLoader.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchThumbnailLoader.java index fbcc2e2d55..888cc5379b 100755 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchThumbnailLoader.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearchThumbnailLoader.java @@ -39,9 +39,10 @@ import org.sleuthkit.datamodel.TskCoreException; import org.openide.util.ImageUtilities; /** - * Loads a thumbnail for the given request. Thumbnail candidates are JPEG files - * that have either TSK_WEB_DOWNLOAD or TSK_WEB_CACHE artifacts that match the - * domain name (see the DomainSearch getArtifacts() API). JPEG files are sorted + * Loads a thumbnail for the given request. Thumbnail candidates types are defined below. + * These candidates types must be the source of either TSK_WEB_DOWNLOAD or + * TSK_WEB_CACHE artifacts that match the + * domain name (see the DomainSearch getArtifacts() API). Candidate files are sorted * by most recent if sourced from TSK_WEB_DOWNLOADs and by size if sourced from * TSK_WEB_CACHE artifacts. The first suitable thumbnail is selected. */ @@ -139,12 +140,12 @@ public class DomainSearchThumbnailLoader extends CacheLoader getCandidatesFromWebCache(SleuthkitCase caseDb, List artifacts) throws TskCoreException, InterruptedException { @@ -192,10 +193,9 @@ public class DomainSearchThumbnailLoader extends CacheLoader files, Content sourceContent) { From 49414ef9a21e07611d0a7697438e495c3d29cd1d Mon Sep 17 00:00:00 2001 From: apriestman Date: Fri, 4 Dec 2020 08:21:05 -0500 Subject: [PATCH 28/30] Change upgrade code since "IF NOT EXISTS" does not work for add column in PG 9.5 --- .../autopsy/healthmonitor/HealthMonitor.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java index 2a2fc63463..4ad0e23eda 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java @@ -183,6 +183,7 @@ public final class HealthMonitor implements PropertyChangeListener { if (conn == null) { throw new HealthMonitorException("Error getting database connection"); } + ResultSet resultSet = null; try (Statement statement = conn.createStatement()) { conn.setAutoCommit(false); @@ -210,8 +211,13 @@ public final class HealthMonitor implements PropertyChangeListener { // Changes: username added to user_data table if (currentSchema.compareTo(new CaseDbSchemaVersionNumber(1, 2)) < 0) { - // Add the user_data table - statement.execute("ALTER TABLE user_data ADD COLUMN IF NOT EXISTS username text"); + resultSet = statement.executeQuery("SELECT column_name " + + "FROM information_schema.columns " + + "WHERE table_name='user_data' and column_name='username'"); + if (! resultSet.next()) { + // Add the user_data table + statement.execute("ALTER TABLE user_data ADD COLUMN username text"); + } } // Update the schema version @@ -228,6 +234,13 @@ public final class HealthMonitor implements PropertyChangeListener { } throw new HealthMonitorException("Error upgrading database", ex); } finally { + if (resultSet != null) { + try { + resultSet.close(); + } catch (SQLException ex2) { + logger.log(Level.SEVERE, "Error closing result set"); + } + } try { conn.close(); } catch (SQLException ex) { From 2dd08f39b051bc38be39937bf3e01ade11d5db63 Mon Sep 17 00:00:00 2001 From: apriestman Date: Fri, 4 Dec 2020 08:39:07 -0500 Subject: [PATCH 29/30] Make error message more descriptive --- Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java index 4ad0e23eda..0a8ccfd7aa 100644 --- a/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/healthmonitor/HealthMonitor.java @@ -984,7 +984,7 @@ public final class HealthMonitor implements PropertyChangeListener { getInstance().writeCurrentStateToDatabase(); } } catch (HealthMonitorException ex) { - logger.log(Level.SEVERE, "Error performing periodic task", ex); //NON-NLS + logger.log(Level.SEVERE, "Error recording health monitor metrics", ex); //NON-NLS } } From 9a6d883b4997b0e4dff6fb58514ab9734eff17dc Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 7 Dec 2020 09:46:40 -0500 Subject: [PATCH 30/30] 6985 fix failure to load first time --- .../autopsy/discovery/ui/ArtifactsWorker.java | 3 ++- .../discovery/ui/DiscoveryTopComponent.java | 4 +++- .../discovery/ui/DomainDetailsPanel.java | 19 +++++++++---------- .../discovery/ui/MiniTimelineWorker.java | 16 ++++++++-------- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsWorker.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsWorker.java index 05bc2bc9c0..2c919f371f 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsWorker.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsWorker.java @@ -76,6 +76,7 @@ class ArtifactsWorker extends SwingWorker, Void> { if (!isCancelled()) { try { listOfArtifacts.addAll(get()); + DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.ArtifactSearchResultEvent(artifactType, listOfArtifacts)); } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, "Exception while trying to get list of artifacts for Domain details for artifact type: " + artifactType.getDisplayName() + " and domain: " + domain, ex); @@ -83,6 +84,6 @@ class ArtifactsWorker extends SwingWorker, Void> { //Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging } } - DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.ArtifactSearchResultEvent(artifactType, listOfArtifacts)); + } } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryTopComponent.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryTopComponent.java index 994f17c390..98ced893df 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryTopComponent.java @@ -333,7 +333,9 @@ public final class DiscoveryTopComponent extends TopComponent { descriptionText += Bundle.DiscoveryTopComponent_additionalFilters_text(); } selectedDomainTabName = validateLastSelectedType(searchCompleteEvent); - rightSplitPane.setBottomComponent(new DomainDetailsPanel(selectedDomainTabName)); + DomainDetailsPanel domainDetailsPanel = new DomainDetailsPanel(); + rightSplitPane.setBottomComponent(domainDetailsPanel); + domainDetailsPanel.configureArtifactTabs(selectedDomainTabName); } else { rightSplitPane.setBottomComponent(new FileDetailsPanel()); } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.java index b71b5adfd3..b7c372aa64 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.java @@ -41,7 +41,7 @@ final class DomainDetailsPanel extends JPanel { private ArtifactsWorker singleArtifactDomainWorker; private MiniTimelineWorker miniTimelineWorker; private String domain; - private String selectedTabName; + private String selectedTabName = null; /** * Creates new form ArtifactDetailsPanel. @@ -49,24 +49,23 @@ final class DomainDetailsPanel extends JPanel { * @param selectedTabName The name of the tab to select initially. */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) - DomainDetailsPanel(String selectedTabName) { + DomainDetailsPanel() { initComponents(); - addArtifactTabs(selectedTabName); + jTabbedPane1.add(Bundle.DomainDetailsPanel_miniTimelineTitle_text(), new MiniTimelinePanel()); + for (BlackboardArtifact.ARTIFACT_TYPE type : SearchData.Type.DOMAIN.getArtifactTypes()) { + jTabbedPane1.add(type.getDisplayName(), new DomainArtifactsTabPanel(type)); + } } /** - * Add the tabs for each of the artifact types which we will be displaying. + * Configure the tabs for each of the artifact types which we will be + * displaying. * * @param tabName The name of the tab to select initially. */ @NbBundle.Messages({"DomainDetailsPanel.miniTimelineTitle.text=Mini Timeline"}) @ThreadConfined(type = ThreadConfined.ThreadType.AWT) - private void addArtifactTabs(String tabName) { - - jTabbedPane1.add(Bundle.DomainDetailsPanel_miniTimelineTitle_text(), new MiniTimelinePanel()); - for (BlackboardArtifact.ARTIFACT_TYPE type : SearchData.Type.DOMAIN.getArtifactTypes()) { - jTabbedPane1.add(type.getDisplayName(), new DomainArtifactsTabPanel(type)); - } + void configureArtifactTabs(String tabName) { selectedTabName = tabName; if (StringUtils.isBlank(selectedTabName)) { selectedTabName = Bundle.DomainDetailsPanel_miniTimelineTitle_text(); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineWorker.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineWorker.java index fefe5d08d3..5df30ba3f6 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineWorker.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineWorker.java @@ -52,10 +52,12 @@ class MiniTimelineWorker extends SwingWorker, Void> { @Override protected List doInBackground() throws Exception { + List results = new ArrayList<>(); if (!StringUtils.isBlank(domain)) { DomainSearch domainSearch = new DomainSearch(); try { - return domainSearch.getAllArtifactsForDomain(Case.getCurrentCase().getSleuthkitCase(), domain); + results.addAll(domainSearch.getAllArtifactsForDomain(Case.getCurrentCase().getSleuthkitCase(), domain)); + } catch (DiscoveryException ex) { if (ex.getCause() instanceof InterruptedException) { logger.log(Level.INFO, "MiniTimeline search was cancelled or interrupted for domain: {0}", domain); @@ -64,24 +66,22 @@ class MiniTimelineWorker extends SwingWorker, Void> { } } } - return new ArrayList<>(); + return results; } @Override protected void done() { - List results = null; + List results = new ArrayList<>(); if (!isCancelled()) { try { - results = get(); + results.addAll(get()); + DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.MiniTimelineResultEvent(results)); } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, "Exception while trying to get list of artifacts for Domain details for mini timeline view for domain: " + domain, ex); } catch (CancellationException ignored) { //Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging } } - if (results == null) { - results = new ArrayList<>(); - } - DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.MiniTimelineResultEvent(results)); + } }