mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
Merge branch '6985-MiniTimelineDiscovery' of https://github.com/wschaeferB/autopsy into 7067-AddContextMenusToArtifacts
This commit is contained in:
commit
b1f047461b
@ -6,6 +6,7 @@ jobs:
|
|||||||
- os: linux
|
- os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
- os: osx
|
- os: osx
|
||||||
|
osx_image: xcode12.2
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.contentviewers.contextviewer;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.sleuthkit.autopsy.contentviewers.contextviewer.ContextViewer.DateTimePanel;
|
||||||
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
|
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
|
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.
|
* 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;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
// defines a list of artifacts that provide context for a file
|
// defines a list of artifacts that provide context for a file
|
||||||
private static final List<BlackboardArtifact.ARTIFACT_TYPE> SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>();
|
private static final List<BlackboardArtifact.ARTIFACT_TYPE> SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
|
SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final BlackboardArtifact sourceContextArtifact;
|
private final BlackboardArtifact sourceContextArtifact;
|
||||||
|
|
||||||
|
private final Long dateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new form ContextViewer
|
* Creates new form ContextViewer
|
||||||
*/
|
*/
|
||||||
public ContextSourcePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact) {
|
public ContextSourcePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact, Long dateTime) {
|
||||||
|
|
||||||
initComponents();
|
initComponents();
|
||||||
sourceContextArtifact = associatedArtifact;
|
sourceContextArtifact = associatedArtifact;
|
||||||
setSourceName(sourceName);
|
setSourceName(sourceName);
|
||||||
setSourceText(sourceText);
|
setSourceText(sourceText);
|
||||||
|
this.dateTime = dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getDateTime() {
|
||||||
|
return dateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,27 +29,36 @@ import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOC
|
|||||||
* usage, if known.
|
* 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;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
// defines a list of artifacts that provide context for a file
|
// defines a list of artifacts that provide context for a file
|
||||||
private static final List<BlackboardArtifact.ARTIFACT_TYPE> SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>();
|
private static final List<BlackboardArtifact.ARTIFACT_TYPE> SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
|
SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final BlackboardArtifact sourceContextArtifact;
|
private final BlackboardArtifact sourceContextArtifact;
|
||||||
|
|
||||||
|
private final Long dateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new form ContextViewer
|
* Creates new form ContextViewer
|
||||||
*/
|
*/
|
||||||
public ContextUsagePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact) {
|
public ContextUsagePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact, Long dateTime) {
|
||||||
|
|
||||||
initComponents();
|
initComponents();
|
||||||
sourceContextArtifact = associatedArtifact;
|
sourceContextArtifact = associatedArtifact;
|
||||||
setUsageName(sourceName);
|
setUsageName(sourceName);
|
||||||
setUsageText(sourceText);
|
setUsageText(sourceText);
|
||||||
|
this.dateTime = dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getDateTime() {
|
||||||
|
return dateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.contentviewers.contextviewer;
|
|||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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
|
// defines a list of artifacts that provide context for a file
|
||||||
private static final List<BlackboardArtifact.ARTIFACT_TYPE> CONTEXT_ARTIFACTS = new ArrayList<>();
|
private static final List<BlackboardArtifact.ARTIFACT_TYPE> CONTEXT_ARTIFACTS = new ArrayList<>();
|
||||||
private final List<javax.swing.JPanel> contextSourcePanels = new ArrayList<>();
|
private final List<ContextSourcePanel> contextSourcePanels = new ArrayList<>();
|
||||||
private final List<javax.swing.JPanel> contextUsagePanels = new ArrayList<>();
|
private final List<ContextUsagePanel> contextUsagePanels = new ArrayList<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
|
CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
|
||||||
@ -338,32 +340,36 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
|||||||
"ContextViewer.programExecution=Program Execution: "
|
"ContextViewer.programExecution=Program Execution: "
|
||||||
})
|
})
|
||||||
private void addArtifactToPanels(BlackboardArtifact associatedArtifact) throws TskCoreException {
|
private void addArtifactToPanels(BlackboardArtifact associatedArtifact) throws TskCoreException {
|
||||||
|
Long dateTime = getArtifactDateTime(associatedArtifact);
|
||||||
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID()
|
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID()
|
||||||
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == associatedArtifact.getArtifactTypeID()) {
|
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == associatedArtifact.getArtifactTypeID()) {
|
||||||
String sourceName = Bundle.ContextViewer_attachmentSource();
|
String sourceName = Bundle.ContextViewer_attachmentSource();
|
||||||
String sourceText = msgArtifactToAbbreviatedString(associatedArtifact);
|
String sourceText = msgArtifactToAbbreviatedString(associatedArtifact);
|
||||||
javax.swing.JPanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact);
|
ContextSourcePanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact, dateTime);
|
||||||
contextSourcePanels.add(sourcePanel);
|
contextSourcePanels.add(sourcePanel);
|
||||||
|
|
||||||
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID()
|
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID()
|
||||||
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == associatedArtifact.getArtifactTypeID()) {
|
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == associatedArtifact.getArtifactTypeID()) {
|
||||||
String sourceName = Bundle.ContextViewer_downloadSource();
|
String sourceName = Bundle.ContextViewer_downloadSource();
|
||||||
String sourceText = webDownloadArtifactToString(associatedArtifact);
|
String sourceText = webDownloadArtifactToString(associatedArtifact);
|
||||||
javax.swing.JPanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact);
|
ContextSourcePanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact, dateTime);
|
||||||
contextSourcePanels.add(sourcePanel);
|
contextSourcePanels.add(sourcePanel);
|
||||||
|
|
||||||
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID() == associatedArtifact.getArtifactTypeID()) {
|
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID() == associatedArtifact.getArtifactTypeID()) {
|
||||||
String sourceName = Bundle.ContextViewer_recentDocs();
|
String sourceName = Bundle.ContextViewer_recentDocs();
|
||||||
String sourceText = recentDocArtifactToString(associatedArtifact);
|
String sourceText = recentDocArtifactToString(associatedArtifact);
|
||||||
javax.swing.JPanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact);
|
ContextUsagePanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact, dateTime);
|
||||||
contextUsagePanels.add(usagePanel);
|
contextUsagePanels.add(usagePanel);
|
||||||
|
|
||||||
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() == associatedArtifact.getArtifactTypeID()) {
|
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() == associatedArtifact.getArtifactTypeID()) {
|
||||||
String sourceName = Bundle.ContextViewer_programExecution();
|
String sourceName = Bundle.ContextViewer_programExecution();
|
||||||
String sourceText = programExecArtifactToString(associatedArtifact);
|
String sourceText = programExecArtifactToString(associatedArtifact);
|
||||||
javax.swing.JPanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact);
|
ContextUsagePanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact, dateTime);
|
||||||
contextUsagePanels.add(usagePanel);
|
contextUsagePanels.add(usagePanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Collections.sort(contextSourcePanels, new SortByDateTime());
|
||||||
|
Collections.sort(contextUsagePanels, new SortByDateTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -533,6 +539,59 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
|||||||
return attributeMap;
|
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<DateTimePanel> {
|
||||||
|
|
||||||
|
@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
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JScrollPane jScrollPane;
|
private javax.swing.JScrollPane jScrollPane;
|
||||||
|
@ -130,7 +130,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
|||||||
if (StringUtils.isBlank(path) || lastOpened == null || lastOpened == 0) {
|
if (StringUtils.isBlank(path) || lastOpened == null || lastOpened == 0) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return new RecentFileDetails(path, lastOpened);
|
return new RecentFileDetails(artifact, path, lastOpened);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
|||||||
if (StringUtils.isBlank(path) || accessedTime == null || accessedTime == 0) {
|
if (StringUtils.isBlank(path) || accessedTime == null || accessedTime == 0) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return new RecentDownloadDetails(path, accessedTime, domain);
|
return new RecentDownloadDetails(artifact, path, accessedTime, domain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +298,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
|||||||
if (date == null || date == 0 || StringUtils.isBlank(path)) {
|
if (date == null || date == 0 || StringUtils.isBlank(path)) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return new RecentAttachmentDetails(path, date, sender);
|
return new RecentAttachmentDetails(messageArtifact, path, date, sender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,14 +324,17 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
|||||||
|
|
||||||
private final String path;
|
private final String path;
|
||||||
private final long date;
|
private final long date;
|
||||||
|
private final BlackboardArtifact artifact;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for files with just a path and date.
|
* Constructor for files with just a path and date.
|
||||||
*
|
*
|
||||||
|
* @param artifact The relevant artifact.
|
||||||
* @param path File path.
|
* @param path File path.
|
||||||
* @param date File access date\time in seconds with java epoch
|
* @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.path = path;
|
||||||
this.date = date;
|
this.date = date;
|
||||||
}
|
}
|
||||||
@ -364,6 +367,12 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The pertinent artifact for this recent file hit.
|
||||||
|
*/
|
||||||
|
public BlackboardArtifact getArtifact() {
|
||||||
|
return artifact;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -376,12 +385,13 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
|||||||
/**
|
/**
|
||||||
* Constructor for files with just a path and date.
|
* Constructor for files with just a path and date.
|
||||||
*
|
*
|
||||||
|
* @param artifact The relevant artifact.
|
||||||
* @param path File path.
|
* @param path File path.
|
||||||
* @param date File access date\time in seconds with java epoch.
|
* @param date File access date\time in seconds with java epoch.
|
||||||
* @param webDomain The webdomain from which the file was downloaded.
|
* @param webDomain The webdomain from which the file was downloaded.
|
||||||
*/
|
*/
|
||||||
RecentDownloadDetails(String path, long date, String webDomain) {
|
RecentDownloadDetails(BlackboardArtifact artifact, String path, long date, String webDomain) {
|
||||||
super(path, date);
|
super(artifact, path, date);
|
||||||
this.webDomain = webDomain;
|
this.webDomain = webDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,13 +417,14 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
|
|||||||
* Constructor for recent download files which have a path, date and
|
* Constructor for recent download files which have a path, date and
|
||||||
* domain value.
|
* domain value.
|
||||||
*
|
*
|
||||||
|
* @param artifact The relevant artifact.
|
||||||
* @param path File path.
|
* @param path File path.
|
||||||
* @param date File crtime.
|
* @param date File crtime.
|
||||||
* @param sender The sender of the message from which the file was
|
* @param sender The sender of the message from which the file was
|
||||||
* attached.
|
* attached.
|
||||||
*/
|
*/
|
||||||
RecentAttachmentDetails(String path, long date, String sender) {
|
RecentAttachmentDetails(BlackboardArtifact artifact, String path, long date, String sender) {
|
||||||
super(path, date);
|
super(artifact, path, date);
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2020 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> 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<? extends TimelineFilter.DataSourceFilter> filterState : dataSourceState.getDataSourcesFilterState().getSubFilterStates()) {
|
||||||
|
DataSourceFilter dsFilter = filterState.getFilter();
|
||||||
|
if (dsFilter != null) {
|
||||||
|
filterState.setSelected(dsFilter.getDataSourceID() == dataSource.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataSourceState;
|
||||||
|
}
|
||||||
|
}
|
@ -38,12 +38,11 @@ import org.sleuthkit.datamodel.AbstractFile;
|
|||||||
import org.sleuthkit.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
import org.sleuthkit.datamodel.TimelineEvent;
|
import org.sleuthkit.datamodel.TimelineEvent;
|
||||||
import org.sleuthkit.datamodel.TimelineEventType;
|
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.TimelineFilter.RootFilter;
|
||||||
import org.sleuthkit.datamodel.TimelineManager;
|
import org.sleuthkit.datamodel.TimelineManager;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,6 +50,23 @@ import org.sleuthkit.autopsy.core.UserPreferences;
|
|||||||
*/
|
*/
|
||||||
public class TimelineSummary implements DefaultUpdateGovernor {
|
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 long DAY_SECS = 24 * 60 * 60;
|
||||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
|
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
|
||||||
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
|
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
|
||||||
@ -64,23 +80,29 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
|||||||
|
|
||||||
private final SleuthkitCaseProvider caseProvider;
|
private final SleuthkitCaseProvider caseProvider;
|
||||||
private final Supplier<TimeZone> timeZoneProvider;
|
private final Supplier<TimeZone> timeZoneProvider;
|
||||||
|
private final DataSourceFilterFunction filterFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
*/
|
*/
|
||||||
public TimelineSummary() {
|
public TimelineSummary() {
|
||||||
this(SleuthkitCaseProvider.DEFAULT, () -> TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()));
|
this(SleuthkitCaseProvider.DEFAULT,
|
||||||
|
() -> TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()),
|
||||||
|
(ds) -> TimelineDataSourceUtils.getInstance().getDataSourceFilter(ds));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct object with given SleuthkitCaseProvider
|
* Construct object with given SleuthkitCaseProvider
|
||||||
*
|
*
|
||||||
* @param caseProvider SleuthkitCaseProvider provider, cannot be null.
|
* @param caseProvider SleuthkitCaseProvider provider; cannot be null.
|
||||||
* @param timeZoneProvider The timezone provider, cannot be null.
|
* @param timeZoneProvider The timezone provider; cannot be null.
|
||||||
|
* @param filterFunction Provides the default root filter function filtered
|
||||||
|
* to the data source; cannot be null.
|
||||||
*/
|
*/
|
||||||
public TimelineSummary(SleuthkitCaseProvider caseProvider, Supplier<TimeZone> timeZoneProvider) {
|
public TimelineSummary(SleuthkitCaseProvider caseProvider, Supplier<TimeZone> timeZoneProvider, DataSourceFilterFunction filterFunction) {
|
||||||
this.caseProvider = caseProvider;
|
this.caseProvider = caseProvider;
|
||||||
this.timeZoneProvider = timeZoneProvider;
|
this.timeZoneProvider = timeZoneProvider;
|
||||||
|
this.filterFunction = filterFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -113,8 +135,9 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
|||||||
* @return The retrieved data.
|
* @return The retrieved data.
|
||||||
* @throws SleuthkitCaseProviderException
|
* @throws SleuthkitCaseProviderException
|
||||||
* @throws TskCoreException
|
* @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();
|
TimeZone timeZone = this.timeZoneProvider.get();
|
||||||
TimelineManager timelineManager = this.caseProvider.get().getTimelineManager();
|
TimelineManager timelineManager = this.caseProvider.get().getTimelineManager();
|
||||||
|
|
||||||
@ -144,7 +167,7 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
|||||||
// get most recent days activity
|
// get most recent days activity
|
||||||
List<DailyActivityAmount> mostRecentActivityAmt = getMostRecentActivityAmounts(dateCounts, minRecentDay, maxDay);
|
List<DailyActivityAmount> mostRecentActivityAmt = getMostRecentActivityAmounts(dateCounts, minRecentDay, maxDay);
|
||||||
|
|
||||||
return new TimelineSummaryData(minDate, maxDate, mostRecentActivityAmt);
|
return new TimelineSummaryData(minDate, maxDate, mostRecentActivityAmt, dataSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,25 +204,15 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
|||||||
* belongs.
|
* belongs.
|
||||||
* @return A Map mapping days from epoch to the activity for that day.
|
* @return A Map mapping days from epoch to the activity for that day.
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
|
* @throws NoCurrentCaseException
|
||||||
*/
|
*/
|
||||||
private Map<Long, DailyActivityAmount> getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone) throws TskCoreException {
|
private Map<Long, DailyActivityAmount> getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone)
|
||||||
|
throws TskCoreException, NoCurrentCaseException {
|
||||||
DataSourcesFilter dataSourceFilter = new DataSourcesFilter();
|
RootFilter rootFilter = this.filterFunction.apply(dataSource);
|
||||||
dataSourceFilter.addSubFilter(new TimelineFilter.DataSourceFilter(dataSource.getName(), dataSource.getId()));
|
|
||||||
|
|
||||||
RootFilter dataSourceRootFilter = new RootFilter(
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
dataSourceFilter,
|
|
||||||
null,
|
|
||||||
Collections.emptySet());
|
|
||||||
|
|
||||||
// get events for data source
|
// get events for data source
|
||||||
long curRunTime = System.currentTimeMillis();
|
long curRunTime = System.currentTimeMillis();
|
||||||
List<TimelineEvent> events = timelineManager.getEvents(new Interval(1, curRunTime), dataSourceRootFilter);
|
List<TimelineEvent> events = timelineManager.getEvents(new Interval(1, curRunTime), rootFilter);
|
||||||
|
|
||||||
// get counts of events per day (left is file system events, right is everything else)
|
// get counts of events per day (left is file system events, right is everything else)
|
||||||
Map<Long, DailyActivityAmount> dateCounts = new HashMap<>();
|
Map<Long, DailyActivityAmount> dateCounts = new HashMap<>();
|
||||||
@ -233,6 +246,7 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
|||||||
private final Date minDate;
|
private final Date minDate;
|
||||||
private final Date maxDate;
|
private final Date maxDate;
|
||||||
private final List<DailyActivityAmount> histogramActivity;
|
private final List<DailyActivityAmount> histogramActivity;
|
||||||
|
private final DataSource dataSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main constructor.
|
* Main constructor.
|
||||||
@ -240,12 +254,15 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
|||||||
* @param minDate Earliest usage date recorded for the data source.
|
* @param minDate Earliest usage date recorded for the data source.
|
||||||
* @param maxDate Latest 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
|
||||||
* the latest usage date by day.
|
* 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<DailyActivityAmount> recentDaysActivity) {
|
TimelineSummaryData(Date minDate, Date maxDate, List<DailyActivityAmount> recentDaysActivity, DataSource dataSource) {
|
||||||
this.minDate = minDate;
|
this.minDate = minDate;
|
||||||
this.maxDate = maxDate;
|
this.maxDate = maxDate;
|
||||||
this.histogramActivity = (recentDaysActivity == null) ? Collections.emptyList() : Collections.unmodifiableList(recentDaysActivity);
|
this.histogramActivity = (recentDaysActivity == null) ? Collections.emptyList() : Collections.unmodifiableList(recentDaysActivity);
|
||||||
|
this.dataSource = dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -264,11 +281,18 @@ public class TimelineSummary implements DefaultUpdateGovernor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A list of activity prior to and including the latest usage
|
* @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<DailyActivityAmount> getMostRecentDaysActivity() {
|
public List<DailyActivityAmount> getMostRecentDaysActivity() {
|
||||||
return histogramActivity;
|
return histogramActivity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The data source that this data applies to.
|
||||||
|
*/
|
||||||
|
public DataSource getDataSource() {
|
||||||
|
return dataSource;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,8 +108,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
private static final String NTOS_BOOT_IDENTIFIER = "NTOSBOOT";
|
private static final String NTOS_BOOT_IDENTIFIER = "NTOSBOOT";
|
||||||
private static final String WINDOWS_PREFIX = "/WINDOWS";
|
private static final String WINDOWS_PREFIX = "/WINDOWS";
|
||||||
|
|
||||||
private static final Comparator<TopAccountResult> TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccess().compareTo(b.getLastAccess());
|
private static final Comparator<TopAccountResult> TOP_ACCOUNT_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccessed().compareTo(b.getLastAccessed());
|
||||||
private static final Comparator<TopWebSearchResult> TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getDateAccessed().compareTo(b.getDateAccessed());
|
private static final Comparator<TopWebSearchResult> TOP_WEBSEARCH_RESULT_DATE_COMPARE = (a, b) -> a.getLastAccessed().compareTo(b.getLastAccessed());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts TopProgramsResults pushing highest run time count then most recent
|
* 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
|
// second priority for sorting is the last run date
|
||||||
// if non-0, this is the return value for the comparator
|
// if non-0, this is the return value for the comparator
|
||||||
int lastRunCompare = nullableCompare(
|
int lastRunCompare = nullableCompare(
|
||||||
a.getLastRun() == null ? null : a.getLastRun().getTime(),
|
a.getLastAccessed() == null ? null : a.getLastAccessed().getTime(),
|
||||||
b.getLastRun() == null ? null : b.getLastRun().getTime());
|
b.getLastAccessed() == null ? null : b.getLastAccessed().getTime());
|
||||||
|
|
||||||
if (lastRunCompare != 0) {
|
if (lastRunCompare != 0) {
|
||||||
return -lastRunCompare;
|
return -lastRunCompare;
|
||||||
@ -219,14 +219,14 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<Long, Map<String, List<Long>>> mostRecentAndGroups = getDomainGroupsAndMostRecent(dataSource);
|
Pair<Long, Map<String, List<Pair<BlackboardArtifact, Long>>>> mostRecentAndGroups = getDomainGroupsAndMostRecent(dataSource);
|
||||||
// if no recent domains, return accordingly
|
// if no recent domains, return accordingly
|
||||||
if (mostRecentAndGroups.getKey() == null || mostRecentAndGroups.getValue().size() == 0) {
|
if (mostRecentAndGroups.getKey() == null || mostRecentAndGroups.getValue().size() == 0) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
final long mostRecentMs = mostRecentAndGroups.getLeft();
|
final long mostRecentMs = mostRecentAndGroups.getLeft();
|
||||||
Map<String, List<Long>> groups = mostRecentAndGroups.getRight();
|
Map<String, List<Pair<BlackboardArtifact, Long>>> groups = mostRecentAndGroups.getRight();
|
||||||
|
|
||||||
return groups.entrySet().stream()
|
return groups.entrySet().stream()
|
||||||
.map(entry -> getDomainsResult(entry.getKey(), entry.getValue(), mostRecentMs))
|
.map(entry -> getDomainsResult(entry.getKey(), entry.getValue(), mostRecentMs))
|
||||||
@ -243,24 +243,32 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
* within DOMAIN_WINDOW_MS of mostRecentMs.
|
* within DOMAIN_WINDOW_MS of mostRecentMs.
|
||||||
*
|
*
|
||||||
* @param domain The domain.
|
* @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.
|
* @param mostRecentMs The most recent visit of any domain.
|
||||||
*
|
*
|
||||||
* @return The TopDomainsResult or null if no visits to this domain within
|
* @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<Long> visits, long mostRecentMs) {
|
private TopDomainsResult getDomainsResult(String domain, List<Pair<BlackboardArtifact, Long>> visits, long mostRecentMs) {
|
||||||
long visitCount = 0;
|
long visitCount = 0;
|
||||||
Long thisMostRecentMs = null;
|
Long thisMostRecentMs = null;
|
||||||
|
BlackboardArtifact thisMostRecentArtifact = null;
|
||||||
|
|
||||||
for (Long visitMs : visits) {
|
for (Pair<BlackboardArtifact, Long> visitInstance : visits) {
|
||||||
|
BlackboardArtifact artifact = visitInstance.getLeft();
|
||||||
|
Long visitMs = visitInstance.getRight();
|
||||||
// make sure that visit is within window of mostRecentMS; otherwise skip it.
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if visit is within window, increment the count and get most recent
|
// if visit is within window, increment the count and get most recent
|
||||||
visitCount++;
|
visitCount++;
|
||||||
|
if (thisMostRecentMs == null || visitMs > thisMostRecentMs) {
|
||||||
|
thisMostRecentMs = visitMs;
|
||||||
|
thisMostRecentArtifact = artifact;
|
||||||
|
}
|
||||||
thisMostRecentMs = getMax(thisMostRecentMs, visitMs);
|
thisMostRecentMs = getMax(thisMostRecentMs, visitMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +277,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
// create a top domain result with the domain, count, and most recent visit date
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,17 +289,18 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
*
|
*
|
||||||
* @return A tuple where the first value is the latest web history accessed
|
* @return A tuple where the first value is the latest web history accessed
|
||||||
* date in milliseconds and the second value maps normalized (lowercase;
|
* date in milliseconds and the second value maps normalized (lowercase;
|
||||||
* trimmed) domain names to when those domains were visited.
|
* trimmed) domain names to when those domains were visited and the relevant
|
||||||
|
* artifact.
|
||||||
*
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
* @throws SleuthkitCaseProviderException
|
* @throws SleuthkitCaseProviderException
|
||||||
*/
|
*/
|
||||||
private Pair<Long, Map<String, List<Long>>> getDomainGroupsAndMostRecent(DataSource dataSource) throws TskCoreException, SleuthkitCaseProviderException {
|
private Pair<Long, Map<String, List<Pair<BlackboardArtifact, Long>>>> getDomainGroupsAndMostRecent(DataSource dataSource) throws TskCoreException, SleuthkitCaseProviderException {
|
||||||
List<BlackboardArtifact> artifacts = DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_WEB_HISTORY,
|
List<BlackboardArtifact> artifacts = DataSourceInfoUtilities.getArtifacts(caseProvider.get(), TYPE_WEB_HISTORY,
|
||||||
dataSource, TYPE_DATETIME_ACCESSED, DataSourceInfoUtilities.SortOrder.DESCENDING, 0);
|
dataSource, TYPE_DATETIME_ACCESSED, DataSourceInfoUtilities.SortOrder.DESCENDING, 0);
|
||||||
|
|
||||||
Long mostRecentMs = null;
|
Long mostRecentMs = null;
|
||||||
Map<String, List<Long>> domainVisits = new HashMap<>();
|
Map<String, List<Pair<BlackboardArtifact, Long>>> domainVisits = new HashMap<>();
|
||||||
|
|
||||||
for (BlackboardArtifact art : artifacts) {
|
for (BlackboardArtifact art : artifacts) {
|
||||||
Long artifactDateSecs = DataSourceInfoUtilities.getLongOrNull(art, TYPE_DATETIME_ACCESSED);
|
Long artifactDateSecs = DataSourceInfoUtilities.getLongOrNull(art, TYPE_DATETIME_ACCESSED);
|
||||||
@ -312,13 +321,13 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
domain = domain.toLowerCase().trim();
|
domain = domain.toLowerCase().trim();
|
||||||
|
|
||||||
// add this visit date to the list of dates for the domain
|
// add this visit date to the list of dates for the domain
|
||||||
List<Long> domainVisitList = domainVisits.get(domain);
|
List<Pair<BlackboardArtifact, Long>> domainVisitList = domainVisits.get(domain);
|
||||||
if (domainVisitList == null) {
|
if (domainVisitList == null) {
|
||||||
domainVisitList = new ArrayList<>();
|
domainVisitList = new ArrayList<>();
|
||||||
domainVisits.put(domain, domainVisitList);
|
domainVisits.put(domain, domainVisitList);
|
||||||
}
|
}
|
||||||
|
|
||||||
domainVisitList.add(artifactDateMs);
|
domainVisitList.add(Pair.of(art, artifactDateMs));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pair.of(mostRecentMs, domainVisits);
|
return Pair.of(mostRecentMs, domainVisits);
|
||||||
@ -354,7 +363,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT);
|
String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT);
|
||||||
Date dateAccessed = DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME_ACCESSED);
|
Date dateAccessed = DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME_ACCESSED);
|
||||||
return (StringUtils.isNotBlank(searchString) && dateAccessed != null)
|
return (StringUtils.isNotBlank(searchString) && dateAccessed != null)
|
||||||
? new TopWebSearchResult(searchString, dateAccessed)
|
? new TopWebSearchResult(searchString, dateAccessed, artifact)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,15 +465,15 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
* @return The most recent one with a non-null date.
|
* @return The most recent one with a non-null date.
|
||||||
*/
|
*/
|
||||||
private TopDeviceAttachedResult getMostRecentDevice(TopDeviceAttachedResult r1, TopDeviceAttachedResult r2) {
|
private TopDeviceAttachedResult getMostRecentDevice(TopDeviceAttachedResult r1, TopDeviceAttachedResult r2) {
|
||||||
if (r2.getDateAccessed() == null) {
|
if (r2.getLastAccessed()== null) {
|
||||||
return r1;
|
return r1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r1.getDateAccessed() == null) {
|
if (r1.getLastAccessed() == null) {
|
||||||
return r2;
|
return r2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return r1.getDateAccessed().compareTo(r2.getDateAccessed()) >= 0 ? r1 : r2;
|
return r1.getLastAccessed().compareTo(r2.getLastAccessed()) >= 0 ? r1 : r2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -495,7 +504,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_ID),
|
DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_ID),
|
||||||
DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME),
|
DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME),
|
||||||
DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MAKE),
|
DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MAKE),
|
||||||
DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MODEL)
|
DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_DEVICE_MODEL),
|
||||||
|
artifact
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
// remove Root Hub identifier
|
// remove Root Hub identifier
|
||||||
@ -524,7 +534,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE);
|
String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE);
|
||||||
Date date = DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME);
|
Date date = DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME);
|
||||||
return (StringUtils.isNotBlank(type) && date != null)
|
return (StringUtils.isNotBlank(type) && date != null)
|
||||||
? new TopAccountResult(type, date)
|
? new TopAccountResult(type, date, artifact)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,7 +562,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (StringUtils.isNotBlank(type) && latestDate != null)
|
return (StringUtils.isNotBlank(type) && latestDate != null)
|
||||||
? new TopAccountResult(type, latestDate)
|
? new TopAccountResult(type, latestDate, artifact)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -690,7 +700,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
programName,
|
programName,
|
||||||
path,
|
path,
|
||||||
longCount,
|
longCount,
|
||||||
DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME)
|
DataSourceInfoUtilities.getDateOrNull(artifact, TYPE_DATETIME),
|
||||||
|
artifact
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -786,11 +797,15 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
res.getProgramPath() == null ? null : res.getProgramPath().toUpperCase()),
|
res.getProgramPath() == null ? null : res.getProgramPath().toUpperCase()),
|
||||||
res -> res,
|
res -> res,
|
||||||
(res1, res2) -> {
|
(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(
|
return new TopProgramsResult(
|
||||||
res1.getProgramName(),
|
maxResult.getProgramName(),
|
||||||
res1.getProgramPath(),
|
maxResult.getProgramPath(),
|
||||||
getMax(res1.getRunTimes(), res2.getRunTimes()),
|
maxRunTimes,
|
||||||
getMax(res1.getLastRun(), res2.getLastRun()));
|
maxDate,
|
||||||
|
maxResult.getArtifact());
|
||||||
})).values();
|
})).values();
|
||||||
|
|
||||||
List<TopProgramsResult> orderedResults = results.stream()
|
List<TopProgramsResult> orderedResults = results.stream()
|
||||||
@ -803,7 +818,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
// if run times / last run information is available, the first item should have some value,
|
// if run times / last run information is available, the first item should have some value,
|
||||||
// and then the items should be limited accordingly.
|
// and then the items should be limited accordingly.
|
||||||
if (isPositiveNum(topResult.getRunTimes())
|
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());
|
return orderedResults.stream().limit(count).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -812,13 +827,47 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
return orderedResults;
|
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.
|
* Object containing information about a web search artifact.
|
||||||
*/
|
*/
|
||||||
public static class TopWebSearchResult {
|
public static class TopWebSearchResult extends LastAccessedArtifact {
|
||||||
|
|
||||||
private final String searchString;
|
private final String searchString;
|
||||||
private final Date dateAccessed;
|
|
||||||
private String translatedResult;
|
private String translatedResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -826,10 +875,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
*
|
*
|
||||||
* @param searchString The search string.
|
* @param searchString The search string.
|
||||||
* @param dateAccessed The latest date searched.
|
* @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.searchString = searchString;
|
||||||
this.dateAccessed = dateAccessed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -854,22 +904,14 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
public String getSearchString() {
|
public String getSearchString() {
|
||||||
return searchString;
|
return searchString;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The date for the search.
|
|
||||||
*/
|
|
||||||
public Date getDateAccessed() {
|
|
||||||
return dateAccessed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A record of a device attached.
|
* A record of a device attached.
|
||||||
*/
|
*/
|
||||||
public static class TopDeviceAttachedResult {
|
public static class TopDeviceAttachedResult extends LastAccessedArtifact {
|
||||||
|
|
||||||
private final String deviceId;
|
private final String deviceId;
|
||||||
private final Date dateAccessed;
|
|
||||||
private final String deviceMake;
|
private final String deviceMake;
|
||||||
private final String deviceModel;
|
private final String deviceModel;
|
||||||
|
|
||||||
@ -880,10 +922,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
* @param dateAccessed The date last attached.
|
* @param dateAccessed The date last attached.
|
||||||
* @param deviceMake The device make.
|
* @param deviceMake The device make.
|
||||||
* @param deviceModel The device model.
|
* @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.deviceId = deviceId;
|
||||||
this.dateAccessed = dateAccessed;
|
|
||||||
this.deviceMake = deviceMake;
|
this.deviceMake = deviceMake;
|
||||||
this.deviceModel = deviceModel;
|
this.deviceModel = deviceModel;
|
||||||
}
|
}
|
||||||
@ -895,13 +938,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
return deviceId;
|
return deviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The date last attached.
|
|
||||||
*/
|
|
||||||
public Date getDateAccessed() {
|
|
||||||
return dateAccessed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The device make.
|
* @return The device make.
|
||||||
*/
|
*/
|
||||||
@ -921,20 +957,20 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
* A record of an account and the last time it was used determined by
|
* A record of an account and the last time it was used determined by
|
||||||
* messages.
|
* messages.
|
||||||
*/
|
*/
|
||||||
public static class TopAccountResult {
|
public static class TopAccountResult extends LastAccessedArtifact {
|
||||||
|
|
||||||
private final String accountType;
|
private final String accountType;
|
||||||
private final Date lastAccess;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main constructor.
|
* Main constructor.
|
||||||
*
|
*
|
||||||
* @param accountType The account type.
|
* @param accountType The account type.
|
||||||
* @param lastAccess The date the account was last accessed.
|
* @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.accountType = accountType;
|
||||||
this.lastAccess = lastAccess;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -943,23 +979,15 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
public String getAccountType() {
|
public String getAccountType() {
|
||||||
return accountType;
|
return accountType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The date the account was last accessed.
|
|
||||||
*/
|
|
||||||
public Date getLastAccess() {
|
|
||||||
return lastAccess;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes a result of a program run on a datasource.
|
* 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 String domain;
|
||||||
private final Long visitTimes;
|
private final Long visitTimes;
|
||||||
private final Date lastVisit;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes a top domain result.
|
* Describes a top domain result.
|
||||||
@ -967,11 +995,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
* @param domain The domain.
|
* @param domain The domain.
|
||||||
* @param visitTimes The number of times it was visited.
|
* @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) {
|
public TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact) {
|
||||||
|
super(lastVisit, artifact);
|
||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
this.visitTimes = visitTimes;
|
this.visitTimes = visitTimes;
|
||||||
this.lastVisit = lastVisit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -987,24 +1016,16 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
public Long getVisitTimes() {
|
public Long getVisitTimes() {
|
||||||
return visitTimes;
|
return visitTimes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The date of the last visit.
|
|
||||||
*/
|
|
||||||
public Date getLastVisit() {
|
|
||||||
return lastVisit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes a result of a program run on a datasource.
|
* 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 programName;
|
||||||
private final String programPath;
|
private final String programPath;
|
||||||
private final Long runTimes;
|
private final Long runTimes;
|
||||||
private final Date lastRun;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main constructor.
|
* Main constructor.
|
||||||
@ -1012,12 +1033,13 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
* @param programName The name of the program.
|
* @param programName The name of the program.
|
||||||
* @param programPath The path 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) {
|
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun, BlackboardArtifact artifact) {
|
||||||
|
super(lastRun, artifact);
|
||||||
this.programName = programName;
|
this.programName = programName;
|
||||||
this.programPath = programPath;
|
this.programPath = programPath;
|
||||||
this.runTimes = runTimes;
|
this.runTimes = runTimes;
|
||||||
this.lastRun = lastRun;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1040,12 +1062,5 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
|
|||||||
public Long getRunTimes() {
|
public Long getRunTimes() {
|
||||||
return runTimes;
|
return runTimes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The last time the program was run or null if not present.
|
|
||||||
*/
|
|
||||||
public Date getLastRun() {
|
|
||||||
return lastRun;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,20 +19,29 @@
|
|||||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||||
|
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.SwingWorker;
|
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.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
|
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;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||||
@ -41,6 +50,8 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler;
|
|||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialExecutor;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialExecutor;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor;
|
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.IngestManager.IngestJobEvent;
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||||
@ -53,6 +64,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
/**
|
/**
|
||||||
* Base class from which other tabs in data source summary derive.
|
* Base class from which other tabs in data source summary derive.
|
||||||
*/
|
*/
|
||||||
|
@Messages({"BaseDataSourceSummaryPanel_goToArtifact=View Source Result",
|
||||||
|
"BaseDataSourceSummaryPanel_goToFile=View Source File in Directory"})
|
||||||
abstract class BaseDataSourceSummaryPanel extends JPanel {
|
abstract class BaseDataSourceSummaryPanel extends JPanel {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
@ -64,6 +77,8 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
|
|||||||
private final EventUpdateHandler updateHandler;
|
private final EventUpdateHandler updateHandler;
|
||||||
private final List<UpdateGovernor> governors;
|
private final List<UpdateGovernor> governors;
|
||||||
|
|
||||||
|
private Runnable notifyParentClose = null;
|
||||||
|
|
||||||
private DataSource dataSource;
|
private DataSource dataSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -227,6 +242,104 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
|
|||||||
this.updateHandler.register();
|
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 for a go to artifact menu item.
|
||||||
|
*/
|
||||||
|
protected CellModelTableCellRenderer.MenuItem getArtifactNavigateItem(BlackboardArtifact artifact) {
|
||||||
|
if (artifact == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CellModelTableCellRenderer.DefaultMenuItem(
|
||||||
|
Bundle.BaseDataSourceSummaryPanel_goToArtifact(),
|
||||||
|
() -> {
|
||||||
|
final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
|
||||||
|
|
||||||
|
// 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<AbstractFile> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 CellModelTableCellRenderer.MenuItem getFileNavigateItem(AbstractFile file) {
|
||||||
|
if (file == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CellModelTableCellRenderer.DefaultMenuItem(
|
||||||
|
Bundle.BaseDataSourceSummaryPanel_goToFile(),
|
||||||
|
() -> {
|
||||||
|
new ViewContextAction(Bundle.BaseDataSourceSummaryPanel_goToFile(), file)
|
||||||
|
.actionPerformed(null);
|
||||||
|
|
||||||
|
notifyParentClose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes listeners and resources.
|
* Closes listeners and resources.
|
||||||
*/
|
*/
|
||||||
@ -246,6 +359,24 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
|
|||||||
onNewDataSource(this.dataSource);
|
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.
|
* @return The current data source.
|
||||||
*/
|
*/
|
||||||
@ -375,8 +506,7 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
|
|||||||
* @param result The data result.
|
* @param result The data result.
|
||||||
* @param factoryClass The fully qualified class name of the relevant
|
* @param factoryClass The fully qualified class name of the relevant
|
||||||
* factory.
|
* factory.
|
||||||
* @param moduleName The name of the ingest module (i.e. 'Keyword
|
* @param moduleName The name of the ingest module (i.e. 'Keyword Search').
|
||||||
* Search').
|
|
||||||
*/
|
*/
|
||||||
protected <T> void showResultWithModuleCheck(LoadableComponent<List<T>> component, DataFetchResult<List<T>> result, String factoryClass, String moduleName) {
|
protected <T> void showResultWithModuleCheck(LoadableComponent<List<T>> component, DataFetchResult<List<T>> result, String factoryClass, String moduleName) {
|
||||||
Predicate<List<T>> hasResults = (lst) -> lst != null && !lst.isEmpty();
|
Predicate<List<T>> hasResults = (lst) -> lst != null && !lst.isEmpty();
|
||||||
@ -395,8 +525,7 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
|
|||||||
* data contains any actual results.
|
* data contains any actual results.
|
||||||
* @param factoryClass The fully qualified class name of the relevant
|
* @param factoryClass The fully qualified class name of the relevant
|
||||||
* factory.
|
* factory.
|
||||||
* @param moduleName The name of the ingest module (i.e. 'Keyword
|
* @param moduleName The name of the ingest module (i.e. 'Keyword Search').
|
||||||
* Search').
|
|
||||||
*/
|
*/
|
||||||
protected <T> void showResultWithModuleCheck(LoadableComponent<T> component, DataFetchResult<T> result,
|
protected <T> void showResultWithModuleCheck(LoadableComponent<T> component, DataFetchResult<T> result,
|
||||||
Predicate<T> hasResults, String factoryClass, String moduleName) {
|
Predicate<T> hasResults, String factoryClass, String moduleName) {
|
||||||
|
@ -43,3 +43,12 @@ PastCasesPanel.notableFileLabel.text=Cases with Common Items That Were Tagged as
|
|||||||
PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs
|
PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs
|
||||||
DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected.
|
DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected.
|
||||||
TimelinePanel.activityRangeLabel.text=Activity Range
|
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
|
||||||
|
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.viewInTimelineBtn.text=View in Timeline
|
||||||
|
@ -3,6 +3,8 @@ AnalysisPanel_keyColumn_title=Name
|
|||||||
AnalysisPanel_keywordSearchModuleName=Keyword Search
|
AnalysisPanel_keywordSearchModuleName=Keyword Search
|
||||||
# {0} - module name
|
# {0} - module name
|
||||||
BaseDataSourceSummaryPanel_defaultNotIngestMessage=The {0} ingest module has not been run on this data source.
|
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
|
ContainerPanel_setFieldsForNonImageDataSource_na=N/A
|
||||||
CTL_DataSourceSummaryAction=Data Source Summary
|
CTL_DataSourceSummaryAction=Data Source Summary
|
||||||
DataSourceSummaryDialog.closeButton.text=Close
|
DataSourceSummaryDialog.closeButton.text=Close
|
||||||
@ -103,6 +105,15 @@ PastCasesPanel.notableFileLabel.text=Cases with Common Items That Were Tagged as
|
|||||||
PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs
|
PastCasesPanel.sameIdLabel.text=Past Cases with the Same Device IDs
|
||||||
DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected.
|
DataSourceSummaryTabbedPane.noDataSourceLabel.text=No data source has been selected.
|
||||||
TimelinePanel.activityRangeLabel.text=Activity Range
|
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
|
||||||
|
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.viewInTimelineBtn.text=View in Timeline
|
||||||
UserActivityPanel_noDataExists=No communication data exists
|
UserActivityPanel_noDataExists=No communication data exists
|
||||||
UserActivityPanel_tab_title=User Activity
|
UserActivityPanel_tab_title=User Activity
|
||||||
UserActivityPanel_TopAccountTableModel_accountType_header=Account Type
|
UserActivityPanel_TopAccountTableModel_accountType_header=Account Type
|
||||||
|
@ -61,6 +61,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
|
|||||||
Map<Long, Long> fileCountsMap = CaseDataSourcesSummary.getCountsOfFiles();
|
Map<Long, Long> fileCountsMap = CaseDataSourcesSummary.getCountsOfFiles();
|
||||||
dataSourcesPanel = new DataSourceBrowser(usageMap, fileCountsMap);
|
dataSourcesPanel = new DataSourceBrowser(usageMap, fileCountsMap);
|
||||||
dataSourceSummaryTabbedPane = new DataSourceSummaryTabbedPane();
|
dataSourceSummaryTabbedPane = new DataSourceSummaryTabbedPane();
|
||||||
|
dataSourceSummaryTabbedPane.setParentCloseListener(() -> DataSourceSummaryDialog.this.dispose());
|
||||||
initComponents();
|
initComponents();
|
||||||
dataSourceSummarySplitPane.setLeftComponent(dataSourcesPanel);
|
dataSourceSummarySplitPane.setLeftComponent(dataSourcesPanel);
|
||||||
dataSourcesPanel.addListSelectionListener((ListSelectionEvent e) -> {
|
dataSourcesPanel.addListSelectionListener((ListSelectionEvent e) -> {
|
||||||
|
@ -47,7 +47,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
|||||||
* Records of tab information (i.e. title, component, function to call on
|
* Records of tab information (i.e. title, component, function to call on
|
||||||
* new data source).
|
* new data source).
|
||||||
*/
|
*/
|
||||||
private static class DataSourceTab {
|
private class DataSourceTab {
|
||||||
|
|
||||||
private final String tabTitle;
|
private final String tabTitle;
|
||||||
private final Component component;
|
private final Component component;
|
||||||
@ -74,8 +74,12 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
|||||||
*
|
*
|
||||||
* @param tabTitle The title of the tab.
|
* @param tabTitle The title of the tab.
|
||||||
* @param panel The component to be displayed in 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) {
|
DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) {
|
||||||
|
|
||||||
|
panel.setParentCloseListener(() -> notifyParentClose());
|
||||||
|
|
||||||
this.tabTitle = tabTitle;
|
this.tabTitle = tabTitle;
|
||||||
this.component = panel;
|
this.component = panel;
|
||||||
this.onDataSource = panel::setDataSource;
|
this.onDataSource = panel::setDataSource;
|
||||||
@ -116,6 +120,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
|||||||
private static final String TABBED_PANE = "tabbedPane";
|
private static final String TABBED_PANE = "tabbedPane";
|
||||||
private static final String NO_DATASOURCE_PANE = "noDataSourcePane";
|
private static final String NO_DATASOURCE_PANE = "noDataSourcePane";
|
||||||
|
|
||||||
|
private Runnable notifyParentClose = null;
|
||||||
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();
|
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();
|
||||||
|
|
||||||
private final List<DataSourceTab> tabs = Arrays.asList(
|
private final List<DataSourceTab> tabs = Arrays.asList(
|
||||||
@ -142,6 +147,24 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
|||||||
postInit();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method called right after initComponents during initialization.
|
* Method called right after initComponents during initialization.
|
||||||
*/
|
*/
|
||||||
|
@ -94,7 +94,7 @@
|
|||||||
</AuxValues>
|
</AuxValues>
|
||||||
<Constraints>
|
<Constraints>
|
||||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||||
<GridBagConstraints gridX="0" gridY="4" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="1.0" weightY="1.0"/>
|
<GridBagConstraints gridX="0" gridY="5" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="1.0" weightY="1.0"/>
|
||||||
</Constraint>
|
</Constraint>
|
||||||
</Constraints>
|
</Constraints>
|
||||||
|
|
||||||
@ -106,7 +106,7 @@
|
|||||||
</AuxValues>
|
</AuxValues>
|
||||||
<Constraints>
|
<Constraints>
|
||||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||||
<GridBagConstraints gridX="0" gridY="6" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="1.0" weightY="1.0"/>
|
<GridBagConstraints gridX="0" gridY="8" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="1.0" weightY="1.0"/>
|
||||||
</Constraint>
|
</Constraint>
|
||||||
</Constraints>
|
</Constraints>
|
||||||
|
|
||||||
@ -140,7 +140,7 @@
|
|||||||
</AuxValues>
|
</AuxValues>
|
||||||
<Constraints>
|
<Constraints>
|
||||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||||
<GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="20" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
<GridBagConstraints gridX="0" gridY="4" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="20" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||||
</Constraint>
|
</Constraint>
|
||||||
</Constraints>
|
</Constraints>
|
||||||
</Component>
|
</Component>
|
||||||
@ -156,7 +156,55 @@
|
|||||||
</AuxValues>
|
</AuxValues>
|
||||||
<Constraints>
|
<Constraints>
|
||||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||||
<GridBagConstraints gridX="0" gridY="5" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="20" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="0.0" weightY="0.0"/>
|
<GridBagConstraints gridX="0" gridY="7" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="20" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="0.0" weightY="0.0"/>
|
||||||
|
</Constraint>
|
||||||
|
</Constraints>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="rightClickForMoreOptions1">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="RecentFilesPanel.rightClickForMoreOptions1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
<Constraints>
|
||||||
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||||
|
<GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="0.0" weightY="0.0"/>
|
||||||
|
</Constraint>
|
||||||
|
</Constraints>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="rightClickForMoreOptions2">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="RecentFilesPanel.rightClickForMoreOptions2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
<Constraints>
|
||||||
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||||
|
<GridBagConstraints gridX="0" gridY="6" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="0.0" weightY="0.0"/>
|
||||||
|
</Constraint>
|
||||||
|
</Constraints>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="rightClickForMoreOptions3">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="RecentFilesPanel.rightClickForMoreOptions3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
<Constraints>
|
||||||
|
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||||
|
<GridBagConstraints gridX="0" gridY="9" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="11" weightX="0.0" weightY="0.0"/>
|
||||||
</Constraint>
|
</Constraint>
|
||||||
</Constraints>
|
</Constraints>
|
||||||
</Component>
|
</Component>
|
||||||
|
@ -21,13 +21,16 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails;
|
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.DefaultCellModel;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.MenuItem;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
|
||||||
@ -77,6 +80,36 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
|||||||
initalizeTables();
|
initalizeTables();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a base class of RecentFileDetails and provides the pertinent menu
|
||||||
|
* items.
|
||||||
|
*
|
||||||
|
* @param record The RecentFileDetails instance.
|
||||||
|
* @return The menu items list containing one action or navigating to the
|
||||||
|
* appropriate artifact/file and closing the data source summary dialog if
|
||||||
|
* open.
|
||||||
|
*/
|
||||||
|
private Supplier<List<MenuItem>> getPopupFunct(RecentFileDetails record) {
|
||||||
|
return () -> {
|
||||||
|
if (record == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MenuItem> 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
|
@Override
|
||||||
protected void fetchInformation(DataSource dataSource) {
|
protected void fetchInformation(DataSource dataSource) {
|
||||||
fetchInformation(dataFetchComponents, dataSource);
|
fetchInformation(dataFetchComponents, dataSource);
|
||||||
@ -113,11 +146,13 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
|||||||
List<ColumnModel<RecentFileDetails>> list = Arrays.asList(
|
List<ColumnModel<RecentFileDetails>> list = Arrays.asList(
|
||||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
|
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
|
||||||
(prog) -> {
|
(prog) -> {
|
||||||
return new DefaultCellModel(prog.getPath());
|
return new DefaultCellModel(prog.getPath())
|
||||||
|
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||||
}, 250),
|
}, 250),
|
||||||
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
|
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
|
||||||
(prog) -> {
|
(prog) -> {
|
||||||
return new DefaultCellModel(prog.getDateAsString());
|
return new DefaultCellModel(prog.getDateAsString())
|
||||||
|
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||||
}, 80));
|
}, 80));
|
||||||
|
|
||||||
ListTableModel<RecentFileDetails> tableModel = JTablePanel.getTableModel(list);
|
ListTableModel<RecentFileDetails> tableModel = JTablePanel.getTableModel(list);
|
||||||
@ -126,6 +161,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
|||||||
pane.setModel(tableModel);
|
pane.setModel(tableModel);
|
||||||
pane.setColumnModel(JTablePanel.getTableColumnModel(list));
|
pane.setColumnModel(JTablePanel.getTableColumnModel(list));
|
||||||
pane.setKeyFunction((recentFile) -> recentFile.getPath());
|
pane.setKeyFunction((recentFile) -> recentFile.getPath());
|
||||||
|
pane.setCellListener(CellModelTableCellRenderer.getMouseListener());
|
||||||
tablePanelList.add(pane);
|
tablePanelList.add(pane);
|
||||||
|
|
||||||
DataFetchWorker.DataFetchComponents<DataSource, List<RecentFileDetails>> worker
|
DataFetchWorker.DataFetchComponents<DataSource, List<RecentFileDetails>> worker
|
||||||
@ -148,15 +184,18 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
|||||||
List<ColumnModel<RecentDownloadDetails>> list = Arrays.asList(
|
List<ColumnModel<RecentDownloadDetails>> list = Arrays.asList(
|
||||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_domain(),
|
new ColumnModel<>(Bundle.RecentFilePanel_col_header_domain(),
|
||||||
(prog) -> {
|
(prog) -> {
|
||||||
return new DefaultCellModel(prog.getWebDomain());
|
return new DefaultCellModel(prog.getWebDomain())
|
||||||
|
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||||
}, 100),
|
}, 100),
|
||||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
|
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
|
||||||
(prog) -> {
|
(prog) -> {
|
||||||
return new DefaultCellModel(prog.getPath());
|
return new DefaultCellModel(prog.getPath())
|
||||||
|
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||||
}, 250),
|
}, 250),
|
||||||
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
|
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
|
||||||
(prog) -> {
|
(prog) -> {
|
||||||
return new DefaultCellModel(prog.getDateAsString());
|
return new DefaultCellModel(prog.getDateAsString())
|
||||||
|
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||||
}, 80));
|
}, 80));
|
||||||
|
|
||||||
ListTableModel<RecentDownloadDetails> tableModel = JTablePanel.getTableModel(list);
|
ListTableModel<RecentDownloadDetails> tableModel = JTablePanel.getTableModel(list);
|
||||||
@ -165,6 +204,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
|||||||
pane.setModel(tableModel);
|
pane.setModel(tableModel);
|
||||||
pane.setKeyFunction((download) -> download.getPath());
|
pane.setKeyFunction((download) -> download.getPath());
|
||||||
pane.setColumnModel(JTablePanel.getTableColumnModel(list));
|
pane.setColumnModel(JTablePanel.getTableColumnModel(list));
|
||||||
|
pane.setCellListener(CellModelTableCellRenderer.getMouseListener());
|
||||||
tablePanelList.add(pane);
|
tablePanelList.add(pane);
|
||||||
|
|
||||||
DataFetchWorker.DataFetchComponents<DataSource, List<RecentDownloadDetails>> worker
|
DataFetchWorker.DataFetchComponents<DataSource, List<RecentDownloadDetails>> worker
|
||||||
@ -187,15 +227,18 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
|||||||
List<ColumnModel<RecentAttachmentDetails>> list = Arrays.asList(
|
List<ColumnModel<RecentAttachmentDetails>> list = Arrays.asList(
|
||||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
|
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
|
||||||
(prog) -> {
|
(prog) -> {
|
||||||
return new DefaultCellModel(prog.getPath());
|
return new DefaultCellModel(prog.getPath())
|
||||||
|
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||||
}, 250),
|
}, 250),
|
||||||
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
|
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
|
||||||
(prog) -> {
|
(prog) -> {
|
||||||
return new DefaultCellModel(prog.getDateAsString());
|
return new DefaultCellModel(prog.getDateAsString())
|
||||||
|
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||||
}, 80),
|
}, 80),
|
||||||
new ColumnModel<>(Bundle.RecentFilePanel_col_header_sender(),
|
new ColumnModel<>(Bundle.RecentFilePanel_col_header_sender(),
|
||||||
(prog) -> {
|
(prog) -> {
|
||||||
return new DefaultCellModel(prog.getSender());
|
return new DefaultCellModel(prog.getSender())
|
||||||
|
.setPopupMenuRetriever(getPopupFunct(prog));
|
||||||
}, 150));
|
}, 150));
|
||||||
|
|
||||||
ListTableModel<RecentAttachmentDetails> tableModel = JTablePanel.getTableModel(list);
|
ListTableModel<RecentAttachmentDetails> tableModel = JTablePanel.getTableModel(list);
|
||||||
@ -204,6 +247,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
|||||||
pane.setModel(tableModel);
|
pane.setModel(tableModel);
|
||||||
pane.setKeyFunction((attachment) -> attachment.getPath());
|
pane.setKeyFunction((attachment) -> attachment.getPath());
|
||||||
pane.setColumnModel(JTablePanel.getTableColumnModel(list));
|
pane.setColumnModel(JTablePanel.getTableColumnModel(list));
|
||||||
|
pane.setCellListener(CellModelTableCellRenderer.getMouseListener());
|
||||||
tablePanelList.add(pane);
|
tablePanelList.add(pane);
|
||||||
|
|
||||||
DataFetchWorker.DataFetchComponents<DataSource, List<RecentAttachmentDetails>> worker
|
DataFetchWorker.DataFetchComponents<DataSource, List<RecentAttachmentDetails>> worker
|
||||||
@ -234,6 +278,9 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
|||||||
javax.swing.JLabel openDocsLabel = new javax.swing.JLabel();
|
javax.swing.JLabel openDocsLabel = new javax.swing.JLabel();
|
||||||
javax.swing.JLabel downloadLabel = new javax.swing.JLabel();
|
javax.swing.JLabel downloadLabel = new javax.swing.JLabel();
|
||||||
javax.swing.JLabel attachmentLabel = new javax.swing.JLabel();
|
javax.swing.JLabel attachmentLabel = 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());
|
setLayout(new java.awt.BorderLayout());
|
||||||
|
|
||||||
@ -263,7 +310,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
|||||||
tablePanel.add(openedDocPane, gridBagConstraints);
|
tablePanel.add(openedDocPane, gridBagConstraints);
|
||||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||||
gridBagConstraints.gridx = 0;
|
gridBagConstraints.gridx = 0;
|
||||||
gridBagConstraints.gridy = 4;
|
gridBagConstraints.gridy = 5;
|
||||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
|
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
|
||||||
gridBagConstraints.weightx = 1.0;
|
gridBagConstraints.weightx = 1.0;
|
||||||
@ -272,7 +319,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
|||||||
tablePanel.add(downloadsPane, gridBagConstraints);
|
tablePanel.add(downloadsPane, gridBagConstraints);
|
||||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||||
gridBagConstraints.gridx = 0;
|
gridBagConstraints.gridx = 0;
|
||||||
gridBagConstraints.gridy = 6;
|
gridBagConstraints.gridy = 8;
|
||||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
|
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
|
||||||
gridBagConstraints.weightx = 1.0;
|
gridBagConstraints.weightx = 1.0;
|
||||||
@ -291,7 +338,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
|||||||
org.openide.awt.Mnemonics.setLocalizedText(downloadLabel, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.downloadLabel.text")); // NOI18N
|
org.openide.awt.Mnemonics.setLocalizedText(downloadLabel, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.downloadLabel.text")); // NOI18N
|
||||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||||
gridBagConstraints.gridx = 0;
|
gridBagConstraints.gridx = 0;
|
||||||
gridBagConstraints.gridy = 3;
|
gridBagConstraints.gridy = 4;
|
||||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||||
gridBagConstraints.insets = new java.awt.Insets(20, 0, 0, 0);
|
gridBagConstraints.insets = new java.awt.Insets(20, 0, 0, 0);
|
||||||
tablePanel.add(downloadLabel, gridBagConstraints);
|
tablePanel.add(downloadLabel, gridBagConstraints);
|
||||||
@ -299,12 +346,36 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
|
|||||||
org.openide.awt.Mnemonics.setLocalizedText(attachmentLabel, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.attachmentLabel.text")); // NOI18N
|
org.openide.awt.Mnemonics.setLocalizedText(attachmentLabel, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.attachmentLabel.text")); // NOI18N
|
||||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||||
gridBagConstraints.gridx = 0;
|
gridBagConstraints.gridx = 0;
|
||||||
gridBagConstraints.gridy = 5;
|
gridBagConstraints.gridy = 7;
|
||||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
|
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
|
||||||
gridBagConstraints.insets = new java.awt.Insets(20, 0, 0, 0);
|
gridBagConstraints.insets = new java.awt.Insets(20, 0, 0, 0);
|
||||||
tablePanel.add(attachmentLabel, gridBagConstraints);
|
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);
|
scrollPane.setViewportView(tablePanel);
|
||||||
|
|
||||||
add(scrollPane, java.awt.BorderLayout.CENTER);
|
add(scrollPane, java.awt.BorderLayout.CENTER);
|
||||||
|
@ -171,7 +171,7 @@
|
|||||||
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
|
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.VerticalStrut"/>
|
||||||
</AuxValues>
|
</AuxValues>
|
||||||
</Component>
|
</Component>
|
||||||
<Container class="javax.swing.JPanel" name="sameIdPanel">
|
<Container class="javax.swing.JPanel" name="last30DaysPanel">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="alignmentX" type="float" value="0.0"/>
|
<Property name="alignmentX" type="float" value="0.0"/>
|
||||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
@ -193,6 +193,17 @@
|
|||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||||
</Container>
|
</Container>
|
||||||
|
<Component class="javax.swing.JButton" name="viewInTimelineBtn">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="TimelinePanel.viewInTimelineBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="enabled" type="boolean" value="false"/>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="viewInTimelineBtnActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
<Component class="javax.swing.Box$Filler" name="filler5">
|
<Component class="javax.swing.Box$Filler" name="filler5">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
@ -26,8 +26,15 @@ import java.util.Arrays;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.logging.Level;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
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.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;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData;
|
||||||
@ -41,7 +48,11 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetch
|
|||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
|
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.datamodel.DataSource;
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tab shown in data source summary displaying information about a data
|
* A tab shown in data source summary displaying information about a data
|
||||||
@ -55,6 +66,7 @@ import org.sleuthkit.datamodel.DataSource;
|
|||||||
"TimlinePanel_last30DaysChart_artifactEvts_title=Artifact Events",})
|
"TimlinePanel_last30DaysChart_artifactEvts_title=Artifact Events",})
|
||||||
public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(TimelinePanel.class.getName());
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat("MMM d, yyyy");
|
private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat("MMM d, yyyy");
|
||||||
private static final DateFormat CHART_FORMAT = getUtcFormat("MMM d");
|
private static final DateFormat CHART_FORMAT = getUtcFormat("MMM d");
|
||||||
@ -75,6 +87,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
|||||||
private final LoadableLabel earliestLabel = new LoadableLabel(Bundle.TimelinePanel_earliestLabel_title());
|
private final LoadableLabel earliestLabel = new LoadableLabel(Bundle.TimelinePanel_earliestLabel_title());
|
||||||
private final LoadableLabel latestLabel = new LoadableLabel(Bundle.TimelinePanel_latestLabel_title());
|
private final LoadableLabel latestLabel = new LoadableLabel(Bundle.TimelinePanel_latestLabel_title());
|
||||||
private final BarChartPanel last30DaysChart = new BarChartPanel(Bundle.TimlinePanel_last30DaysChart_title(), "", "");
|
private final BarChartPanel last30DaysChart = new BarChartPanel(Bundle.TimlinePanel_last30DaysChart_title(), "", "");
|
||||||
|
private final TimelineDataSourceUtils timelineUtils = TimelineDataSourceUtils.getInstance();
|
||||||
|
|
||||||
// all loadable components on this tab
|
// all loadable components on this tab
|
||||||
private final List<LoadableComponent<?>> loadableComponents = Arrays.asList(earliestLabel, latestLabel, last30DaysChart);
|
private final List<LoadableComponent<?>> loadableComponents = Arrays.asList(earliestLabel, latestLabel, last30DaysChart);
|
||||||
@ -151,6 +164,9 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
|||||||
new BarChartSeries(Bundle.TimlinePanel_last30DaysChart_artifactEvts_title(), ARTIFACT_EVT_COLOR, artifactEvtCounts));
|
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
|
* Handles displaying the result for each displayable item in the
|
||||||
* TimelinePanel by breaking the TimelineSummaryData result into its
|
* TimelinePanel by breaking the TimelineSummaryData result into its
|
||||||
@ -163,6 +179,87 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
|||||||
earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT)));
|
earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT)));
|
||||||
latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT)));
|
latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT)));
|
||||||
last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity())));
|
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();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
Interval timeSpan = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final TimeLineController controller = TimeLineModule.getController();
|
||||||
|
|
||||||
|
if (dataSource != null) {
|
||||||
|
controller.pushFilters(timelineUtils.getDataSourceFilterState(dataSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minDate != null && maxDate != null) {
|
||||||
|
timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate));
|
||||||
|
}
|
||||||
|
} catch (NoCurrentCaseException | TskCoreException 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
|
@Override
|
||||||
@ -198,7 +295,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
|||||||
javax.swing.JPanel earliestLabelPanel = earliestLabel;
|
javax.swing.JPanel earliestLabelPanel = earliestLabel;
|
||||||
javax.swing.JPanel latestLabelPanel = latestLabel;
|
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.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;
|
||||||
|
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));
|
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));
|
mainContentPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
@ -233,12 +331,21 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
|||||||
filler2.setAlignmentX(0.0F);
|
filler2.setAlignmentX(0.0F);
|
||||||
mainContentPanel.add(filler2);
|
mainContentPanel.add(filler2);
|
||||||
|
|
||||||
sameIdPanel.setAlignmentX(0.0F);
|
last30DaysPanel.setAlignmentX(0.0F);
|
||||||
sameIdPanel.setMaximumSize(new java.awt.Dimension(600, 300));
|
last30DaysPanel.setMaximumSize(new java.awt.Dimension(600, 300));
|
||||||
sameIdPanel.setMinimumSize(new java.awt.Dimension(600, 300));
|
last30DaysPanel.setMinimumSize(new java.awt.Dimension(600, 300));
|
||||||
sameIdPanel.setPreferredSize(new java.awt.Dimension(600, 300));
|
last30DaysPanel.setPreferredSize(new java.awt.Dimension(600, 300));
|
||||||
sameIdPanel.setVerifyInputWhenFocusTarget(false);
|
last30DaysPanel.setVerifyInputWhenFocusTarget(false);
|
||||||
mainContentPanel.add(sameIdPanel);
|
mainContentPanel.add(last30DaysPanel);
|
||||||
|
|
||||||
|
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);
|
filler5.setAlignmentX(0.0F);
|
||||||
mainContentPanel.add(filler5);
|
mainContentPanel.add(filler5);
|
||||||
@ -257,7 +364,11 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
|
|||||||
);
|
);
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
|
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
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
|
private javax.swing.JButton viewInTimelineBtn;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
}
|
}
|
||||||
|
@ -135,6 +135,17 @@
|
|||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||||
</Container>
|
</Container>
|
||||||
|
<Component class="javax.swing.JLabel" name="rightClickForMoreOptions1">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="UserActivityPanel.rightClickForMoreOptions1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
<Component class="javax.swing.Box$Filler" name="filler3">
|
<Component class="javax.swing.Box$Filler" name="filler3">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
@ -204,6 +215,17 @@
|
|||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||||
</Container>
|
</Container>
|
||||||
|
<Component class="javax.swing.JLabel" name="rightClickForMoreOptions2">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="UserActivityPanel.rightClickForMoreOptions2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
<Component class="javax.swing.Box$Filler" name="filler4">
|
<Component class="javax.swing.Box$Filler" name="filler4">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
@ -273,6 +295,17 @@
|
|||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||||
</Container>
|
</Container>
|
||||||
|
<Component class="javax.swing.JLabel" name="rightClickForMoreOptions3">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="UserActivityPanel.rightClickForMoreOptions3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
<Component class="javax.swing.Box$Filler" name="filler6">
|
<Component class="javax.swing.Box$Filler" name="filler6">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
@ -342,6 +375,17 @@
|
|||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||||
</Container>
|
</Container>
|
||||||
|
<Component class="javax.swing.JLabel" name="rightClickForMoreOptions4">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="UserActivityPanel.rightClickForMoreOptions4.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
<Component class="javax.swing.Box$Filler" name="filler8">
|
<Component class="javax.swing.Box$Filler" name="filler8">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
@ -411,6 +455,17 @@
|
|||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||||
</Container>
|
</Container>
|
||||||
|
<Component class="javax.swing.JLabel" name="rightClickForMoreOptions5">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="UserActivityPanel.rightClickForMoreOptions5.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AuxValues>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
|
||||||
|
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
|
||||||
|
</AuxValues>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Container>
|
</Container>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
|
@ -29,12 +29,14 @@ import org.apache.commons.lang.StringUtils;
|
|||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary;
|
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.TopAccountResult;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult;
|
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
|
||||||
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.MenuItem;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
|
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
|
||||||
@ -92,7 +94,8 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
Bundle.UserActivityPanel_TopProgramsTableModel_name_header(),
|
Bundle.UserActivityPanel_TopProgramsTableModel_name_header(),
|
||||||
(prog) -> {
|
(prog) -> {
|
||||||
return new DefaultCellModel(prog.getProgramName())
|
return new DefaultCellModel(prog.getProgramName())
|
||||||
.setTooltip(prog.getProgramPath());
|
.setTooltip(prog.getProgramPath())
|
||||||
|
.setPopupMenu(getPopup(prog));
|
||||||
},
|
},
|
||||||
250),
|
250),
|
||||||
// program folder column
|
// program folder column
|
||||||
@ -103,7 +106,8 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
getShortFolderName(
|
getShortFolderName(
|
||||||
prog.getProgramPath(),
|
prog.getProgramPath(),
|
||||||
prog.getProgramName()))
|
prog.getProgramName()))
|
||||||
.setTooltip(prog.getProgramPath());
|
.setTooltip(prog.getProgramPath())
|
||||||
|
.setPopupMenu(getPopup(prog));
|
||||||
},
|
},
|
||||||
150),
|
150),
|
||||||
// run count column
|
// run count column
|
||||||
@ -111,13 +115,17 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
Bundle.UserActivityPanel_TopProgramsTableModel_count_header(),
|
Bundle.UserActivityPanel_TopProgramsTableModel_count_header(),
|
||||||
(prog) -> {
|
(prog) -> {
|
||||||
String runTimes = prog.getRunTimes() == null ? "" : Long.toString(prog.getRunTimes());
|
String runTimes = prog.getRunTimes() == null ? "" : Long.toString(prog.getRunTimes());
|
||||||
return new DefaultCellModel(runTimes);
|
return new DefaultCellModel(runTimes)
|
||||||
|
.setPopupMenu(getPopup(prog));
|
||||||
},
|
},
|
||||||
80),
|
80),
|
||||||
// last run date column
|
// last run date column
|
||||||
new ColumnModel<>(
|
new ColumnModel<>(
|
||||||
Bundle.UserActivityPanel_TopProgramsTableModel_lastrun_header(),
|
Bundle.UserActivityPanel_TopProgramsTableModel_lastrun_header(),
|
||||||
(prog) -> new DefaultCellModel(getFormatted(prog.getLastRun())),
|
(prog) -> {
|
||||||
|
return new DefaultCellModel(getFormatted(prog.getLastAccessed()))
|
||||||
|
.setPopupMenu(getPopup(prog));
|
||||||
|
},
|
||||||
150)
|
150)
|
||||||
))
|
))
|
||||||
.setKeyFunction((prog) -> prog.getProgramPath() + ":" + prog.getProgramName());
|
.setKeyFunction((prog) -> prog.getProgramPath() + ":" + prog.getProgramName());
|
||||||
@ -127,20 +135,27 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
// domain column
|
// domain column
|
||||||
new ColumnModel<TopDomainsResult>(
|
new ColumnModel<TopDomainsResult>(
|
||||||
Bundle.UserActivityPanel_TopDomainsTableModel_domain_header(),
|
Bundle.UserActivityPanel_TopDomainsTableModel_domain_header(),
|
||||||
(recentDomain) -> new DefaultCellModel(recentDomain.getDomain()),
|
(recentDomain) -> {
|
||||||
|
return new DefaultCellModel(recentDomain.getDomain())
|
||||||
|
.setPopupMenu(getPopup(recentDomain));
|
||||||
|
},
|
||||||
250),
|
250),
|
||||||
// count column
|
// count column
|
||||||
new ColumnModel<>(
|
new ColumnModel<>(
|
||||||
Bundle.UserActivityPanel_TopDomainsTableModel_count_header(),
|
Bundle.UserActivityPanel_TopDomainsTableModel_count_header(),
|
||||||
(recentDomain) -> {
|
(recentDomain) -> {
|
||||||
String visitTimes = recentDomain.getVisitTimes() == null ? "" : Long.toString(recentDomain.getVisitTimes());
|
String visitTimes = recentDomain.getVisitTimes() == null ? "" : Long.toString(recentDomain.getVisitTimes());
|
||||||
return new DefaultCellModel(visitTimes);
|
return new DefaultCellModel(visitTimes)
|
||||||
|
.setPopupMenu(getPopup(recentDomain));
|
||||||
},
|
},
|
||||||
100),
|
100),
|
||||||
// last accessed column
|
// last accessed column
|
||||||
new ColumnModel<>(
|
new ColumnModel<>(
|
||||||
Bundle.UserActivityPanel_TopDomainsTableModel_lastAccess_header(),
|
Bundle.UserActivityPanel_TopDomainsTableModel_lastAccess_header(),
|
||||||
(recentDomain) -> new DefaultCellModel(getFormatted(recentDomain.getLastVisit())),
|
(recentDomain) -> {
|
||||||
|
return new DefaultCellModel(getFormatted(recentDomain.getLastAccessed()))
|
||||||
|
.setPopupMenu(getPopup(recentDomain));
|
||||||
|
},
|
||||||
150)
|
150)
|
||||||
))
|
))
|
||||||
.setKeyFunction((domain) -> domain.getDomain());
|
.setKeyFunction((domain) -> domain.getDomain());
|
||||||
@ -150,19 +165,28 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
// search string column
|
// search string column
|
||||||
new ColumnModel<TopWebSearchResult>(
|
new ColumnModel<TopWebSearchResult>(
|
||||||
Bundle.UserActivityPanel_TopWebSearchTableModel_searchString_header(),
|
Bundle.UserActivityPanel_TopWebSearchTableModel_searchString_header(),
|
||||||
(webSearch) -> new DefaultCellModel(webSearch.getSearchString()),
|
(webSearch) -> {
|
||||||
|
return new DefaultCellModel(webSearch.getSearchString())
|
||||||
|
.setPopupMenu(getPopup(webSearch));
|
||||||
|
},
|
||||||
250
|
250
|
||||||
),
|
),
|
||||||
// last accessed
|
// last accessed
|
||||||
new ColumnModel<>(
|
new ColumnModel<>(
|
||||||
Bundle.UserActivityPanel_TopWebSearchTableModel_dateAccessed_header(),
|
Bundle.UserActivityPanel_TopWebSearchTableModel_dateAccessed_header(),
|
||||||
(webSearch) -> new DefaultCellModel(getFormatted(webSearch.getDateAccessed())),
|
(webSearch) -> {
|
||||||
|
return new DefaultCellModel(getFormatted(webSearch.getLastAccessed()))
|
||||||
|
.setPopupMenu(getPopup(webSearch));
|
||||||
|
},
|
||||||
150
|
150
|
||||||
),
|
),
|
||||||
// translated value
|
// translated value
|
||||||
new ColumnModel<>(
|
new ColumnModel<>(
|
||||||
Bundle.UserActivityPanel_TopWebSearchTableModel_translatedResult_header(),
|
Bundle.UserActivityPanel_TopWebSearchTableModel_translatedResult_header(),
|
||||||
(webSearch) -> new DefaultCellModel(webSearch.getTranslatedResult()),
|
(webSearch) -> {
|
||||||
|
return new DefaultCellModel(webSearch.getTranslatedResult())
|
||||||
|
.setPopupMenu(getPopup(webSearch));
|
||||||
|
},
|
||||||
250
|
250
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
@ -173,13 +197,19 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
// device id column
|
// device id column
|
||||||
new ColumnModel<TopDeviceAttachedResult>(
|
new ColumnModel<TopDeviceAttachedResult>(
|
||||||
Bundle.UserActivityPanel_TopDeviceAttachedTableModel_deviceId_header(),
|
Bundle.UserActivityPanel_TopDeviceAttachedTableModel_deviceId_header(),
|
||||||
(device) -> new DefaultCellModel(device.getDeviceId()),
|
(device) -> {
|
||||||
|
return new DefaultCellModel(device.getDeviceId())
|
||||||
|
.setPopupMenu(getPopup(device));
|
||||||
|
},
|
||||||
250
|
250
|
||||||
),
|
),
|
||||||
// last accessed
|
// last accessed
|
||||||
new ColumnModel<>(
|
new ColumnModel<>(
|
||||||
Bundle.UserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header(),
|
Bundle.UserActivityPanel_TopDeviceAttachedTableModel_dateAccessed_header(),
|
||||||
(device) -> new DefaultCellModel(getFormatted(device.getDateAccessed())),
|
(device) -> {
|
||||||
|
return new DefaultCellModel(getFormatted(device.getLastAccessed()))
|
||||||
|
.setPopupMenu(getPopup(device));
|
||||||
|
},
|
||||||
150
|
150
|
||||||
),
|
),
|
||||||
// make and model
|
// make and model
|
||||||
@ -191,7 +221,8 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
String makeModelString = (make.isEmpty() || model.isEmpty())
|
String makeModelString = (make.isEmpty() || model.isEmpty())
|
||||||
? make + model
|
? make + model
|
||||||
: String.format("%s - %s", make, model);
|
: String.format("%s - %s", make, model);
|
||||||
return new DefaultCellModel(makeModelString);
|
return new DefaultCellModel(makeModelString)
|
||||||
|
.setPopupMenu(getPopup(device));
|
||||||
},
|
},
|
||||||
250
|
250
|
||||||
)
|
)
|
||||||
@ -203,13 +234,19 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
// account type column
|
// account type column
|
||||||
new ColumnModel<TopAccountResult>(
|
new ColumnModel<TopAccountResult>(
|
||||||
Bundle.UserActivityPanel_TopAccountTableModel_accountType_header(),
|
Bundle.UserActivityPanel_TopAccountTableModel_accountType_header(),
|
||||||
(account) -> new DefaultCellModel(account.getAccountType()),
|
(account) -> {
|
||||||
|
return new DefaultCellModel(account.getAccountType())
|
||||||
|
.setPopupMenu(getPopup(account));
|
||||||
|
},
|
||||||
250
|
250
|
||||||
),
|
),
|
||||||
// last accessed
|
// last accessed
|
||||||
new ColumnModel<>(
|
new ColumnModel<>(
|
||||||
Bundle.UserActivityPanel_TopAccountTableModel_lastAccess_header(),
|
Bundle.UserActivityPanel_TopAccountTableModel_lastAccess_header(),
|
||||||
(account) -> new DefaultCellModel(getFormatted(account.getLastAccess())),
|
(account) -> {
|
||||||
|
return new DefaultCellModel(getFormatted(account.getLastAccessed()))
|
||||||
|
.setPopupMenu(getPopup(account));
|
||||||
|
},
|
||||||
150
|
150
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
@ -292,6 +329,19 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
initComponents();
|
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<MenuItem> getPopup(LastAccessedArtifact record) {
|
||||||
|
return record == null ? null : Arrays.asList(getArtifactNavigateItem(record.getArtifact()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries DataSourceTopProgramsSummary instance for short folder name.
|
* Queries DataSourceTopProgramsSummary instance for short folder name.
|
||||||
*
|
*
|
||||||
@ -335,22 +385,27 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
javax.swing.JLabel programsRunLabel = new javax.swing.JLabel();
|
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.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;
|
javax.swing.JPanel topProgramsTablePanel = topProgramsTable;
|
||||||
|
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.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.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.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;
|
javax.swing.JPanel recentDomainsTablePanel = recentDomainsTable;
|
||||||
|
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.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.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.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;
|
javax.swing.JPanel topWebSearches = topWebSearchesTable;
|
||||||
|
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.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.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.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;
|
javax.swing.JPanel recentDevicesAttached = topDevicesAttachedTable;
|
||||||
|
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.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.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.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;
|
javax.swing.JPanel topAccounts = topAccountsTable;
|
||||||
|
javax.swing.JLabel rightClickForMoreOptions5 = new javax.swing.JLabel();
|
||||||
|
|
||||||
setLayout(new java.awt.BorderLayout());
|
setLayout(new java.awt.BorderLayout());
|
||||||
|
|
||||||
@ -379,6 +434,9 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
topProgramsTablePanel.setMinimumSize(new java.awt.Dimension(10, 106));
|
topProgramsTablePanel.setMinimumSize(new java.awt.Dimension(10, 106));
|
||||||
topProgramsTablePanel.setPreferredSize(new java.awt.Dimension(10, 106));
|
topProgramsTablePanel.setPreferredSize(new java.awt.Dimension(10, 106));
|
||||||
contentPanel.add(topProgramsTablePanel);
|
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);
|
contentPanel.add(filler3);
|
||||||
|
|
||||||
recentDomainsLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
recentDomainsLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
||||||
@ -391,6 +449,9 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
recentDomainsTablePanel.setMinimumSize(new java.awt.Dimension(10, 106));
|
recentDomainsTablePanel.setMinimumSize(new java.awt.Dimension(10, 106));
|
||||||
recentDomainsTablePanel.setPreferredSize(new java.awt.Dimension(10, 106));
|
recentDomainsTablePanel.setPreferredSize(new java.awt.Dimension(10, 106));
|
||||||
contentPanel.add(recentDomainsTablePanel);
|
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);
|
contentPanel.add(filler4);
|
||||||
|
|
||||||
topWebSearchLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
topWebSearchLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
||||||
@ -403,6 +464,9 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
topWebSearches.setMinimumSize(new java.awt.Dimension(10, 106));
|
topWebSearches.setMinimumSize(new java.awt.Dimension(10, 106));
|
||||||
topWebSearches.setPreferredSize(new java.awt.Dimension(10, 106));
|
topWebSearches.setPreferredSize(new java.awt.Dimension(10, 106));
|
||||||
contentPanel.add(topWebSearches);
|
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);
|
contentPanel.add(filler6);
|
||||||
|
|
||||||
topDevicesAttachedLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
topDevicesAttachedLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
||||||
@ -415,6 +479,9 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
recentDevicesAttached.setMinimumSize(new java.awt.Dimension(10, 106));
|
recentDevicesAttached.setMinimumSize(new java.awt.Dimension(10, 106));
|
||||||
recentDevicesAttached.setPreferredSize(new java.awt.Dimension(10, 106));
|
recentDevicesAttached.setPreferredSize(new java.awt.Dimension(10, 106));
|
||||||
contentPanel.add(recentDevicesAttached);
|
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);
|
contentPanel.add(filler8);
|
||||||
|
|
||||||
recentAccountsLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
recentAccountsLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
||||||
@ -428,6 +495,9 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
|
|||||||
topAccounts.setPreferredSize(new java.awt.Dimension(10, 106));
|
topAccounts.setPreferredSize(new java.awt.Dimension(10, 106));
|
||||||
contentPanel.add(topAccounts);
|
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);
|
contentScrollPane.setViewportView(contentPanel);
|
||||||
|
|
||||||
add(contentScrollPane, java.awt.BorderLayout.CENTER);
|
add(contentScrollPane, java.awt.BorderLayout.CENTER);
|
||||||
|
@ -304,4 +304,5 @@ public class BarChartPanel extends AbstractLoadableComponent<List<BarChartPanel.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,22 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
|||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
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.BorderFactory;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.table.DefaultTableCellRenderer;
|
import javax.swing.table.DefaultTableCellRenderer;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
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
|
* A Table cell renderer that renders a cell of a table based off of the
|
||||||
@ -64,6 +74,53 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Runnable getAction() {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic interface for a cell model.
|
* Basic interface for a cell model.
|
||||||
*/
|
*/
|
||||||
@ -88,6 +145,12 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer {
|
|||||||
* @return The insets for the cell text.
|
* @return The insets for the cell text.
|
||||||
*/
|
*/
|
||||||
Insets getInsets();
|
Insets getInsets();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The popup menu associated with this cell or null if no popup
|
||||||
|
* menu should be shown for this cell.
|
||||||
|
*/
|
||||||
|
List<MenuItem> getPopupMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,6 +162,8 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer {
|
|||||||
private String tooltip;
|
private String tooltip;
|
||||||
private HorizontalAlign horizontalAlignment;
|
private HorizontalAlign horizontalAlignment;
|
||||||
private Insets insets;
|
private Insets insets;
|
||||||
|
private List<MenuItem> popupMenu;
|
||||||
|
private Supplier<List<MenuItem>> menuItemSupplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main constructor.
|
* Main constructor.
|
||||||
@ -166,6 +231,41 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<MenuItem> getPopupMenu() {
|
||||||
|
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<List<MenuItem>> menuItemSupplier) {
|
||||||
|
this.menuItemSupplier = menuItemSupplier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list of items for a popup menu
|
||||||
|
*
|
||||||
|
* @param popupMenu
|
||||||
|
* @return As a utility, returns this.
|
||||||
|
*/
|
||||||
|
public DefaultCellModel setPopupMenu(List<MenuItem> popupMenu) {
|
||||||
|
this.popupMenu = popupMenu == null ? null : new ArrayList<>(popupMenu);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getText();
|
return getText();
|
||||||
@ -231,4 +331,42 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer {
|
|||||||
|
|
||||||
return defaultCell;
|
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
|
||||||
|
public void mouseClicked(CellMouseEvent cellEvent) {
|
||||||
|
if (cellEvent.getCellValue() instanceof CellModel && cellEvent.getMouseEvent().getButton() != MouseEvent.BUTTON1) {
|
||||||
|
cellEvent.getTable().setRowSelectionInterval(cellEvent.getRow(), cellEvent.getRow());
|
||||||
|
CellModel cellModel = (CellModel) cellEvent.getCellValue();
|
||||||
|
List<MenuItem> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The default cell mouse listener that triggers popups for
|
||||||
|
* non-primary button events.
|
||||||
|
*/
|
||||||
|
public static CellMouseListener getMouseListener() {
|
||||||
|
return DEFAULT_CELL_MOUSE_LISTENER;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
|||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@ -40,6 +42,85 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRendere
|
|||||||
*/
|
*/
|
||||||
public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
|
public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
this.row = row;
|
||||||
|
this.col = col;
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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
|
* JTables don't allow displaying messages. So this LayerUI is used to
|
||||||
* display the contents of a child JLabel. Inspired by TableWaitLayerTest
|
* display the contents of a child JLabel. Inspired by TableWaitLayerTest
|
||||||
@ -197,14 +278,18 @@ public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
|
|||||||
*/
|
*/
|
||||||
public static <T> JTablePanel<T> getJTablePanel(List<ColumnModel<T>> columns) {
|
public static <T> JTablePanel<T> getJTablePanel(List<ColumnModel<T>> columns) {
|
||||||
ListTableModel<T> tableModel = getTableModel(columns);
|
ListTableModel<T> tableModel = getTableModel(columns);
|
||||||
JTablePanel<T> resultTable = new JTablePanel<>(tableModel);
|
JTablePanel<T> resultTable = new JTablePanel<>(tableModel)
|
||||||
return resultTable.setColumnModel(getTableColumnModel(columns));
|
.setColumnModel(getTableColumnModel(columns))
|
||||||
|
.setCellListener(CellModelTableCellRenderer.getMouseListener());
|
||||||
|
|
||||||
|
return resultTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JScrollPane tableScrollPane;
|
private JScrollPane tableScrollPane;
|
||||||
private Overlay overlayLayer;
|
private Overlay overlayLayer;
|
||||||
private ListTableModel<T> tableModel;
|
private ListTableModel<T> tableModel;
|
||||||
private JTable table;
|
private JTable table;
|
||||||
|
private CellMouseListener cellListener = null;
|
||||||
private Function<T, ? extends Object> keyFunction = (rowItem) -> rowItem;
|
private Function<T, ? extends Object> keyFunction = (rowItem) -> rowItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -222,6 +307,26 @@ public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
|
|||||||
*/
|
*/
|
||||||
public JTablePanel() {
|
public JTablePanel() {
|
||||||
initComponents();
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -242,6 +347,26 @@ public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
|
|||||||
return this;
|
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<T> setCellListener(CellMouseListener cellListener) {
|
||||||
|
this.cellListener = cellListener;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The underlying JTable's column model.
|
* @return The underlying JTable's column model.
|
||||||
*/
|
*/
|
||||||
@ -263,8 +388,7 @@ public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The function for determining the key for a data row. This key is
|
* @return The function for determining the key for a data row. This key is
|
||||||
* used to maintain current selection in the table despite changing
|
* used to maintain current selection in the table despite changing rows.
|
||||||
* rows.
|
|
||||||
*/
|
*/
|
||||||
public Function<T, ? extends Object> getKeyFunction() {
|
public Function<T, ? extends Object> getKeyFunction() {
|
||||||
return keyFunction;
|
return keyFunction;
|
||||||
@ -330,7 +454,6 @@ public class JTablePanel<T> extends AbstractLoadableComponent<List<T>> {
|
|||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
table = new JTable();
|
table = new JTable();
|
||||||
table.getTableHeader().setReorderingAllowed(false);
|
table.getTableHeader().setReorderingAllowed(false);
|
||||||
|
|
||||||
overlayLayer = new Overlay();
|
overlayLayer = new Overlay();
|
||||||
tableScrollPane = new JScrollPane(table);
|
tableScrollPane = new JScrollPane(table);
|
||||||
JLayer<JComponent> dualLayer = new JLayer<>(tableScrollPane, overlayLayer);
|
JLayer<JComponent> dualLayer = new JLayer<>(tableScrollPane, overlayLayer);
|
||||||
|
@ -22,6 +22,7 @@ import com.google.common.cache.CacheLoader;
|
|||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
@ -38,17 +39,22 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
import org.openide.util.ImageUtilities;
|
import org.openide.util.ImageUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a thumbnail for the given request. Thumbnail candidates are JPEG files
|
* Loads a thumbnail for the given request. Thumbnail candidates types are defined below.
|
||||||
* that have either TSK_WEB_DOWNLOAD or TSK_WEB_CACHE artifacts that match the
|
* These candidates types must be the source of either TSK_WEB_DOWNLOAD or
|
||||||
* domain name (see the DomainSearch getArtifacts() API). JPEG files are sorted
|
* 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
|
* 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.
|
* TSK_WEB_CACHE artifacts. The first suitable thumbnail is selected.
|
||||||
*/
|
*/
|
||||||
public class DomainSearchThumbnailLoader extends CacheLoader<DomainSearchThumbnailRequest, Image> {
|
public class DomainSearchThumbnailLoader extends CacheLoader<DomainSearchThumbnailRequest, Image> {
|
||||||
|
|
||||||
private static final String UNSUPPORTED_IMAGE = "org/sleuthkit/autopsy/images/image-extraction-not-supported.png";
|
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 List<String> SUPPORTED_EXTENSIONS = Arrays.asList("jpg", "svg", "png", "webp", "ico", "gif");
|
||||||
private static final String JPG_MIME_TYPE = "image/jpeg";
|
private static final List<String> 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<String> ICO_MIMETYPES = Arrays.asList("image/vnd.microsoft.icon", "image/x-icon");
|
||||||
private final DomainSearchArtifactsCache artifactsCache;
|
private final DomainSearchArtifactsCache artifactsCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,8 +81,21 @@ public class DomainSearchThumbnailLoader extends CacheLoader<DomainSearchThumbna
|
|||||||
final DomainSearchArtifactsRequest webDownloadsRequest = new DomainSearchArtifactsRequest(
|
final DomainSearchArtifactsRequest webDownloadsRequest = new DomainSearchArtifactsRequest(
|
||||||
caseDb, thumbnailRequest.getDomain(), TSK_WEB_DOWNLOAD);
|
caseDb, thumbnailRequest.getDomain(), TSK_WEB_DOWNLOAD);
|
||||||
final List<BlackboardArtifact> webDownloads = artifactsCache.get(webDownloadsRequest);
|
final List<BlackboardArtifact> webDownloads = artifactsCache.get(webDownloadsRequest);
|
||||||
final List<AbstractFile> webDownloadPictures = getJpegsFromWebDownload(caseDb, webDownloads);
|
final List<AbstractFile> webDownloadPictures = getCandidatesFromWebDownloads(caseDb, webDownloads);
|
||||||
Collections.sort(webDownloadPictures, (file1, file2) -> Long.compare(file1.getCrtime(), file2.getCrtime()));
|
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--) {
|
for (int i = webDownloadPictures.size() - 1; i >= 0; i--) {
|
||||||
if(Thread.currentThread().isInterrupted()) {
|
if(Thread.currentThread().isInterrupted()) {
|
||||||
throw new InterruptedException();
|
throw new InterruptedException();
|
||||||
@ -92,8 +111,20 @@ public class DomainSearchThumbnailLoader extends CacheLoader<DomainSearchThumbna
|
|||||||
final DomainSearchArtifactsRequest webCacheRequest = new DomainSearchArtifactsRequest(
|
final DomainSearchArtifactsRequest webCacheRequest = new DomainSearchArtifactsRequest(
|
||||||
caseDb, thumbnailRequest.getDomain(), TSK_WEB_CACHE);
|
caseDb, thumbnailRequest.getDomain(), TSK_WEB_CACHE);
|
||||||
final List<BlackboardArtifact> webCacheArtifacts = artifactsCache.get(webCacheRequest);
|
final List<BlackboardArtifact> webCacheArtifacts = artifactsCache.get(webCacheRequest);
|
||||||
final List<AbstractFile> webCachePictures = getJpegsFromWebCache(caseDb, webCacheArtifacts);
|
final List<AbstractFile> webCachePictures = getCandidatesFromWebCache(caseDb, webCacheArtifacts);
|
||||||
Collections.sort(webCachePictures, (file1, file2) -> Long.compare(file1.getSize(), file2.getSize()));
|
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--) {
|
for (int i = webCachePictures.size() - 1; i >= 0; i--) {
|
||||||
if(Thread.currentThread().isInterrupted()) {
|
if(Thread.currentThread().isInterrupted()) {
|
||||||
throw new InterruptedException();
|
throw new InterruptedException();
|
||||||
@ -109,40 +140,45 @@ public class DomainSearchThumbnailLoader extends CacheLoader<DomainSearchThumbna
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds all JPEG source files from TSK_WEB_DOWNLOAD instances.
|
* Finds all supported image files from TSK_WEB_DOWNLOAD instances.
|
||||||
*
|
*
|
||||||
* @param caseDb The case database being searched.
|
* @param caseDb The case database being searched.
|
||||||
* @param artifacts The list of artifacts to get jpegs from.
|
* @param artifacts The list of artifacts to search.
|
||||||
*
|
*
|
||||||
* @return The list of AbstractFiles representing jpegs which were
|
* @return The list of AbstractFiles representing supported images which were
|
||||||
* associated with the artifacts.
|
* associated with the artifacts.
|
||||||
*
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
*/
|
*/
|
||||||
private List<AbstractFile> getJpegsFromWebDownload(SleuthkitCase caseDb, List<BlackboardArtifact> artifacts) throws TskCoreException, InterruptedException {
|
private List<AbstractFile> getCandidatesFromWebDownloads(SleuthkitCase caseDb, List<BlackboardArtifact> artifacts) throws TskCoreException, InterruptedException {
|
||||||
final List<AbstractFile> jpegs = new ArrayList<>();
|
final List<AbstractFile> candidates = new ArrayList<>();
|
||||||
for (BlackboardArtifact artifact : artifacts) {
|
for (BlackboardArtifact artifact : artifacts) {
|
||||||
if(Thread.currentThread().isInterrupted()) {
|
if(Thread.currentThread().isInterrupted()) {
|
||||||
throw new InterruptedException();
|
throw new InterruptedException();
|
||||||
}
|
}
|
||||||
final Content sourceContent = caseDb.getContentById(artifact.getObjectID());
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds all JPEG source files from TSK_WEB_CACHE instances.
|
* Finds all supported image files from TSK_WEB_CACHE instances.
|
||||||
*
|
*
|
||||||
* @param caseDb The case database being searched.
|
* @param caseDb The case database being searched.
|
||||||
* @param artifacts The list of artifacts to get jpegs from.
|
* @param artifacts The list of artifacts to get images from.
|
||||||
*
|
*
|
||||||
* @return The list of AbstractFiles representing jpegs which were
|
* @return The list of AbstractFiles representing supported images which were
|
||||||
* associated with the artifacts.
|
* associated with the artifacts.
|
||||||
*/
|
*/
|
||||||
private List<AbstractFile> getJpegsFromWebCache(SleuthkitCase caseDb, List<BlackboardArtifact> artifacts) throws TskCoreException, InterruptedException {
|
private List<AbstractFile> getCandidatesFromWebCache(SleuthkitCase caseDb, List<BlackboardArtifact> artifacts) throws TskCoreException, InterruptedException {
|
||||||
final BlackboardAttribute.Type TSK_PATH_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH_ID);
|
final BlackboardAttribute.Type TSK_PATH_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH_ID);
|
||||||
final List<AbstractFile> jpegs = new ArrayList<>();
|
final List<AbstractFile> candidates = new ArrayList<>();
|
||||||
for (BlackboardArtifact artifact : artifacts) {
|
for (BlackboardArtifact artifact : artifacts) {
|
||||||
if(Thread.currentThread().isInterrupted()) {
|
if(Thread.currentThread().isInterrupted()) {
|
||||||
throw new InterruptedException();
|
throw new InterruptedException();
|
||||||
@ -150,24 +186,23 @@ public class DomainSearchThumbnailLoader extends CacheLoader<DomainSearchThumbna
|
|||||||
final BlackboardAttribute tskPathId = artifact.getAttribute(TSK_PATH_ID);
|
final BlackboardAttribute tskPathId = artifact.getAttribute(TSK_PATH_ID);
|
||||||
if (tskPathId != null) {
|
if (tskPathId != null) {
|
||||||
final Content sourceContent = caseDb.getContentById(tskPathId.getValueLong());
|
final Content sourceContent = caseDb.getContentById(tskPathId.getValueLong());
|
||||||
addIfJpeg(jpegs, sourceContent);
|
addIfSupported(candidates, sourceContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return jpegs;
|
return candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the candidate source content is indeed a JPEG file.
|
* Checks if the candidate source content is indeed a supported type.
|
||||||
*
|
*
|
||||||
* @param files The list of source content files which are jpegs to
|
* @param files The list of source content files which are supported.
|
||||||
* add to.
|
|
||||||
* @param sourceContent The source content to check and possibly add.
|
* @param sourceContent The source content to check and possibly add.
|
||||||
*/
|
*/
|
||||||
private void addIfJpeg(List<AbstractFile> files, Content sourceContent) {
|
private void addIfSupported(List<AbstractFile> files, Content sourceContent) {
|
||||||
if ((sourceContent instanceof AbstractFile) && !(sourceContent instanceof DataSource)) {
|
if ((sourceContent instanceof AbstractFile) && !(sourceContent instanceof DataSource)) {
|
||||||
final AbstractFile file = (AbstractFile) sourceContent;
|
final AbstractFile file = (AbstractFile) sourceContent;
|
||||||
if (JPG_EXTENSION.equals(file.getNameExtension())
|
if (SUPPORTED_EXTENSIONS.contains(file.getNameExtension())
|
||||||
|| JPG_MIME_TYPE.equals(file.getMIMEType())) {
|
|| SUPPORTED_MIMETYPES.contains(file.getMIMEType())) {
|
||||||
files.add(file);
|
files.add(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,7 @@ class ArtifactsWorker extends SwingWorker<List<BlackboardArtifact>, Void> {
|
|||||||
if (!isCancelled()) {
|
if (!isCancelled()) {
|
||||||
try {
|
try {
|
||||||
listOfArtifacts.addAll(get());
|
listOfArtifacts.addAll(get());
|
||||||
|
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.ArtifactSearchResultEvent(artifactType, listOfArtifacts));
|
||||||
} catch (InterruptedException | ExecutionException ex) {
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
logger.log(Level.SEVERE, "Exception while trying to get list of artifacts for Domain details for artifact type: "
|
logger.log(Level.SEVERE, "Exception while trying to get list of artifacts for Domain details for artifact type: "
|
||||||
+ artifactType.getDisplayName() + " and domain: " + domain, ex);
|
+ artifactType.getDisplayName() + " and domain: " + domain, ex);
|
||||||
@ -83,6 +84,6 @@ class ArtifactsWorker extends SwingWorker<List<BlackboardArtifact>, Void> {
|
|||||||
//Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging
|
//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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -333,7 +333,9 @@ public final class DiscoveryTopComponent extends TopComponent {
|
|||||||
descriptionText += Bundle.DiscoveryTopComponent_additionalFilters_text();
|
descriptionText += Bundle.DiscoveryTopComponent_additionalFilters_text();
|
||||||
}
|
}
|
||||||
selectedDomainTabName = validateLastSelectedType(searchCompleteEvent);
|
selectedDomainTabName = validateLastSelectedType(searchCompleteEvent);
|
||||||
rightSplitPane.setBottomComponent(new DomainDetailsPanel(selectedDomainTabName));
|
DomainDetailsPanel domainDetailsPanel = new DomainDetailsPanel();
|
||||||
|
rightSplitPane.setBottomComponent(domainDetailsPanel);
|
||||||
|
domainDetailsPanel.configureArtifactTabs(selectedDomainTabName);
|
||||||
} else {
|
} else {
|
||||||
rightSplitPane.setBottomComponent(new FileDetailsPanel());
|
rightSplitPane.setBottomComponent(new FileDetailsPanel());
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ final class DomainDetailsPanel extends JPanel {
|
|||||||
private ArtifactsWorker singleArtifactDomainWorker;
|
private ArtifactsWorker singleArtifactDomainWorker;
|
||||||
private MiniTimelineWorker miniTimelineWorker;
|
private MiniTimelineWorker miniTimelineWorker;
|
||||||
private String domain;
|
private String domain;
|
||||||
private String selectedTabName;
|
private String selectedTabName = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new form ArtifactDetailsPanel.
|
* Creates new form ArtifactDetailsPanel.
|
||||||
@ -49,24 +49,23 @@ final class DomainDetailsPanel extends JPanel {
|
|||||||
* @param selectedTabName The name of the tab to select initially.
|
* @param selectedTabName The name of the tab to select initially.
|
||||||
*/
|
*/
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
DomainDetailsPanel(String selectedTabName) {
|
DomainDetailsPanel() {
|
||||||
initComponents();
|
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.
|
* @param tabName The name of the tab to select initially.
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages({"DomainDetailsPanel.miniTimelineTitle.text=Mini Timeline"})
|
@NbBundle.Messages({"DomainDetailsPanel.miniTimelineTitle.text=Mini Timeline"})
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
private void addArtifactTabs(String tabName) {
|
void configureArtifactTabs(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));
|
|
||||||
}
|
|
||||||
selectedTabName = tabName;
|
selectedTabName = tabName;
|
||||||
if (StringUtils.isBlank(selectedTabName)) {
|
if (StringUtils.isBlank(selectedTabName)) {
|
||||||
selectedTabName = Bundle.DomainDetailsPanel_miniTimelineTitle_text();
|
selectedTabName = Bundle.DomainDetailsPanel_miniTimelineTitle_text();
|
||||||
|
@ -52,10 +52,12 @@ class MiniTimelineWorker extends SwingWorker<List<MiniTimelineResult>, Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<MiniTimelineResult> doInBackground() throws Exception {
|
protected List<MiniTimelineResult> doInBackground() throws Exception {
|
||||||
|
List<MiniTimelineResult> results = new ArrayList<>();
|
||||||
if (!StringUtils.isBlank(domain)) {
|
if (!StringUtils.isBlank(domain)) {
|
||||||
DomainSearch domainSearch = new DomainSearch();
|
DomainSearch domainSearch = new DomainSearch();
|
||||||
try {
|
try {
|
||||||
return domainSearch.getAllArtifactsForDomain(Case.getCurrentCase().getSleuthkitCase(), domain);
|
results.addAll(domainSearch.getAllArtifactsForDomain(Case.getCurrentCase().getSleuthkitCase(), domain));
|
||||||
|
|
||||||
} catch (DiscoveryException ex) {
|
} catch (DiscoveryException ex) {
|
||||||
if (ex.getCause() instanceof InterruptedException) {
|
if (ex.getCause() instanceof InterruptedException) {
|
||||||
logger.log(Level.INFO, "MiniTimeline search was cancelled or interrupted for domain: {0}", domain);
|
logger.log(Level.INFO, "MiniTimeline search was cancelled or interrupted for domain: {0}", domain);
|
||||||
@ -64,24 +66,22 @@ class MiniTimelineWorker extends SwingWorker<List<MiniTimelineResult>, Void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new ArrayList<>();
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void done() {
|
protected void done() {
|
||||||
List<MiniTimelineResult> results = null;
|
List<MiniTimelineResult> results = new ArrayList<>();
|
||||||
if (!isCancelled()) {
|
if (!isCancelled()) {
|
||||||
try {
|
try {
|
||||||
results = get();
|
results.addAll(get());
|
||||||
|
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.MiniTimelineResultEvent(results));
|
||||||
} catch (InterruptedException | ExecutionException ex) {
|
} 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);
|
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) {
|
} catch (CancellationException ignored) {
|
||||||
//Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging
|
//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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
initializeDatabaseSchema();
|
initializeDatabaseSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CURRENT_DB_SCHEMA_VERSION.equals(getVersion())) {
|
if (getVersion().compareTo(CURRENT_DB_SCHEMA_VERSION) < 0) {
|
||||||
upgradeDatabaseSchema();
|
upgradeDatabaseSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,10 +183,15 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
if (conn == null) {
|
if (conn == null) {
|
||||||
throw new HealthMonitorException("Error getting database connection");
|
throw new HealthMonitorException("Error getting database connection");
|
||||||
}
|
}
|
||||||
|
ResultSet resultSet = null;
|
||||||
|
|
||||||
try (Statement statement = conn.createStatement()) {
|
try (Statement statement = conn.createStatement()) {
|
||||||
conn.setAutoCommit(false);
|
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
|
// Upgrade from 1.0 to 1.1
|
||||||
// Changes: user_data table added
|
// Changes: user_data table added
|
||||||
if (currentSchema.compareTo(new CaseDbSchemaVersionNumber(1, 1)) < 0) {
|
if (currentSchema.compareTo(new CaseDbSchemaVersionNumber(1, 1)) < 0) {
|
||||||
@ -206,9 +211,14 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
// Changes: username added to user_data table
|
// Changes: username added to user_data table
|
||||||
if (currentSchema.compareTo(new CaseDbSchemaVersionNumber(1, 2)) < 0) {
|
if (currentSchema.compareTo(new CaseDbSchemaVersionNumber(1, 2)) < 0) {
|
||||||
|
|
||||||
|
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
|
// Add the user_data table
|
||||||
statement.execute("ALTER TABLE user_data ADD COLUMN username text");
|
statement.execute("ALTER TABLE user_data ADD COLUMN username text");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update the schema version
|
// Update the schema version
|
||||||
statement.execute("UPDATE db_info SET value='" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "' WHERE name='SCHEMA_VERSION'");
|
statement.execute("UPDATE db_info SET value='" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "' WHERE name='SCHEMA_VERSION'");
|
||||||
@ -224,6 +234,13 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
}
|
}
|
||||||
throw new HealthMonitorException("Error upgrading database", ex);
|
throw new HealthMonitorException("Error upgrading database", ex);
|
||||||
} finally {
|
} finally {
|
||||||
|
if (resultSet != null) {
|
||||||
|
try {
|
||||||
|
resultSet.close();
|
||||||
|
} catch (SQLException ex2) {
|
||||||
|
logger.log(Level.SEVERE, "Error closing result set");
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
conn.close();
|
conn.close();
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
@ -967,7 +984,7 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
getInstance().writeCurrentStateToDatabase();
|
getInstance().writeCurrentStateToDatabase();
|
||||||
}
|
}
|
||||||
} catch (HealthMonitorException ex) {
|
} 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import javafx.application.Platform;
|
|||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
|
import org.joda.time.Interval;
|
||||||
import org.openide.awt.ActionID;
|
import org.openide.awt.ActionID;
|
||||||
import org.openide.awt.ActionReference;
|
import org.openide.awt.ActionReference;
|
||||||
import org.openide.awt.ActionReferences;
|
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
|
* 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)
|
* various specific states (e.g., showing a specific artifact in the List View)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline")
|
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline")
|
||||||
@ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false)
|
@ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false)
|
||||||
@ActionReferences(value = {
|
@ActionReferences(value = {
|
||||||
@ActionReference(path = "Menu/Tools", position = 104)
|
@ActionReference(path = "Menu/Tools", position = 104),
|
||||||
,
|
|
||||||
@ActionReference(path = "Toolbars/Case", position = 104)})
|
@ActionReference(path = "Toolbars/Case", position = 104)})
|
||||||
public final class OpenTimelineAction extends CallableSystemAction {
|
public final class OpenTimelineAction extends CallableSystemAction {
|
||||||
|
|
||||||
@ -64,7 +62,6 @@ public final class OpenTimelineAction extends CallableSystemAction {
|
|||||||
private final JButton toolbarButton = new JButton(getName(),
|
private final JButton toolbarButton = new JButton(getName(),
|
||||||
new ImageIcon(getClass().getResource("images/btn_icon_timeline_colorized_26.png"))); //NON-NLS
|
new ImageIcon(getClass().getResource("images/btn_icon_timeline_colorized_26.png"))); //NON-NLS
|
||||||
|
|
||||||
|
|
||||||
public OpenTimelineAction() {
|
public OpenTimelineAction() {
|
||||||
toolbarButton.addActionListener(actionEvent -> performAction());
|
toolbarButton.addActionListener(actionEvent -> performAction());
|
||||||
menuItem = super.getMenuPresenter();
|
menuItem = super.getMenuPresenter();
|
||||||
@ -74,9 +71,10 @@ public final class OpenTimelineAction extends CallableSystemAction {
|
|||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
/**
|
/**
|
||||||
* We used to also check if Case.getCurrentOpenCase().hasData() was true. We
|
* We used to also check if Case.getCurrentOpenCase().hasData() was
|
||||||
* disabled that check because if it is executed while a data source is
|
* true. We disabled that check because if it is executed while a data
|
||||||
* being added, it blocks the edt. We still do that in ImageGallery.
|
* source is being added, it blocks the edt. We still do that in
|
||||||
|
* ImageGallery.
|
||||||
*/
|
*/
|
||||||
return super.isEnabled() && Case.isCaseOpen() && Installer.isJavaFxInited();
|
return super.isEnabled() && Case.isCaseOpen() && Installer.isJavaFxInited();
|
||||||
}
|
}
|
||||||
@ -103,7 +101,7 @@ public final class OpenTimelineAction extends CallableSystemAction {
|
|||||||
@NbBundle.Messages({
|
@NbBundle.Messages({
|
||||||
"OpenTimelineAction.settingsErrorMessage=Failed to initialize timeline settings.",
|
"OpenTimelineAction.settingsErrorMessage=Failed to initialize timeline settings.",
|
||||||
"OpenTimeLineAction.msgdlg.text=Could not create timeline, there are no data sources."})
|
"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 {
|
try {
|
||||||
Case currentCase = Case.getCurrentCaseThrows();
|
Case currentCase = Case.getCurrentCaseThrows();
|
||||||
if (currentCase.hasData() == false) {
|
if (currentCase.hasData() == false) {
|
||||||
@ -112,6 +110,15 @@ public final class OpenTimelineAction extends CallableSystemAction {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TimeLineController controller = TimeLineModule.getController();
|
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);
|
controller.showTimeLine(file, artifact);
|
||||||
} catch (NoCurrentCaseException e) {
|
} catch (NoCurrentCaseException e) {
|
||||||
//there is no case... Do nothing.
|
//there is no case... Do nothing.
|
||||||
@ -123,7 +130,18 @@ public final class OpenTimelineAction extends CallableSystemAction {
|
|||||||
*/
|
*/
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
public void showTimeline() throws TskCoreException {
|
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)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
public void showFileInTimeline(AbstractFile file) throws TskCoreException {
|
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)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
public void showArtifactInTimeline(BlackboardArtifact artifact) throws TskCoreException {
|
public void showArtifactInTimeline(BlackboardArtifact artifact) throws TskCoreException {
|
||||||
showTimeline(null, artifact);
|
showTimeline(null, artifact, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -62,7 +62,6 @@ import org.joda.time.format.DateTimeFormat;
|
|||||||
import org.joda.time.format.DateTimeFormatter;
|
import org.joda.time.format.DateTimeFormatter;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
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 static org.sleuthkit.autopsy.casemodule.Case.Events.DATA_SOURCE_ADDED;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
|
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
|
||||||
@ -348,7 +347,7 @@ public class TimeLineController {
|
|||||||
/**
|
/**
|
||||||
* Show the entire range of the timeline.
|
* Show the entire range of the timeline.
|
||||||
*/
|
*/
|
||||||
private boolean showFullRange() throws TskCoreException {
|
boolean showFullRange() throws TskCoreException {
|
||||||
synchronized (filteredEvents) {
|
synchronized (filteredEvents) {
|
||||||
return pushTimeRange(filteredEvents.getSpanningInterval());
|
return pushTimeRange(filteredEvents.getSpanningInterval());
|
||||||
}
|
}
|
||||||
@ -376,7 +375,6 @@ public class TimeLineController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shuts down the task executor in charge of handling case events.
|
* Shuts down the task executor in charge of handling case events.
|
||||||
*/
|
*/
|
||||||
@ -395,14 +393,12 @@ public class TimeLineController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the case and ingest listeners, prompt for rebuilding the database if
|
* Add the case and ingest listeners, prompt for rebuilding the database if
|
||||||
* necessary, and show the timeline window.
|
* necessary, and show the timeline window.
|
||||||
*
|
*
|
||||||
* @param file The AbstractFile from which to choose an event to show in
|
* @param file The AbstractFile from which to choose an event to show in the
|
||||||
* the List View.
|
* List View.
|
||||||
* @param artifact The BlackboardArtifact to show in the List View.
|
* @param artifact The BlackboardArtifact to show in the List View.
|
||||||
*/
|
*/
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||||
@ -421,9 +417,7 @@ public class TimeLineController {
|
|||||||
try {
|
try {
|
||||||
if (file == null && artifact == null) {
|
if (file == null && artifact == null) {
|
||||||
SwingUtilities.invokeLater(TimeLineController.this::showWindow);
|
SwingUtilities.invokeLater(TimeLineController.this::showWindow);
|
||||||
this.showFullRange();
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
//prompt user to pick specific event and time range
|
//prompt user to pick specific event and time range
|
||||||
ShowInTimelineDialog showInTimelineDilaog = (file == null)
|
ShowInTimelineDialog showInTimelineDilaog = (file == null)
|
||||||
? new ShowInTimelineDialog(this, artifact)
|
? new ShowInTimelineDialog(this, artifact)
|
||||||
@ -497,7 +491,7 @@ public class TimeLineController {
|
|||||||
topComponent.requestActive();
|
topComponent.requestActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized public TimeLineTopComponent getTopComponent(){
|
synchronized public TimeLineTopComponent getTopComponent() {
|
||||||
return topComponent;
|
return topComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +300,7 @@ public class UserActivitySummaryTest {
|
|||||||
Assert.assertEquals(acceptedDevice, results.get(0).getDeviceModel());
|
Assert.assertEquals(acceptedDevice, results.get(0).getDeviceModel());
|
||||||
Assert.assertEquals("MAKE " + acceptedDevice, results.get(0).getDeviceMake());
|
Assert.assertEquals("MAKE " + acceptedDevice, results.get(0).getDeviceMake());
|
||||||
Assert.assertEquals("ID " + acceptedDevice, results.get(0).getDeviceId());
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -353,7 +353,7 @@ public class UserActivitySummaryTest {
|
|||||||
List<TopDeviceAttachedResult> results = summary.getRecentDevices(dataSource, 10);
|
List<TopDeviceAttachedResult> results = summary.getRecentDevices(dataSource, 10);
|
||||||
|
|
||||||
Assert.assertEquals(1, results.size());
|
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("ID1".equalsIgnoreCase(results.get(0).getDeviceId()));
|
||||||
Assert.assertTrue("MAKE1".equalsIgnoreCase(results.get(0).getDeviceMake()));
|
Assert.assertTrue("MAKE1".equalsIgnoreCase(results.get(0).getDeviceMake()));
|
||||||
Assert.assertTrue("MODEL1".equalsIgnoreCase(results.get(0).getDeviceModel()));
|
Assert.assertTrue("MODEL1".equalsIgnoreCase(results.get(0).getDeviceModel()));
|
||||||
@ -403,9 +403,9 @@ public class UserActivitySummaryTest {
|
|||||||
|
|
||||||
Assert.assertEquals("Expected two different search queries", 2, results.size());
|
Assert.assertEquals("Expected two different search queries", 2, results.size());
|
||||||
Assert.assertTrue(query1.equalsIgnoreCase(results.get(0).getSearchString()));
|
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.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<String> queries, boolean hasProvider, String translationSuffix)
|
private void webSearchTranslationTest(List<String> queries, boolean hasProvider, String translationSuffix)
|
||||||
@ -599,11 +599,11 @@ public class UserActivitySummaryTest {
|
|||||||
Assert.assertEquals(2, domains.size());
|
Assert.assertEquals(2, domains.size());
|
||||||
|
|
||||||
Assert.assertTrue("Expected " + domain1 + " to be first domain", domain1.equalsIgnoreCase(domains.get(0).getDomain()));
|
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.assertEquals((Long) 2L, domains.get(0).getVisitTimes());
|
||||||
|
|
||||||
Assert.assertTrue("Expected " + domain3 + " to be second domain", domain3.equalsIgnoreCase(domains.get(1).getDomain()));
|
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());
|
Assert.assertEquals((Long) 1L, domains.get(1).getVisitTimes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,7 +643,7 @@ public class UserActivitySummaryTest {
|
|||||||
Assert.assertEquals(1, domains.size());
|
Assert.assertEquals(1, domains.size());
|
||||||
|
|
||||||
Assert.assertTrue("Expected " + domain1 + " to be most recent domain", domain1.equalsIgnoreCase(domains.get(0).getDomain()));
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -680,11 +680,11 @@ public class UserActivitySummaryTest {
|
|||||||
Assert.assertEquals(2, domains.size());
|
Assert.assertEquals(2, domains.size());
|
||||||
|
|
||||||
Assert.assertTrue(domain1.equalsIgnoreCase(domains.get(1).getDomain()));
|
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.assertEquals((Long) 2L, domains.get(1).getVisitTimes());
|
||||||
|
|
||||||
Assert.assertTrue(domain2.equalsIgnoreCase(domains.get(0).getDomain()));
|
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());
|
Assert.assertEquals((Long) 3L, domains.get(0).getVisitTimes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -869,7 +869,8 @@ public class UserActivitySummaryTest {
|
|||||||
|
|
||||||
// since this may be somewhat variable
|
// since this may be somewhat variable
|
||||||
Assert.assertTrue(expectedItem.getAccountType().equalsIgnoreCase(receivedItem.getAccountType()));
|
Assert.assertTrue(expectedItem.getAccountType().equalsIgnoreCase(receivedItem.getAccountType()));
|
||||||
Assert.assertEquals(expectedItem.getLastAccess().getTime(), receivedItem.getLastAccess().getTime());
|
Assert.assertEquals(expectedItem.getLastAccessed().getTime(), receivedItem.getLastAccessed().getTime());
|
||||||
|
Assert.assertEquals(expectedItem.getArtifact(), receivedItem.getArtifact());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -896,13 +897,13 @@ public class UserActivitySummaryTest {
|
|||||||
getRecentAccountsOneArtTest(ds1, email1,
|
getRecentAccountsOneArtTest(ds1, email1,
|
||||||
new TopAccountResult(
|
new TopAccountResult(
|
||||||
Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(),
|
Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(),
|
||||||
new Date(DAY_SECONDS * 1000)));
|
new Date(DAY_SECONDS * 1000), email1));
|
||||||
|
|
||||||
BlackboardArtifact email2 = getEmailArtifact(2, ds1, null, DAY_SECONDS);
|
BlackboardArtifact email2 = getEmailArtifact(2, ds1, null, DAY_SECONDS);
|
||||||
getRecentAccountsOneArtTest(ds1, email2,
|
getRecentAccountsOneArtTest(ds1, email2,
|
||||||
new TopAccountResult(
|
new TopAccountResult(
|
||||||
Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(),
|
Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(),
|
||||||
new Date(DAY_SECONDS * 1000)));
|
new Date(DAY_SECONDS * 1000), email2));
|
||||||
|
|
||||||
BlackboardArtifact email3 = getEmailArtifact(3, ds1, null, null);
|
BlackboardArtifact email3 = getEmailArtifact(3, ds1, null, null);
|
||||||
getRecentAccountsOneArtTest(ds1, email3, null);
|
getRecentAccountsOneArtTest(ds1, email3, null);
|
||||||
@ -911,19 +912,19 @@ public class UserActivitySummaryTest {
|
|||||||
getRecentAccountsOneArtTest(ds1, email4,
|
getRecentAccountsOneArtTest(ds1, email4,
|
||||||
new TopAccountResult(
|
new TopAccountResult(
|
||||||
Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(),
|
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);
|
BlackboardArtifact callog1 = getCallogArtifact(11, ds1, DAY_SECONDS, null);
|
||||||
getRecentAccountsOneArtTest(ds1, callog1,
|
getRecentAccountsOneArtTest(ds1, callog1,
|
||||||
new TopAccountResult(
|
new TopAccountResult(
|
||||||
Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(),
|
Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(),
|
||||||
new Date(DAY_SECONDS * 1000)));
|
new Date(DAY_SECONDS * 1000), callog1));
|
||||||
|
|
||||||
BlackboardArtifact callog2 = getCallogArtifact(12, ds1, null, DAY_SECONDS);
|
BlackboardArtifact callog2 = getCallogArtifact(12, ds1, null, DAY_SECONDS);
|
||||||
getRecentAccountsOneArtTest(ds1, callog2,
|
getRecentAccountsOneArtTest(ds1, callog2,
|
||||||
new TopAccountResult(
|
new TopAccountResult(
|
||||||
Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(),
|
Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(),
|
||||||
new Date(DAY_SECONDS * 1000)));
|
new Date(DAY_SECONDS * 1000), callog2));
|
||||||
|
|
||||||
BlackboardArtifact callog3 = getCallogArtifact(13, ds1, null, null);
|
BlackboardArtifact callog3 = getCallogArtifact(13, ds1, null, null);
|
||||||
getRecentAccountsOneArtTest(ds1, callog3, null);
|
getRecentAccountsOneArtTest(ds1, callog3, null);
|
||||||
@ -932,7 +933,7 @@ public class UserActivitySummaryTest {
|
|||||||
getRecentAccountsOneArtTest(ds1, callog4,
|
getRecentAccountsOneArtTest(ds1, callog4,
|
||||||
new TopAccountResult(
|
new TopAccountResult(
|
||||||
Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(),
|
Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(),
|
||||||
new Date(DAY_SECONDS * 2 * 1000)));
|
new Date(DAY_SECONDS * 2 * 1000), callog4));
|
||||||
|
|
||||||
BlackboardArtifact message1 = getMessageArtifact(21, ds1, "Skype", null);
|
BlackboardArtifact message1 = getMessageArtifact(21, ds1, "Skype", null);
|
||||||
getRecentAccountsOneArtTest(ds1, message1, null);
|
getRecentAccountsOneArtTest(ds1, message1, null);
|
||||||
@ -944,7 +945,7 @@ public class UserActivitySummaryTest {
|
|||||||
getRecentAccountsOneArtTest(ds1, message3, null);
|
getRecentAccountsOneArtTest(ds1, message3, null);
|
||||||
|
|
||||||
BlackboardArtifact message4 = getMessageArtifact(24, ds1, "Skype", DAY_SECONDS);
|
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 +978,10 @@ public class UserActivitySummaryTest {
|
|||||||
getRecentAccountsTest(ds1, 10,
|
getRecentAccountsTest(ds1, 10,
|
||||||
Arrays.asList(email1, email2, email3, callog1, callog2, message1a, message1b, message2a, message2b),
|
Arrays.asList(email1, email2, email3, callog1, callog2, message1a, message1b, message2a, message2b),
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
new TopAccountResult("Facebook", new Date((DAY_SECONDS + 42) * 1000)),
|
new TopAccountResult("Facebook", new Date((DAY_SECONDS + 42) * 1000), message2b),
|
||||||
new TopAccountResult("Skype", new Date((DAY_SECONDS + 32) * 1000)),
|
new TopAccountResult("Skype", new Date((DAY_SECONDS + 32) * 1000), message1b),
|
||||||
new TopAccountResult(Bundle.DataSourceUserActivitySummary_getRecentAccounts_calllogMessage(), new Date((DAY_SECONDS + 22) * 1000)),
|
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))
|
new TopAccountResult(Bundle.DataSourceUserActivitySummary_getRecentAccounts_emailMessage(), new Date((DAY_SECONDS + 13) * 1000), email3)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1156,17 +1157,17 @@ public class UserActivitySummaryTest {
|
|||||||
Assert.assertTrue("program1.exe".equalsIgnoreCase(results.get(0).getProgramName()));
|
Assert.assertTrue("program1.exe".equalsIgnoreCase(results.get(0).getProgramName()));
|
||||||
Assert.assertTrue("/Program Files/another/".equalsIgnoreCase(results.get(0).getProgramPath()));
|
Assert.assertTrue("/Program Files/another/".equalsIgnoreCase(results.get(0).getProgramPath()));
|
||||||
Assert.assertEquals((Long) 31L, results.get(0).getRunTimes());
|
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("program1.exe".equalsIgnoreCase(results.get(1).getProgramName()));
|
||||||
Assert.assertTrue("/Program Files/etc/".equalsIgnoreCase(results.get(1).getProgramPath()));
|
Assert.assertTrue("/Program Files/etc/".equalsIgnoreCase(results.get(1).getProgramPath()));
|
||||||
Assert.assertEquals((Long) 21L, results.get(1).getRunTimes());
|
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("program2.exe".equalsIgnoreCase(results.get(2).getProgramName()));
|
||||||
Assert.assertTrue("/Program Files/another/".equalsIgnoreCase(results.get(2).getProgramPath()));
|
Assert.assertTrue("/Program Files/another/".equalsIgnoreCase(results.get(2).getProgramPath()));
|
||||||
Assert.assertEquals((Long) 10L, results.get(2).getRunTimes());
|
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<BlackboardArtifact> artifacts, List<String> programNamesReturned)
|
private void assertProgramOrder(DataSource ds1, List<BlackboardArtifact> artifacts, List<String> programNamesReturned)
|
||||||
|
@ -15,10 +15,7 @@ echo "Testing Autopsy..." && echo -en 'travis_fold:start:script.tests\\r'
|
|||||||
echo "Free Space:"
|
echo "Free Space:"
|
||||||
echo `df -h .`
|
echo `df -h .`
|
||||||
|
|
||||||
if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
||||||
# if os x, just run it
|
|
||||||
# ant -q test-no-regression
|
|
||||||
elif [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
|
||||||
# if linux use xvfb
|
# if linux use xvfb
|
||||||
xvfb-run ant -q test-no-regression
|
xvfb-run ant -q test-no-regression
|
||||||
fi
|
fi
|
||||||
|
Loading…
x
Reference in New Issue
Block a user