From 72c7e856e8145b2233ef8810bcebe6adf4593e19 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 6 Nov 2018 11:55:39 +0100 Subject: [PATCH 1/6] TL file type filter first commit --- .../autopsy/timeline/FilteredEventsModel.java | 52 ++++++++++++++++++- .../autopsy/timeline/TimeLineController.java | 4 +- .../ui/detailview/EventClusterNode.java | 6 +-- .../datamodel/DetailsViewModel.java | 5 +- .../timeline/ui/filtering/FilterSetPanel.java | 2 +- .../timeline/ui/filtering/LegendCell.java | 8 +-- .../filtering/datamodel/RootFilterState.java | 44 +++++++++++----- .../ui/listvew/datamodel/ListViewModel.java | 5 +- 8 files changed, 94 insertions(+), 32 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index 6e419b2f0d..6adebab93d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -20,8 +20,13 @@ package org.sleuthkit.autopsy.timeline; import com.google.common.cache.CacheBuilder; import com.google.common.cache.LoadingCache; +import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Multimap; import com.google.common.eventbus.EventBus; +import com.google.common.net.MediaType; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -42,8 +47,10 @@ import javafx.collections.ObservableList; import javafx.collections.ObservableMap; import javafx.collections.ObservableSet; import javafx.collections.SetChangeListener; +import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTimeZone; import org.joda.time.Interval; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; @@ -87,7 +94,7 @@ import org.sleuthkit.datamodel.timeline.TimelineFilter.RootFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.TagNameFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.TagsFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.TextFilter; -import org.sleuthkit.datamodel.timeline.TimelineFilter.TypeFilter; +import org.sleuthkit.datamodel.timeline.TimelineFilter.EventTypeFilter; /** * This class acts as the model for a TimelineView @@ -129,6 +136,7 @@ public final class FilteredEventsModel { private final LoadingCache> eventCountsCache; private final ObservableMap datasourcesMap = FXCollections.observableHashMap(); private final ObservableSet< String> hashSets = FXCollections.observableSet(); + private final ObservableMap< MediaType, Long> fileTypesMap = FXCollections.observableHashMap(); private final ObservableList tagNames = FXCollections.observableArrayList(); public FilteredEventsModel(Case autoCase, ReadOnlyObjectProperty currentStateProperty) throws TskCoreException { @@ -227,6 +235,10 @@ public final class FilteredEventsModel { return hashSets; } + private ObservableMap getMediaTypes() { + return fileTypesMap; + } + public Interval getBoundingEventsInterval(Interval timeRange, RootFilter filter, DateTimeZone timeZone) throws TskCoreException { return eventManager.getSpanningInterval(timeRange, filter, timeZone); } @@ -266,6 +278,22 @@ public final class FilteredEventsModel { //should this only be tags applied to files or event bearing artifacts? tagNames.setAll(skCase.getTagNamesInUse()); + + + //TODO: limit this to files that have events derived from them. + try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery("SELECT mime_type , COUNT(mime_type) FROM tsk_files GROUP BY mime_type"); + ResultSet results = executeQuery.getResultSet();) { + while (results.next()) { + String mimeType = results.getString("mime_type"); + if (StringUtils.isNotBlank(mimeType)) { + String[] splitMime = mimeType.split("/"); + fileTypesMap.put(MediaType.create(splitMime[0], splitMime[1]), results.getLong("COUNT(mime_type)")); + } + } + } catch (SQLException ex) { + Exceptions.printStackTrace(ex); + } + ; } /** @@ -283,6 +311,7 @@ public final class FilteredEventsModel { for (FilterState filterState : rootFilter.getTagsFilterState().getSubFilterStates()) { filterState.setDisabled(tagNames.contains(filterState.getFilter().getTagName()) == false); } + } /** @@ -361,12 +390,23 @@ public final class FilteredEventsModel { TagNameFilter tagNameFilter = new TagNameFilter(tagName); tagsFilter.addSubFilter(tagNameFilter); }); + + Multimap mimeTypesMap = HashMultimap.create(); + getMediaTypes().forEach((fileType, count) -> mimeTypesMap.put(fileType.type(), fileType.subtype())); + + TimelineFilter.FileTypesFilter fileTypesFilter = new TimelineFilter.FileTypesFilter(); + mimeTypesMap.asMap().forEach((type, subTypes) -> { + TimelineFilter.FileTypeFilter fileTypeFilter = new TimelineFilter.FileTypeFilter(type); + subTypes.forEach(subType -> fileTypeFilter.addSubFilter(new TimelineFilter.FileSubTypeFilter(type, subType))); + fileTypesFilter.addSubFilter(fileTypeFilter); + }); return new RootFilterState(new RootFilter(new HideKnownFilter(), tagsFilter, hashHitsFilter, new TextFilter(), - new TypeFilter(EventType.ROOT_EVENT_TYPE), + new EventTypeFilter(EventType.ROOT_EVENT_TYPE), dataSourcesFilter, + fileTypesFilter, Collections.emptySet())); } @@ -444,6 +484,8 @@ public final class FilteredEventsModel { /** * @return The smallest interval spanning all the events from the * repository, ignoring any filters or requested ranges. + * + * @throws org.sleuthkit.datamodel.TskCoreException */ public Interval getSpanningInterval() throws TskCoreException { return new Interval(getMinTime() * 1000, 1000 + getMaxTime() * 1000); @@ -460,6 +502,8 @@ public final class FilteredEventsModel { * @return the time (in seconds from unix epoch) of the absolutely first * event available from the repository, ignoring any filters or * requested ranges + * + * @throws org.sleuthkit.datamodel.TskCoreException */ public Long getMinTime() throws TskCoreException { try { @@ -473,6 +517,8 @@ public final class FilteredEventsModel { * @return the time (in seconds from unix epoch) of the absolutely last * event available from the repository, ignoring any filters or * requested ranges + * + * @throws org.sleuthkit.datamodel.TskCoreException */ public Long getMaxTime() throws TskCoreException { try { @@ -617,6 +663,8 @@ public final class FilteredEventsModel { /** * (Re)Post an AutopsyEvent received from another event distribution system * locally to all registered subscribers. + * + * @param event The event to re-post. */ public void postAutopsyEventLocally(AutopsyEvent event) { eventbus.post(event); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 18f201df54..3e0b588cf5 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -86,7 +86,7 @@ import org.sleuthkit.datamodel.timeline.EventTypeZoomLevel; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; import org.sleuthkit.autopsy.timeline.zooming.TimeUnits; import org.sleuthkit.datamodel.timeline.TimelineFilter.DescriptionFilter; -import org.sleuthkit.datamodel.timeline.TimelineFilter.TypeFilter; +import org.sleuthkit.datamodel.timeline.TimelineFilter.EventTypeFilter; /** * Controller in the MVC design along with FilteredEventsModel TimeLineView. @@ -607,7 +607,7 @@ public class TimeLineController { @Override protected Collection< Long> call() throws Exception { synchronized (TimeLineController.this) { - return filteredEvents.getEventIDs(timeRange, new TypeFilter(type)); + return filteredEvents.getEventIDs(timeRange, new EventTypeFilter(type)); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java index 3d67a8d7c2..d50a88df50 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventClusterNode.java @@ -58,11 +58,11 @@ import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.SingleDetailsViewE import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; import org.sleuthkit.autopsy.timeline.zooming.ZoomState; import org.sleuthkit.datamodel.DescriptionLoD; +import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.timeline.EventTypeZoomLevel; import org.sleuthkit.datamodel.timeline.TimelineEvent; -import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.timeline.TimelineFilter.DescriptionFilter; -import org.sleuthkit.datamodel.timeline.TimelineFilter.TypeFilter; +import org.sleuthkit.datamodel.timeline.TimelineFilter.EventTypeFilter; /** * A Node to represent an EventCluster in a DetailsChart @@ -179,7 +179,7 @@ final class EventClusterNode extends MultiEventNodeBase= " + start + " AND time < " + end + " AND " + eventManager.getSQLWhere(filterState.getActiveFilter()) // NON-NLS + " GROUP BY interval, " + typeColumn + " , " + descriptionColumn // NON-NLS + " ORDER BY min(time)"; // NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java index 643a42652e..a46d228c8d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java @@ -117,7 +117,7 @@ final public class FilterSetPanel extends BorderPane { //type is the only filter expanded initialy expansionMap.put(controller.getEventsModel().getFilterState().getFilter(), true); - expansionMap.put(controller.getEventsModel().getFilterState().getTypeFilterState().getFilter(), true); + expansionMap.put(controller.getEventsModel().getFilterState().getEventTypeFilterState().getFilter(), true); this.filteredEvents.eventTypeZoomProperty().addListener((Observable observable) -> applyFilters()); this.filteredEvents.descriptionLODProperty().addListener((Observable observable1) -> applyFilters()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/LegendCell.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/LegendCell.java index 99c4e19f3b..414455e243 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/LegendCell.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/LegendCell.java @@ -34,7 +34,7 @@ import org.sleuthkit.autopsy.timeline.ui.EventTypeUtils; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; import org.sleuthkit.datamodel.timeline.EventTypeZoomLevel; import org.sleuthkit.datamodel.timeline.TimelineFilter.TextFilter; -import org.sleuthkit.datamodel.timeline.TimelineFilter.TypeFilter; +import org.sleuthkit.datamodel.timeline.TimelineFilter.EventTypeFilter; /** * A TreeTableCell that shows an icon and color corresponding to the represented @@ -67,8 +67,8 @@ final class LegendCell extends TreeTableCell, FilterState> { } else { //TODO: make some subclasses rather than use this if else chain. - if (item.getFilter() instanceof TypeFilter) { - TypeFilter filter = (TypeFilter) item.getFilter(); + if (item.getFilter() instanceof EventTypeFilter) { + EventTypeFilter filter = (EventTypeFilter) item.getFilter(); Rectangle rect = new Rectangle(20, 20); rect.setArcHeight(5); @@ -104,7 +104,7 @@ final class LegendCell extends TreeTableCell, FilterState> { } } - private void setLegendColor(TypeFilter filter, Rectangle rect, EventTypeZoomLevel eventTypeZoom) { + private void setLegendColor(EventTypeFilter filter, Rectangle rect, EventTypeZoomLevel eventTypeZoom) { //only show legend color if filter is of the same zoomlevel as requested in filteredEvents if (eventTypeZoom.equals(filter.getEventType().getZoomLevel())) { Platform.runLater(() -> { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java index 71e1025045..024b80776a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java @@ -26,23 +26,26 @@ import org.python.google.common.collect.Lists; import org.sleuthkit.datamodel.timeline.TimelineFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.DataSourceFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.DataSourcesFilter; +import org.sleuthkit.datamodel.timeline.TimelineFilter.EventTypeFilter; +import org.sleuthkit.datamodel.timeline.TimelineFilter.FileTypeFilter; +import org.sleuthkit.datamodel.timeline.TimelineFilter.FileTypesFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.HashHitsFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.HashSetFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.HideKnownFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.RootFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.TextFilter; -import org.sleuthkit.datamodel.timeline.TimelineFilter.TypeFilter; /** */ public class RootFilterState implements FilterState, CompoundFilterState< TimelineFilter, RootFilter> { - private final CompoundFilterState typeFilterState; + private final CompoundFilterState eventTypeFilterState; private final DefaultFilterState knownFilterState; private final DefaultFilterState textFilterState; private final TagsFilterState tagsFilterState; private final CompoundFilterState hashHitsFilterState; private final CompoundFilterState dataSourcesFilterState; + private final CompoundFilterState fileTypesFilterState; private static final ReadOnlyBooleanProperty ALWAYS_TRUE = new ReadOnlyBooleanWrapper(true).getReadOnlyProperty(); private final static ReadOnlyBooleanProperty ALWAYS_FALSE = new ReadOnlyBooleanWrapper(false).getReadOnlyProperty(); @@ -52,34 +55,40 @@ public class RootFilterState implements FilterState, CompoundFilterS public RootFilterState(RootFilter delegate) { this(delegate, - new CompoundFilterStateImpl<>(delegate.getTypeFilter()), + new CompoundFilterStateImpl<>(delegate.getEventTypeFilter()), new DefaultFilterState<>(delegate.getKnownFilter()), new DefaultFilterState<>(delegate.getTextFilter()), new TagsFilterState(delegate.getTagsFilter()), new CompoundFilterStateImpl<>(delegate.getHashHitsFilter()), - new CompoundFilterStateImpl<>(delegate.getDataSourcesFilter()) + new CompoundFilterStateImpl<>(delegate.getDataSourcesFilter()), + new CompoundFilterStateImpl<>(delegate.getFileTypesFilter()) ); } private RootFilterState(RootFilter delegate, - CompoundFilterState typeFilterState, + CompoundFilterState eventTypeFilterState, DefaultFilterState knownFilterState, DefaultFilterState textFilterState, TagsFilterState tagsFilterState, CompoundFilterState hashHitsFilterState, - CompoundFilterState dataSourcesFilterState) { + CompoundFilterState dataSourcesFilterState, + CompoundFilterState fileTypesFilterState) { this.delegate = delegate; - this.typeFilterState = typeFilterState; + this.eventTypeFilterState = eventTypeFilterState; this.knownFilterState = knownFilterState; this.textFilterState = textFilterState; this.tagsFilterState = tagsFilterState; this.hashHitsFilterState = hashHitsFilterState; this.dataSourcesFilterState = dataSourcesFilterState; + this.fileTypesFilterState = fileTypesFilterState; subFilterStates.addAll( - knownFilterState, textFilterState, + knownFilterState, + textFilterState, tagsFilterState, hashHitsFilterState, - dataSourcesFilterState, typeFilterState); + dataSourcesFilterState, + fileTypesFilterState, + eventTypeFilterState); } /** @@ -112,16 +121,18 @@ public class RootFilterState implements FilterState, CompoundFilterS @Override public RootFilterState copyOf() { return new RootFilterState(getFilter().copyOf(), - getTypeFilterState().copyOf(), + getEventTypeFilterState().copyOf(), getKnownFilterState().copyOf(), getTextFilterState().copyOf(), getTagsFilterState().copyOf(), getHashHitsFilterState().copyOf(), - getDataSourcesFilterState().copyOf()); + getDataSourcesFilterState().copyOf(), + getFileTypesFilterState().copyOf() + ); } - public CompoundFilterState getTypeFilterState() { - return typeFilterState; + public CompoundFilterState getEventTypeFilterState() { + return eventTypeFilterState; } public DefaultFilterState getKnownFilterState() { @@ -144,14 +155,19 @@ public class RootFilterState implements FilterState, CompoundFilterS return dataSourcesFilterState; } + public CompoundFilterState getFileTypesFilterState() { + return fileTypesFilterState; + } + @Override public RootFilter getActiveFilter() { return new RootFilter(knownFilterState.getActiveFilter(), tagsFilterState.getActiveFilter(), hashHitsFilterState.getActiveFilter(), textFilterState.getActiveFilter(), - typeFilterState.getActiveFilter(), + eventTypeFilterState.getActiveFilter(), dataSourcesFilterState.getActiveFilter(), + fileTypesFilterState.getActiveFilter(), Lists.transform(subFilterStates, FilterState::getActiveFilter)); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/datamodel/ListViewModel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/datamodel/ListViewModel.java index 203db0994c..756e701e3f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/datamodel/ListViewModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/datamodel/ListViewModel.java @@ -85,13 +85,12 @@ public class ListViewModel { } ArrayList combinedEvents = new ArrayList<>(); - final boolean needsTags = filterState.hasActiveTagsFilters(); - final boolean needsHashSets = filterState.hasActiveHashFilters(); + TimelineDBUtils dbUtils = new TimelineDBUtils(sleuthkitCase); final String querySql = "SELECT full_description, time, file_obj_id, " + dbUtils.csvAggFunction("CAST(tsk_events.event_id AS VARCHAR)") + " AS eventIDs, " + dbUtils.csvAggFunction("CAST(sub_type AS VARCHAR)") + " AS eventTypes" - + " FROM " + TimelineManager.getAugmentedEventsTablesSQL(needsTags, needsHashSets) + + " FROM " + TimelineManager.getAugmentedEventsTablesSQL(filterState.getActiveFilter()) + " WHERE time >= " + startTime + " AND time <" + endTime + " AND " + eventManager.getSQLWhere(filterState.getActiveFilter()) + " GROUP BY time, full_description, file_obj_id ORDER BY time ASC, full_description"; From 1aac00b9475046f003db97ba8b9531617a61020e Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 6 Nov 2018 15:57:36 +0100 Subject: [PATCH 2/6] comments --- .../autopsy/timeline/ui/filtering/datamodel/FilterState.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/FilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/FilterState.java index f928849d8e..72bba54ef5 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/FilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/FilterState.java @@ -23,9 +23,9 @@ import javafx.beans.property.ReadOnlyBooleanProperty; import org.sleuthkit.datamodel.timeline.TimelineFilter; /** - * + * The state of a filter: selected, disabled, active, etc. * - * @param + * @param The type of filter this is the state for. */ public interface FilterState { From 7f31d8d5711275a017294a0a20770dce36e72c1d Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 7 Nov 2018 15:19:14 +0100 Subject: [PATCH 3/6] fix codacy issues --- .../org/sleuthkit/autopsy/timeline/FilteredEventsModel.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index 6adebab93d..7145ef8fe4 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -48,6 +48,7 @@ import javafx.collections.ObservableMap; import javafx.collections.ObservableSet; import javafx.collections.SetChangeListener; import org.apache.commons.lang3.StringUtils; +import static org.apache.commons.lang3.StringUtils.isBlank; import org.joda.time.DateTimeZone; import org.joda.time.Interval; import org.openide.util.Exceptions; @@ -279,7 +280,6 @@ public final class FilteredEventsModel { //should this only be tags applied to files or event bearing artifacts? tagNames.setAll(skCase.getTagNamesInUse()); - //TODO: limit this to files that have events derived from them. try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery("SELECT mime_type , COUNT(mime_type) FROM tsk_files GROUP BY mime_type"); ResultSet results = executeQuery.getResultSet();) { @@ -293,7 +293,6 @@ public final class FilteredEventsModel { } catch (SQLException ex) { Exceptions.printStackTrace(ex); } - ; } /** @@ -745,7 +744,7 @@ public final class FilteredEventsModel { */ public static List unGroupConcat(String groupConcat, CheckedFunction mapper) throws TskCoreException { - if (org.apache.commons.lang3.StringUtils.isBlank(groupConcat)) { + if (isBlank(groupConcat)) { return Collections.emptyList(); } From 3f4b04aa2d052f4c007ba065c827216953c1da3b Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 7 Nov 2018 17:27:55 +0100 Subject: [PATCH 4/6] rework file type filtering to use predefined groups of mime types --- .../autopsy/timeline/FilteredEventsModel.java | 82 +++++++++++++------ 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index 7145ef8fe4..e694af777e 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -37,6 +37,8 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javafx.beans.Observable; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectWrapper; @@ -53,6 +55,7 @@ import org.joda.time.DateTimeZone; import org.joda.time.Interval; import org.openide.util.Exceptions; import org.openide.util.NbBundle; +import org.python.google.common.collect.ImmutableSet; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; @@ -118,6 +121,49 @@ public final class FilteredEventsModel { private static final Logger logger = Logger.getLogger(FilteredEventsModel.class.getName()); + private static final Set MEDIA_MIME_TYPES = Stream.of( + "image/*", + "video/*", + "audio/*", + "application/vnd.ms-asf", //NON-NLS + "application/vnd.rn-realmedia", //NON-NLS + "application/x-shockwave-flash" //NON-NLS + ).map(MediaType::parse).collect(Collectors.toSet()); + + private static final Set EXECUTABLE_MIME_TYPES = Stream.of( + "application/x-bat", + "application/x-dosexec", + "application/vnd.microsoft.portable-executable", + "application/x-msdownload", + "application/exe", + "application/x-exe", + "application/dos-exe", + "vms/exe", + "application/x-winexe", + "application/msdos-windows", + "application/x-msdos-program" + ).map(MediaType::parse).collect(Collectors.toSet()); + + private static final Set DOCUMENT_MIME_TYPES = Stream.of( + "text/*", //NON-NLS + "application/rtf", //NON-NLS + "application/pdf", //NON-NLS + "application/json", //NON-NLS + "application/javascript", //NON-NLS + "application/xml", //NON-NLS + "application/x-msoffice", //NON-NLS + "application/x-ooxml", //NON-NLS + "application/msword", //NON-NLS + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", //NON-NLS + "application/vnd.ms-powerpoint", //NON-NLS + "application/vnd.openxmlformats-officedocument.presentationml.presentation", //NON-NLS + "application/vnd.ms-excel", //NON-NLS + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS + "application/vnd.oasis.opendocument.presentation", //NON-NLS + "application/vnd.oasis.opendocument.spreadsheet", //NON-NLS + "application/vnd.oasis.opendocument.text" //NON-NLS + ).map(MediaType::parse).collect(Collectors.toSet()); + private final TimelineManager eventManager; private final Case autoCase; @@ -137,7 +183,6 @@ public final class FilteredEventsModel { private final LoadingCache> eventCountsCache; private final ObservableMap datasourcesMap = FXCollections.observableHashMap(); private final ObservableSet< String> hashSets = FXCollections.observableSet(); - private final ObservableMap< MediaType, Long> fileTypesMap = FXCollections.observableHashMap(); private final ObservableList tagNames = FXCollections.observableArrayList(); public FilteredEventsModel(Case autoCase, ReadOnlyObjectProperty currentStateProperty) throws TskCoreException { @@ -236,10 +281,6 @@ public final class FilteredEventsModel { return hashSets; } - private ObservableMap getMediaTypes() { - return fileTypesMap; - } - public Interval getBoundingEventsInterval(Interval timeRange, RootFilter filter, DateTimeZone timeZone) throws TskCoreException { return eventManager.getSpanningInterval(timeRange, filter, timeZone); } @@ -279,20 +320,6 @@ public final class FilteredEventsModel { //should this only be tags applied to files or event bearing artifacts? tagNames.setAll(skCase.getTagNamesInUse()); - - //TODO: limit this to files that have events derived from them. - try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery("SELECT mime_type , COUNT(mime_type) FROM tsk_files GROUP BY mime_type"); - ResultSet results = executeQuery.getResultSet();) { - while (results.next()) { - String mimeType = results.getString("mime_type"); - if (StringUtils.isNotBlank(mimeType)) { - String[] splitMime = mimeType.split("/"); - fileTypesMap.put(MediaType.create(splitMime[0], splitMime[1]), results.getLong("COUNT(mime_type)")); - } - } - } catch (SQLException ex) { - Exceptions.printStackTrace(ex); - } } /** @@ -390,15 +417,12 @@ public final class FilteredEventsModel { tagsFilter.addSubFilter(tagNameFilter); }); - Multimap mimeTypesMap = HashMultimap.create(); - getMediaTypes().forEach((fileType, count) -> mimeTypesMap.put(fileType.type(), fileType.subtype())); - TimelineFilter.FileTypesFilter fileTypesFilter = new TimelineFilter.FileTypesFilter(); - mimeTypesMap.asMap().forEach((type, subTypes) -> { - TimelineFilter.FileTypeFilter fileTypeFilter = new TimelineFilter.FileTypeFilter(type); - subTypes.forEach(subType -> fileTypeFilter.addSubFilter(new TimelineFilter.FileSubTypeFilter(type, subType))); - fileTypesFilter.addSubFilter(fileTypeFilter); - }); + + fileTypesFilter.addSubFilter(new TimelineFilter.FileTypeFilter("Media", MEDIA_MIME_TYPES)); + fileTypesFilter.addSubFilter(new TimelineFilter.FileTypeFilter("Documents", DOCUMENT_MIME_TYPES)); + fileTypesFilter.addSubFilter(new TimelineFilter.FileTypeFilter("Executables", EXECUTABLE_MIME_TYPES)); + return new RootFilterState(new RootFilter(new HideKnownFilter(), tagsFilter, hashHitsFilter, @@ -409,6 +433,10 @@ public final class FilteredEventsModel { Collections.emptySet())); } + enum FileType { + DOCUMENT, EXECUTABLE, MULTIMEDIA, OTHER; + } + public Interval getBoundingEventsInterval(DateTimeZone timeZone) throws TskCoreException { return eventManager.getSpanningInterval(zoomStateProperty().get().getTimeRange(), getFilterState().getActiveFilter(), timeZone); } From e1b593c0993722a9405b98df356dc9ab5d7d957c Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 8 Nov 2018 15:03:15 +0100 Subject: [PATCH 5/6] move mime-type lists into FilterUtils and implement 'Other' FileTypeFilter --- .../autopsy/timeline/FilteredEventsModel.java | 150 +++++------------- .../autopsy/timeline/utils/FilterUtils.java | 117 ++++++++++++++ 2 files changed, 161 insertions(+), 106 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index e694af777e..795e804c27 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -20,13 +20,8 @@ package org.sleuthkit.autopsy.timeline; import com.google.common.cache.CacheBuilder; import com.google.common.cache.LoadingCache; -import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Multimap; import com.google.common.eventbus.EventBus; -import com.google.common.net.MediaType; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -37,8 +32,6 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javafx.beans.Observable; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectWrapper; @@ -49,13 +42,10 @@ import javafx.collections.ObservableList; import javafx.collections.ObservableMap; import javafx.collections.ObservableSet; import javafx.collections.SetChangeListener; -import org.apache.commons.lang3.StringUtils; import static org.apache.commons.lang3.StringUtils.isBlank; import org.joda.time.DateTimeZone; import org.joda.time.Interval; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; -import org.python.google.common.collect.ImmutableSet; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; @@ -73,6 +63,7 @@ import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; import org.sleuthkit.autopsy.timeline.utils.CacheLoaderImpl; import org.sleuthkit.autopsy.timeline.utils.CheckedFunction; +import org.sleuthkit.autopsy.timeline.utils.FilterUtils; import org.sleuthkit.autopsy.timeline.zooming.ZoomState; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -91,6 +82,8 @@ import org.sleuthkit.datamodel.timeline.TimelineEvent; import org.sleuthkit.datamodel.timeline.TimelineFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.DataSourceFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.DataSourcesFilter; +import org.sleuthkit.datamodel.timeline.TimelineFilter.EventTypeFilter; +import org.sleuthkit.datamodel.timeline.TimelineFilter.FileTypesFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.HashHitsFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.HashSetFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.HideKnownFilter; @@ -98,7 +91,6 @@ import org.sleuthkit.datamodel.timeline.TimelineFilter.RootFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.TagNameFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.TagsFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.TextFilter; -import org.sleuthkit.datamodel.timeline.TimelineFilter.EventTypeFilter; /** * This class acts as the model for a TimelineView @@ -112,58 +104,15 @@ import org.sleuthkit.datamodel.timeline.TimelineFilter.EventTypeFilter; * as to avoid unnecessary db calls through the TimelineManager -jm * * Concurrency Policy: TimelineManager is internally synchronized, so methods - * that only access the repo atomically do not need further synchronization. All - * other member state variables should only be accessed with intrinsic lock of - * containing FilteredEventsModel held. + * that only access the TimelineManager atomically do not need further + * synchronization. All other member state variables should only be accessed + * with intrinsic lock of containing FilteredEventsModel held. * */ public final class FilteredEventsModel { private static final Logger logger = Logger.getLogger(FilteredEventsModel.class.getName()); - private static final Set MEDIA_MIME_TYPES = Stream.of( - "image/*", - "video/*", - "audio/*", - "application/vnd.ms-asf", //NON-NLS - "application/vnd.rn-realmedia", //NON-NLS - "application/x-shockwave-flash" //NON-NLS - ).map(MediaType::parse).collect(Collectors.toSet()); - - private static final Set EXECUTABLE_MIME_TYPES = Stream.of( - "application/x-bat", - "application/x-dosexec", - "application/vnd.microsoft.portable-executable", - "application/x-msdownload", - "application/exe", - "application/x-exe", - "application/dos-exe", - "vms/exe", - "application/x-winexe", - "application/msdos-windows", - "application/x-msdos-program" - ).map(MediaType::parse).collect(Collectors.toSet()); - - private static final Set DOCUMENT_MIME_TYPES = Stream.of( - "text/*", //NON-NLS - "application/rtf", //NON-NLS - "application/pdf", //NON-NLS - "application/json", //NON-NLS - "application/javascript", //NON-NLS - "application/xml", //NON-NLS - "application/x-msoffice", //NON-NLS - "application/x-ooxml", //NON-NLS - "application/msword", //NON-NLS - "application/vnd.openxmlformats-officedocument.wordprocessingml.document", //NON-NLS - "application/vnd.ms-powerpoint", //NON-NLS - "application/vnd.openxmlformats-officedocument.presentationml.presentation", //NON-NLS - "application/vnd.ms-excel", //NON-NLS - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS - "application/vnd.oasis.opendocument.presentation", //NON-NLS - "application/vnd.oasis.opendocument.spreadsheet", //NON-NLS - "application/vnd.oasis.opendocument.text" //NON-NLS - ).map(MediaType::parse).collect(Collectors.toSet()); - private final TimelineManager eventManager; private final Case autoCase; @@ -175,15 +124,18 @@ public final class FilteredEventsModel { private final ReadOnlyObjectWrapper requestedZoomState = new ReadOnlyObjectWrapper<>(); private final ReadOnlyObjectWrapper< EventTypeZoomLevel> requestedTypeZoom = new ReadOnlyObjectWrapper<>(EventTypeZoomLevel.BASE_TYPE); private final ReadOnlyObjectWrapper< DescriptionLoD> requestedLOD = new ReadOnlyObjectWrapper<>(DescriptionLoD.SHORT); + // end Filter and zoome state - //caches + //caches private final LoadingCache maxCache; private final LoadingCache minCache; private final LoadingCache idToEventCache; private final LoadingCache> eventCountsCache; + /** Map from datasource id to datasource name. */ private final ObservableMap datasourcesMap = FXCollections.observableHashMap(); private final ObservableSet< String> hashSets = FXCollections.observableSet(); private final ObservableList tagNames = FXCollections.observableArrayList(); + // end caches public FilteredEventsModel(Case autoCase, ReadOnlyObjectProperty currentStateProperty) throws TskCoreException { this.autoCase = autoCase; @@ -205,19 +157,19 @@ public final class FilteredEventsModel { minCache = CacheBuilder.newBuilder() .build(new CacheLoaderImpl<>(ignored -> eventManager.getMinTime())); - getDatasourcesMap().addListener((MapChangeListener.Change change) -> { + datasourcesMap.addListener((MapChangeListener.Change change) -> { DataSourceFilter dataSourceFilter = new DataSourceFilter(change.getValueAdded(), change.getKey()); RootFilterState rootFilter = filterProperty().get(); rootFilter.getDataSourcesFilterState().getFilter().getSubFilters().add(dataSourceFilter); requestedFilter.set(rootFilter.copyOf()); }); - getHashSets().addListener((SetChangeListener.Change< ? extends String> change) -> { + hashSets.addListener((SetChangeListener.Change< ? extends String> change) -> { HashSetFilter hashSetFilter = new HashSetFilter(change.getElementAdded()); RootFilterState rootFilter = filterProperty().get(); rootFilter.getHashHitsFilterState().getFilter().getSubFilters().add(hashSetFilter); requestedFilter.set(rootFilter.copyOf()); }); - getTagNames().addListener((ListChangeListener.Change change) -> { + tagNames.addListener((ListChangeListener.Change change) -> { RootFilterState rootFilter = filterProperty().get(); syncTagsFilter(rootFilter); requestedFilter.set(rootFilter.copyOf()); @@ -269,18 +221,6 @@ public final class FilteredEventsModel { return autoCase.getSleuthkitCase(); } - public ObservableList getTagNames() { - return tagNames; - } - - synchronized public ObservableMap getDatasourcesMap() { - return datasourcesMap; - } - - synchronized public ObservableSet< String> getHashSets() { - return hashSets; - } - public Interval getBoundingEventsInterval(Interval timeRange, RootFilter filter, DateTimeZone timeZone) throws TskCoreException { return eventManager.getSpanningInterval(timeRange, filter, timeZone); } @@ -327,14 +267,14 @@ public final class FilteredEventsModel { * for tags that are not in use in the case, and add new filters for tags * that don't have them. New filters are selected by default. * - * @param rootFilter the filter state to modify so it is consistent with the - * tags in use in the case + * @param rootFilterState the filter state to modify so it is consistent + * with the tags in use in the case */ - public void syncTagsFilter(RootFilterState rootFilter) { - for (TagName tagName : tagNames) { - rootFilter.getTagsFilterState().getFilter().addSubFilter(new TagNameFilter(tagName)); - } - for (FilterState filterState : rootFilter.getTagsFilterState().getSubFilterStates()) { + public void syncTagsFilter(RootFilterState rootFilterState) { + tagNames.forEach((tagName) -> { + rootFilterState.getTagsFilterState().getFilter().addSubFilter(new TagNameFilter(tagName)); + }); + for (FilterState filterState : rootFilterState.getTagsFilterState().getSubFilterStates()) { filterState.setDisabled(tagNames.contains(filterState.getFilter().getTagName()) == false); } @@ -397,31 +337,19 @@ public final class FilteredEventsModel { /** * @return the default filter used at startup */ - public RootFilterState getDefaultFilter() { + public synchronized RootFilterState getDefaultFilter() { DataSourcesFilter dataSourcesFilter = new DataSourcesFilter(); - - getDatasourcesMap().entrySet().stream().forEach((Map.Entry entry) -> { - DataSourceFilter dataSourceFilter = new DataSourceFilter(entry.getValue(), entry.getKey()); - dataSourcesFilter.addSubFilter(dataSourceFilter); - }); + datasourcesMap.entrySet().forEach(dataSourceEntry + -> dataSourcesFilter.addSubFilter(new DataSourceFilter(dataSourceEntry.getValue(), dataSourceEntry.getKey())) + ); HashHitsFilter hashHitsFilter = new HashHitsFilter(); - getHashSets().forEach(hashSetName -> { - HashSetFilter hashSetFilter = new HashSetFilter(hashSetName); - hashHitsFilter.addSubFilter(hashSetFilter); - }); + hashSets.stream().map(HashSetFilter::new).forEach(hashHitsFilter::addSubFilter); TagsFilter tagsFilter = new TagsFilter(); - getTagNames().stream().forEach(tagName -> { - TagNameFilter tagNameFilter = new TagNameFilter(tagName); - tagsFilter.addSubFilter(tagNameFilter); - }); + tagNames.stream().map(TagNameFilter::new).forEach(tagsFilter::addSubFilter); - TimelineFilter.FileTypesFilter fileTypesFilter = new TimelineFilter.FileTypesFilter(); - - fileTypesFilter.addSubFilter(new TimelineFilter.FileTypeFilter("Media", MEDIA_MIME_TYPES)); - fileTypesFilter.addSubFilter(new TimelineFilter.FileTypeFilter("Documents", DOCUMENT_MIME_TYPES)); - fileTypesFilter.addSubFilter(new TimelineFilter.FileTypeFilter("Executables", EXECUTABLE_MIME_TYPES)); + FileTypesFilter fileTypesFilter = FilterUtils.createDefaultFileTypesFilter(); return new RootFilterState(new RootFilter(new HideKnownFilter(), tagsFilter, @@ -433,10 +361,6 @@ public final class FilteredEventsModel { Collections.emptySet())); } - enum FileType { - DOCUMENT, EXECUTABLE, MULTIMEDIA, OTHER; - } - public Interval getBoundingEventsInterval(DateTimeZone timeZone) throws TskCoreException { return eventManager.getSpanningInterval(zoomStateProperty().get().getTimeRange(), getFilterState().getActiveFilter(), timeZone); } @@ -464,6 +388,8 @@ public final class FilteredEventsModel { * @param eventIDsWithTags the event ids to get the tag counts map for * * @return a map from tagname displayname to count of applications + * + * @throws org.sleuthkit.datamodel.TskCoreException */ public Map getTagCountsByTagName(Set eventIDsWithTags) throws TskCoreException { return eventManager.getTagCountsByTagName(eventIDsWithTags); @@ -482,7 +408,7 @@ public final class FilteredEventsModel { } /** - * return the number of events that pass the requested filter and are within + * Return the number of events that pass the requested filter and are within * the given time range. * * NOTE: this method does not change the requested time range @@ -509,8 +435,8 @@ public final class FilteredEventsModel { } /** - * @return The smallest interval spanning all the events from the - * repository, ignoring any filters or requested ranges. + * @return The smallest interval spanning all the events from the case, + * ignoring any filters or requested ranges. * * @throws org.sleuthkit.datamodel.TskCoreException */ @@ -519,7 +445,13 @@ public final class FilteredEventsModel { } /** + * Get the smallest interval spanning all the given events. + * + * @param eventIDs The IDs of the events to get a spanning interval arround. + * * @return the smallest interval spanning all the given events + * + * @throws org.sleuthkit.datamodel.TskCoreException */ public Interval getSpanningInterval(Collection eventIDs) throws TskCoreException { return eventManager.getSpanningInterval(eventIDs); @@ -609,6 +541,8 @@ public final class FilteredEventsModel { * * @return A List of event IDs for the events that are derived from the * given file. + * + * @throws org.sleuthkit.datamodel.TskCoreException */ public List getEventIDsForFile(AbstractFile file, boolean includeDerivedArtifacts) throws TskCoreException { return eventManager.getEventIDsForFile(file, includeDerivedArtifacts); @@ -622,6 +556,8 @@ public final class FilteredEventsModel { * * @return A List of event IDs for the events that are derived from the * given artifact. + * + * @throws org.sleuthkit.datamodel.TskCoreException */ public List getEventIDsForArtifact(BlackboardArtifact artifact) throws TskCoreException { return eventManager.getEventIDsForArtifact(artifact); @@ -769,6 +705,8 @@ public final class FilteredEventsModel { * * @return a Set of X, each element mapped from one element of the original * comma delimited string + * + * @throws org.sleuthkit.datamodel.TskCoreException */ public static List unGroupConcat(String groupConcat, CheckedFunction mapper) throws TskCoreException { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java b/Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java new file mode 100644 index 0000000000..8375e625a3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java @@ -0,0 +1,117 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.timeline.utils; + +import com.google.common.net.MediaType; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.sleuthkit.datamodel.TimelineManager; +import org.sleuthkit.datamodel.timeline.TimelineFilter.FileTypeFilter; +import org.sleuthkit.datamodel.timeline.TimelineFilter.FileTypesFilter; + +/** + * Utilities to deal with TimelineFilters + */ +public final class FilterUtils { + + private static final Set MEDIA_MIME_TYPES = Stream.of( + "image/*", + "video/*", + "audio/*", + "application/vnd.ms-asf", //NON-NLS + "application/vnd.rn-realmedia", //NON-NLS + "application/x-shockwave-flash" //NON-NLS + ).map(MediaType::parse).collect(Collectors.toSet()); + + private static final Set EXECUTABLE_MIME_TYPES = Stream.of( + "application/x-bat", + "application/x-dosexec", + "application/vnd.microsoft.portable-executable", + "application/x-msdownload", + "application/exe", + "application/x-exe", + "application/dos-exe", + "vms/exe", + "application/x-winexe", + "application/msdos-windows", + "application/x-msdos-program" + ).map(MediaType::parse).collect(Collectors.toSet()); + + private static final Set DOCUMENT_MIME_TYPES = Stream.of( + "text/*", //NON-NLS + "application/rtf", //NON-NLS + "application/pdf", //NON-NLS + "application/json", //NON-NLS + "application/javascript", //NON-NLS + "application/xml", //NON-NLS + "application/x-msoffice", //NON-NLS + "application/x-ooxml", //NON-NLS + "application/msword", //NON-NLS + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", //NON-NLS + "application/vnd.ms-powerpoint", //NON-NLS + "application/vnd.openxmlformats-officedocument.presentationml.presentation", //NON-NLS + "application/vnd.ms-excel", //NON-NLS + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS + "application/vnd.oasis.opendocument.presentation", //NON-NLS + "application/vnd.oasis.opendocument.spreadsheet", //NON-NLS + "application/vnd.oasis.opendocument.text" //NON-NLS + ).map(MediaType::parse).collect(Collectors.toSet()); + + private static final Set NON_OTHER_MIME_TYPES = new HashSet<>(); + + static { + NON_OTHER_MIME_TYPES.addAll(MEDIA_MIME_TYPES); + NON_OTHER_MIME_TYPES.addAll(DOCUMENT_MIME_TYPES); + NON_OTHER_MIME_TYPES.addAll(EXECUTABLE_MIME_TYPES); + } + + private FilterUtils() { + } + + public static FileTypesFilter createDefaultFileTypesFilter() { + FileTypesFilter fileTypesFilter = new FileTypesFilter(); + + fileTypesFilter.addSubFilter(new FileTypeFilter("Media", MEDIA_MIME_TYPES)); + fileTypesFilter.addSubFilter(new FileTypeFilter("Documents", DOCUMENT_MIME_TYPES)); + fileTypesFilter.addSubFilter(new FileTypeFilter("Executables", EXECUTABLE_MIME_TYPES)); + fileTypesFilter.addSubFilter(new InverseFileTypeFilter("Other", NON_OTHER_MIME_TYPES)); + + return fileTypesFilter; + } + + private static class InverseFileTypeFilter extends FileTypeFilter { + + InverseFileTypeFilter(String displayName, Collection mediaTypes) { + super(displayName, mediaTypes); + } + + @Override + public InverseFileTypeFilter copyOf() { + return new InverseFileTypeFilter("Other", NON_OTHER_MIME_TYPES); + } + + @Override + protected String getSQLWhere(TimelineManager manager) { + return " NOT " + super.getSQLWhere(manager) ; + } + } +} From 6be0b109dbb9e1040aa6c2eac73471db31fe1e7b Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 8 Nov 2018 15:08:43 +0100 Subject: [PATCH 6/6] cleanup, comments --- .../autopsy/timeline/license-timeline.txt | 18 ------- .../autopsy/timeline/utils/FilterUtils.java | 54 ++++++++++++------- 2 files changed, 35 insertions(+), 37 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/license-timeline.txt diff --git a/Core/src/org/sleuthkit/autopsy/timeline/license-timeline.txt b/Core/src/org/sleuthkit/autopsy/timeline/license-timeline.txt deleted file mode 100644 index df5d98b91a..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/license-timeline.txt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java b/Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java index 8375e625a3..0e6ff39d35 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.openide.util.NbBundle; import org.sleuthkit.datamodel.TimelineManager; import org.sleuthkit.datamodel.timeline.TimelineFilter.FileTypeFilter; import org.sleuthkit.datamodel.timeline.TimelineFilter.FileTypesFilter; @@ -34,26 +35,26 @@ import org.sleuthkit.datamodel.timeline.TimelineFilter.FileTypesFilter; public final class FilterUtils { private static final Set MEDIA_MIME_TYPES = Stream.of( - "image/*", - "video/*", - "audio/*", + "image/*",//NON-NLS + "video/*",//NON-NLS + "audio/*",//NON-NLS "application/vnd.ms-asf", //NON-NLS "application/vnd.rn-realmedia", //NON-NLS "application/x-shockwave-flash" //NON-NLS ).map(MediaType::parse).collect(Collectors.toSet()); private static final Set EXECUTABLE_MIME_TYPES = Stream.of( - "application/x-bat", - "application/x-dosexec", - "application/vnd.microsoft.portable-executable", - "application/x-msdownload", - "application/exe", - "application/x-exe", - "application/dos-exe", - "vms/exe", - "application/x-winexe", - "application/msdos-windows", - "application/x-msdos-program" + "application/x-bat",//NON-NLS + "application/x-dosexec",//NON-NLS + "application/vnd.microsoft.portable-executable",//NON-NLS + "application/x-msdownload",//NON-NLS + "application/exe",//NON-NLS + "application/x-exe",//NON-NLS + "application/dos-exe",//NON-NLS + "vms/exe",//NON-NLS + "application/x-winexe",//NON-NLS + "application/msdos-windows",//NON-NLS + "application/x-msdos-program"//NON-NLS ).map(MediaType::parse).collect(Collectors.toSet()); private static final Set DOCUMENT_MIME_TYPES = Stream.of( @@ -87,17 +88,32 @@ public final class FilterUtils { private FilterUtils() { } + /** + * Create a new FileTypesFilter with the default FileTypeFilters for Media, + * Documents, Executables, and Other. + * + * @return The new FileTypesFilter. + */ + @NbBundle.Messages({ + "FilterUtils.mediaFilter.displayName=Media", + "FilterUtils.documentsFilter.displayName=Documents", + "FilterUtils.executablesFilter.displayName=Executables", + "FilterUtils.otherFilter.displayName=Other"}) public static FileTypesFilter createDefaultFileTypesFilter() { FileTypesFilter fileTypesFilter = new FileTypesFilter(); - fileTypesFilter.addSubFilter(new FileTypeFilter("Media", MEDIA_MIME_TYPES)); - fileTypesFilter.addSubFilter(new FileTypeFilter("Documents", DOCUMENT_MIME_TYPES)); - fileTypesFilter.addSubFilter(new FileTypeFilter("Executables", EXECUTABLE_MIME_TYPES)); - fileTypesFilter.addSubFilter(new InverseFileTypeFilter("Other", NON_OTHER_MIME_TYPES)); + fileTypesFilter.addSubFilter(new FileTypeFilter(Bundle.FilterUtils_mediaFilter_displayName(), MEDIA_MIME_TYPES)); + fileTypesFilter.addSubFilter(new FileTypeFilter(Bundle.FilterUtils_documentsFilter_displayName(), DOCUMENT_MIME_TYPES)); + fileTypesFilter.addSubFilter(new FileTypeFilter(Bundle.FilterUtils_executablesFilter_displayName(), EXECUTABLE_MIME_TYPES)); + fileTypesFilter.addSubFilter(new InverseFileTypeFilter(Bundle.FilterUtils_otherFilter_displayName(), NON_OTHER_MIME_TYPES)); return fileTypesFilter; } + /** + * Subclass of FileTypeFilter that excludes rather than includes the given + * MediaTypes. + */ private static class InverseFileTypeFilter extends FileTypeFilter { InverseFileTypeFilter(String displayName, Collection mediaTypes) { @@ -111,7 +127,7 @@ public final class FilterUtils { @Override protected String getSQLWhere(TimelineManager manager) { - return " NOT " + super.getSQLWhere(manager) ; + return " NOT " + super.getSQLWhere(manager); } } }