/* * Autopsy Forensic Browser * * Copyright 2014-16 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.timeline.datamodel; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedSet; import java.util.Collections; import java.util.Comparator; import java.util.Optional; import java.util.Set; import java.util.SortedSet; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import org.joda.time.Interval; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; import org.sleuthkit.datamodel.TskData; /** * A single event. */ @Immutable public class SingleEvent implements TimeLineEvent { private final long eventID; /** * The TSK object ID of the file this event is derived from. */ private final long objID; /** * The TSK artifact ID of the file this event is derived from. Null, if this * event is not derived from an artifact. */ private final Long artifactID; /** * The TSK datasource ID of the datasource this event belongs to. */ private final long dataSourceID; /** * The time of this event in second from the Unix epoch. */ private final long time; /** * The type of this event. */ private final EventType type; /** * The three descriptions (full, med, short) stored in a map, keyed by * DescriptionLOD (Level of Detail) */ private final ImmutableMap<DescriptionLoD, String> descriptions; /** * The known value for the file this event is derived from. */ private final TskData.FileKnown known; /** * True if the file this event is derived from hits any of the configured * hash sets. */ private final boolean hashHit; /** * True if the file or artifact this event is derived from is tagged. */ private final boolean tagged; /** * Single events may or may not have their parent set, since the parent is a * transient property of the current (details) view settings. */ private MultiEvent<?> parent = null; public SingleEvent(long eventID, long dataSourceID, long objID, @Nullable Long artifactID, long time, EventType type, String fullDescription, String medDescription, String shortDescription, TskData.FileKnown known, boolean hashHit, boolean tagged) { this.eventID = eventID; this.dataSourceID = dataSourceID; this.objID = objID; this.artifactID = Long.valueOf(0).equals(artifactID) ? null : artifactID; this.time = time; this.type = type; descriptions = ImmutableMap.<DescriptionLoD, String>of(DescriptionLoD.FULL, fullDescription, DescriptionLoD.MEDIUM, medDescription, DescriptionLoD.SHORT, shortDescription); this.known = known; this.hashHit = hashHit; this.tagged = tagged; } /** * Get a new SingleEvent that is the same as this event, but with the given * parent. * * @param newParent the parent of the new event object. * * @return a new SingleEvent that is the same as this event, but with the * given parent. */ public SingleEvent withParent(MultiEvent<?> newParent) { SingleEvent singleEvent = new SingleEvent(eventID, dataSourceID, objID, artifactID, time, type, descriptions.get(DescriptionLoD.FULL), descriptions.get(DescriptionLoD.MEDIUM), descriptions.get(DescriptionLoD.SHORT), known, hashHit, tagged); singleEvent.parent = newParent; return singleEvent; } /** * Is the file or artifact this event is derived from tagged? * * @return true if he file or artifact this event is derived from is tagged. */ public boolean isTagged() { return tagged; } /** * Is the file this event is derived from in any of the configured hash * sets. * * * @return True if the file this event is derived from is in any of the * configured hash sets. */ public boolean isHashHit() { return hashHit; } /** * Get the artifact id of the artifact this event is derived from. * * @return An Optional containing the artifact ID. Will be empty if this * event is not derived from an artifact */ public Optional<Long> getArtifactID() { return Optional.ofNullable(artifactID); } /** * Get the event id of this event. * * @return The event id of this event. */ public long getEventID() { return eventID; } /** * Get the obj id of the file this event is derived from. * * @return the object id. */ public long getFileID() { return objID; } /** * Get the time of this event (in seconds from the Unix epoch). * * @return the time of this event in seconds from Unix epoch */ public long getTime() { return time; } @Override public EventType getEventType() { return type; } /** * Get the full description of this event. * * @return the full description */ public String getFullDescription() { return getDescription(DescriptionLoD.FULL); } /** * Get the medium description of this event. * * @return the medium description */ public String getMedDescription() { return getDescription(DescriptionLoD.MEDIUM); } /** * Get the short description of this event. * * @return the short description */ public String getShortDescription() { return getDescription(DescriptionLoD.SHORT); } /** * Get the known value of the file this event is derived from. * * @return the known value */ public TskData.FileKnown getKnown() { return known; } /** * Get the description of this event at the give level of detail(LoD). * * @param lod The level of detail to get. * * @return The description of this event at the given level of detail. */ public String getDescription(DescriptionLoD lod) { return descriptions.get(lod); } /** * Get the datasource id of the datasource this event belongs to. * * @return the datasource id. */ public long getDataSourceID() { return dataSourceID; } @Override public Set<Long> getEventIDs() { return Collections.singleton(eventID); } @Override public Set<Long> getEventIDsWithHashHits() { return isHashHit() ? Collections.singleton(eventID) : Collections.emptySet(); } @Override public Set<Long> getEventIDsWithTags() { return isTagged() ? Collections.singleton(eventID) : Collections.emptySet(); } @Override public long getEndMillis() { return time * 1000; } @Override public long getStartMillis() { return time * 1000; } @Override public int hashCode() { int hash = 7; hash = 13 * hash + (int) (this.eventID ^ (this.eventID >>> 32)); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final SingleEvent other = (SingleEvent) obj; if (this.eventID != other.eventID) { return false; } return true; } @Override public SortedSet<EventCluster> getClusters() { EventCluster eventCluster = new EventCluster(new Interval(time * 1000, time * 1000), type, getEventIDs(), getEventIDsWithHashHits(), getEventIDsWithTags(), getFullDescription(), DescriptionLoD.FULL); return ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis)).add(eventCluster).build(); } @Override public String getDescription() { return getFullDescription(); } @Override public DescriptionLoD getDescriptionLoD() { return DescriptionLoD.FULL; } /** * get the EventStripe (if any) that contains this event, skipping over any * intervening event cluster * * @return an Optional containing the parent stripe of this cluster: empty * if the cluster has no parent set or the parent has no parent * stripe. */ @Override public Optional<EventStripe> getParentStripe() { if (parent == null) { return Optional.empty(); } else if (parent instanceof EventStripe) { return Optional.of((EventStripe) parent); } else { return parent.getParentStripe(); } } }