diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index 6e419b2f0d..795e804c27 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -42,6 +42,7 @@ import javafx.collections.ObservableList; import javafx.collections.ObservableMap; import javafx.collections.ObservableSet; import javafx.collections.SetChangeListener; +import static org.apache.commons.lang3.StringUtils.isBlank; import org.joda.time.DateTimeZone; import org.joda.time.Interval; import org.openide.util.NbBundle; @@ -62,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; @@ -80,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; @@ -87,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.TypeFilter; /** * This class acts as the model for a TimelineView @@ -101,9 +104,9 @@ import org.sleuthkit.datamodel.timeline.TimelineFilter.TypeFilter; * 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 { @@ -121,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; @@ -151,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()); @@ -215,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); } @@ -273,16 +267,17 @@ 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); } + } /** @@ -342,31 +337,27 @@ 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); + + FileTypesFilter fileTypesFilter = FilterUtils.createDefaultFileTypesFilter(); + 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())); } @@ -397,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); @@ -415,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 @@ -442,15 +435,23 @@ 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 */ public Interval getSpanningInterval() throws TskCoreException { return new Interval(getMinTime() * 1000, 1000 + getMaxTime() * 1000); } /** + * 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); @@ -460,6 +461,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 +476,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 { @@ -536,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); @@ -549,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); @@ -617,6 +626,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); @@ -694,10 +705,12 @@ 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 { - if (org.apache.commons.lang3.StringUtils.isBlank(groupConcat)) { + if (isBlank(groupConcat)) { return Collections.emptyList(); } 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/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/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/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 { 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"; 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..0e6ff39d35 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/utils/FilterUtils.java @@ -0,0 +1,133 @@ +/* + * 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.openide.util.NbBundle; +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/*",//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",//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( + "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() { + } + + /** + * 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(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) { + 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); + } + } +}