19 package org.sleuthkit.datamodel;
21 import com.google.common.annotations.Beta;
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.collect.ImmutableMap;
24 import java.sql.PreparedStatement;
25 import java.sql.ResultSet;
26 import java.sql.SQLException;
27 import java.sql.Statement;
28 import java.time.Instant;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.List;
36 import java.util.Objects;
37 import static java.util.Objects.isNull;
38 import java.util.Optional;
40 import java.util.logging.Level;
41 import java.util.logging.Logger;
42 import java.util.stream.Collectors;
43 import org.joda.time.DateTimeZone;
44 import org.joda.time.Interval;
57 private static final Logger logger = Logger.getLogger(
TimelineManager.class.getName());
62 private static final ImmutableList<TimelineEventType> ROOT_CATEGORY_AND_FILESYSTEM_TYPES
79 private static final ImmutableList<TimelineEventType> PREDEFINED_EVENT_TYPES
92 private static final Long MAX_TIMESTAMP_TO_ADD = Instant.now().getEpochSecond() + 394200000;
97 private final Map<Long, TimelineEventType> eventTypeIDMap =
new HashMap<>();
109 this.caseDB = caseDB;
112 ROOT_CATEGORY_AND_FILESYSTEM_TYPES.forEach(eventType -> eventTypeIDMap.put(eventType.getTypeID(), eventType));
116 try (
final CaseDbConnection con = caseDB.getConnection();
117 final Statement statement = con.createStatement()) {
119 con.executeUpdate(statement,
120 insertOrIgnore(
" INTO tsk_event_types(event_type_id, display_name, super_type_id) "
121 +
"VALUES( " + type.getTypeID() +
", '"
122 + escapeSingleQuotes(type.getDisplayName()) +
"',"
123 + type.getParent().getTypeID()
125 eventTypeIDMap.put(type.getTypeID(), type);
127 }
catch (SQLException ex) {
128 throw new TskCoreException(
"Failed to initialize timeline event types", ex);
146 if (eventIDs.isEmpty()) {
149 final String query =
"SELECT Min(time) as minTime, Max(time) as maxTime FROM tsk_events WHERE event_id IN (" + buildCSVString(eventIDs) +
")";
151 try (CaseDbConnection con = caseDB.getConnection();
152 Statement stmt = con.createStatement();
153 ResultSet results = stmt.executeQuery(query);) {
154 if (results.next()) {
155 return new Interval(results.getLong(
"minTime") * 1000, (results.getLong(
"maxTime") + 1) * 1000, DateTimeZone.UTC);
157 }
catch (SQLException ex) {
158 throw new TskCoreException(
"Error executing get spanning interval query: " + query, ex);
178 long start = timeRange.getStartMillis() / 1000;
179 long end = timeRange.getEndMillis() / 1000;
180 String sqlWhere = getSQLWhere(filter);
181 String augmentedEventsTablesSQL = getAugmentedEventsTablesSQL(filter);
182 String queryString =
" SELECT (SELECT Max(time) FROM " + augmentedEventsTablesSQL
183 +
" WHERE time <=" + start +
" AND " + sqlWhere +
") AS start,"
184 +
" (SELECT Min(time) FROM " + augmentedEventsTablesSQL
185 +
" WHERE time >= " + end +
" AND " + sqlWhere +
") AS end";
187 try (CaseDbConnection con = caseDB.getConnection();
188 Statement stmt = con.createStatement();
189 ResultSet results = stmt.executeQuery(queryString);) {
191 if (results.next()) {
192 long start2 = results.getLong(
"start");
193 long end2 = results.getLong(
"end");
198 return new Interval(start2 * 1000, (end2 + 1) * 1000, timeZone);
200 }
catch (SQLException ex) {
218 String sql =
"SELECT * FROM " + getAugmentedEventsTablesSQL(
false) +
" WHERE event_id = " + eventID;
220 try (CaseDbConnection con = caseDB.getConnection();
221 Statement stmt = con.createStatement();) {
222 try (ResultSet results = stmt.executeQuery(sql);) {
223 if (results.next()) {
224 int typeID = results.getInt(
"event_type_id");
227 results.getLong(
"data_source_obj_id"),
228 results.getLong(
"content_obj_id"),
229 results.getLong(
"artifact_id"),
230 results.getLong(
"time"),
231 type, results.getString(
"full_description"),
232 results.getString(
"med_description"),
233 results.getString(
"short_description"),
234 intToBoolean(results.getInt(
"hash_hit")),
235 intToBoolean(results.getInt(
"tagged")));
238 }
catch (SQLException sqlEx) {
258 Long startTime = timeRange.getStartMillis() / 1000;
259 Long endTime = timeRange.getEndMillis() / 1000;
261 if (Objects.equals(startTime, endTime)) {
265 ArrayList<Long> resultIDs =
new ArrayList<>();
267 String query =
"SELECT tsk_events.event_id AS event_id FROM " + getAugmentedEventsTablesSQL(filter)
268 +
" WHERE time >= " + startTime +
" AND time <" + endTime +
" AND " + getSQLWhere(filter) +
" ORDER BY time ASC";
270 try (CaseDbConnection con = caseDB.getConnection();
271 Statement stmt = con.createStatement();
272 ResultSet results = stmt.executeQuery(query);) {
273 while (results.next()) {
274 resultIDs.add(results.getLong(
"event_id"));
277 }
catch (SQLException sqlEx) {
278 throw new TskCoreException(
"Error while executing query " + query, sqlEx);
296 try (CaseDbConnection con = caseDB.getConnection();
297 Statement stms = con.createStatement();
298 ResultSet results = stms.executeQuery(STATEMENTS.GET_MAX_TIME.getSQL());) {
299 if (results.next()) {
300 return results.getLong(
"max");
302 }
catch (SQLException ex) {
303 throw new TskCoreException(
"Error while executing query " + STATEMENTS.GET_MAX_TIME.getSQL(), ex);
320 try (CaseDbConnection con = caseDB.getConnection();
321 Statement stms = con.createStatement();
322 ResultSet results = stms.executeQuery(STATEMENTS.GET_MIN_TIME.getSQL());) {
323 if (results.next()) {
324 return results.getLong(
"min");
326 }
catch (SQLException ex) {
327 throw new TskCoreException(
"Error while executing query " + STATEMENTS.GET_MAX_TIME.getSQL(), ex);
343 return Optional.ofNullable(eventTypeIDMap.get(eventTypeID));
352 return ImmutableList.copyOf(eventTypeIDMap.values());
355 private String insertOrIgnore(String query) {
358 return " INSERT " + query +
" ON CONFLICT DO NOTHING ";
360 return " INSERT OR IGNORE " + query;
362 throw new UnsupportedOperationException(
"Unsupported DB type: " + caseDB.
getDatabaseType().name());
369 private enum STATEMENTS {
371 GET_MAX_TIME(
"SELECT Max(time) AS max FROM tsk_events"),
372 GET_MIN_TIME(
"SELECT Min(time) AS min FROM tsk_events");
374 private final String sql;
376 private STATEMENTS(String sql) {
396 ArrayList<Long> eventIDs =
new ArrayList<>();
399 =
"SELECT event_id FROM tsk_events "
400 +
" LEFT JOIN tsk_event_descriptions on ( tsk_events.event_description_id = tsk_event_descriptions.event_description_id ) "
401 +
" WHERE artifact_id = " + artifact.getArtifactID();
403 try (CaseDbConnection con = caseDB.getConnection();
404 Statement stmt = con.createStatement();
405 ResultSet results = stmt.executeQuery(query);) {
406 while (results.next()) {
407 eventIDs.add(results.getLong(
"event_id"));
409 }
catch (SQLException ex) {
410 throw new TskCoreException(
"Error executing getEventIDsForArtifact query.", ex);
432 try (CaseDbConnection conn = caseDB.getConnection()) {
433 return getEventAndDescriptionIDs(conn, content.getId(), includeDerivedArtifacts).keySet();
456 private long addEventDescription(
long dataSourceObjId,
long fileObjId, Long artifactID,
457 String fullDescription, String medDescription, String shortDescription,
458 boolean hasHashHits,
boolean tagged, CaseDbConnection connection)
throws TskCoreException {
459 String insertDescriptionSql
460 =
"INSERT INTO tsk_event_descriptions ( "
461 +
"data_source_obj_id, content_obj_id, artifact_id, "
462 +
" full_description, med_description, short_description, "
463 +
" hash_hit, tagged "
465 + dataSourceObjId +
","
467 + Objects.toString(artifactID,
"NULL") +
","
468 + quotePreservingNull(fullDescription) +
","
469 + quotePreservingNull(medDescription) +
","
470 + quotePreservingNull(shortDescription) +
", "
471 + booleanToInt(hasHashHits) +
","
472 + booleanToInt(tagged)
476 try (Statement insertDescriptionStmt = connection.createStatement()) {
477 connection.executeUpdate(insertDescriptionStmt, insertDescriptionSql, PreparedStatement.RETURN_GENERATED_KEYS);
478 try (ResultSet generatedKeys = insertDescriptionStmt.getGeneratedKeys()) {
479 generatedKeys.next();
480 return generatedKeys.getLong(1);
482 }
catch (SQLException ex) {
483 throw new TskCoreException(
"Failed to insert event description.", ex);
489 Collection<TimelineEvent> addEventsForNewFile(AbstractFile file, CaseDbConnection connection)
throws TskCoreException {
491 Map<TimelineEventType, Long> timeMap = ImmutableMap.of(TimelineEventType.FILE_CREATED, file.getCrtime(),
492 TimelineEventType.FILE_ACCESSED, file.getAtime(),
493 TimelineEventType.FILE_CHANGED, file.getCtime(),
494 TimelineEventType.FILE_MODIFIED, file.getMtime());
500 if (Collections.max(timeMap.values()) <= 0) {
501 return Collections.emptySet();
504 String description = file.getParentPath() + file.getName();
505 long fileObjId = file.getId();
506 Set<TimelineEvent> events =
new HashSet<>();
509 long descriptionID = addEventDescription(file.getDataSourceObjectId(), fileObjId, null,
510 description, null, null,
false,
false, connection);
512 for (Map.Entry<TimelineEventType, Long> timeEntry : timeMap.entrySet()) {
513 Long time = timeEntry.getValue();
514 if (time > 0 && time < MAX_TIMESTAMP_TO_ADD) {
515 TimelineEventType type = timeEntry.getKey();
516 long eventID = addEventWithExistingDescription(time, type, descriptionID, connection);
523 events.add(
new TimelineEvent(eventID, descriptionID, fileObjId, null, time, type,
524 description, null, null,
false,
false));
526 if (time >= MAX_TIMESTAMP_TO_ADD) {
527 logger.log(Level.WARNING, String.format(
"Date/Time discarded from Timeline for %s for file %s with Id %d", timeEntry.getKey().getDisplayName(), file.getParentPath() + file.getName(), file.getId()));
536 .map(TimelineEventAddedEvent::new)
537 .forEach(caseDB::fireTSKEvent);
555 Set<TimelineEvent> addArtifactEvents(BlackboardArtifact artifact)
throws TskCoreException {
556 Set<TimelineEvent> newEvents =
new HashSet<>();
563 if (artifact.getArtifactTypeID() == TSK_TL_EVENT.getTypeID()) {
564 TimelineEventType eventType;
565 BlackboardAttribute attribute = artifact.getAttribute(
new BlackboardAttribute.Type(TSK_TL_EVENT_TYPE));
566 if (attribute == null) {
567 eventType = TimelineEventType.OTHER;
569 long eventTypeID = attribute.getValueLong();
570 eventType = eventTypeIDMap.getOrDefault(eventTypeID, TimelineEventType.OTHER);
574 addArtifactEvent(((TimelineEventArtifactTypeImpl) TimelineEventType.OTHER)::makeEventDescription, eventType, artifact)
575 .ifPresent(newEvents::add);
581 Set<TimelineEventArtifactTypeImpl> eventTypesForArtifact = eventTypeIDMap.values().stream()
582 .filter(TimelineEventArtifactTypeImpl.class::isInstance)
583 .map(TimelineEventArtifactTypeImpl.class::cast)
584 .filter(eventType -> eventType.getArtifactTypeID() == artifact.getArtifactTypeID())
585 .collect(Collectors.toSet());
587 for (TimelineEventArtifactTypeImpl eventType : eventTypesForArtifact) {
588 addArtifactEvent(eventType::makeEventDescription, eventType, artifact)
589 .ifPresent(newEvents::add);
593 .map(TimelineEventAddedEvent::new)
594 .forEach(caseDB::fireTSKEvent);
615 private Optional<TimelineEvent> addArtifactEvent(TSKCoreCheckedFunction<BlackboardArtifact, TimelineEventDescriptionWithTime> payloadExtractor,
616 TimelineEventType eventType, BlackboardArtifact artifact)
throws TskCoreException {
617 TimelineEventDescriptionWithTime eventPayload = payloadExtractor.apply(artifact);
618 if (eventPayload == null) {
619 return Optional.empty();
621 long time = eventPayload.getTime();
623 if (time <= 0 || time >= MAX_TIMESTAMP_TO_ADD) {
624 if (time >= MAX_TIMESTAMP_TO_ADD) {
625 logger.log(Level.WARNING, String.format(
"Date/Time discarded from Timeline for %s for artifact %s with id %d", artifact.getDisplayName(), eventPayload.getDescription(TimelineLevelOfDetail.HIGH), artifact.getId()));
627 return Optional.empty();
629 String fullDescription = eventPayload.getDescription(TimelineLevelOfDetail.HIGH);
630 String medDescription = eventPayload.getDescription(TimelineLevelOfDetail.MEDIUM);
631 String shortDescription = eventPayload.getDescription(TimelineLevelOfDetail.LOW);
632 long artifactID = artifact.getArtifactID();
633 long fileObjId = artifact.getObjectID();
634 long dataSourceObjectID = artifact.getDataSourceObjectID();
637 boolean hasHashHits =
false;
640 hasHashHits = isNotEmpty(file.getHashSetNames());
646 try (CaseDbConnection connection = caseDB.getConnection();) {
648 long descriptionID = addEventDescription(dataSourceObjectID, fileObjId, artifactID,
649 fullDescription, medDescription, shortDescription,
650 hasHashHits, tagged, connection);
652 long eventID = addEventWithExistingDescription(time, eventType, descriptionID, connection);
654 event =
new TimelineEvent(eventID, dataSourceObjectID, fileObjId, artifactID,
655 time, eventType, fullDescription, medDescription, shortDescription,
656 hasHashHits, tagged);
661 return Optional.of(event);
664 private long addEventWithExistingDescription(Long time, TimelineEventType type,
long descriptionID, CaseDbConnection connection)
throws TskCoreException {
665 String insertEventSql
666 =
"INSERT INTO tsk_events ( event_type_id, event_description_id , time) "
667 +
" VALUES (" + type.getTypeID() +
", " + descriptionID +
", " + time +
")";
670 try (Statement insertRowStmt = connection.createStatement();) {
671 connection.executeUpdate(insertRowStmt, insertEventSql, PreparedStatement.RETURN_GENERATED_KEYS);
673 try (ResultSet generatedKeys = insertRowStmt.getGeneratedKeys();) {
674 generatedKeys.next();
675 return generatedKeys.getLong(1);
677 }
catch (SQLException ex) {
678 throw new TskCoreException(
"Failed to insert event for existing description.", ex);
684 static private String quotePreservingNull(String value) {
685 return isNull(value) ?
" NULL " :
"'" + escapeSingleQuotes(value) +
"'";
688 private Map<Long, Long> getEventAndDescriptionIDs(CaseDbConnection conn,
long contentObjID,
boolean includeArtifacts)
throws TskCoreException {
689 return getEventAndDescriptionIDsHelper(conn, contentObjID, (includeArtifacts ?
"" :
" AND artifact_id IS NULL"));
692 private Map<Long, Long> getEventAndDescriptionIDs(CaseDbConnection conn,
long contentObjID, Long artifactID)
throws TskCoreException {
693 return getEventAndDescriptionIDsHelper(conn, contentObjID,
" AND artifact_id = " + artifactID);
696 private Map<Long, Long> getEventAndDescriptionIDsHelper(CaseDbConnection con,
long fileObjID, String artifactClause)
throws TskCoreException {
698 Map<Long, Long> eventIDToDescriptionIDs =
new HashMap<>();
699 String sql =
"SELECT event_id, tsk_events.event_description_id"
700 +
" FROM tsk_events "
701 +
" LEFT JOIN tsk_event_descriptions ON ( tsk_events.event_description_id = tsk_event_descriptions.event_description_id )"
702 +
" WHERE content_obj_id = " + fileObjID
704 try (Statement selectStmt = con.createStatement(); ResultSet executeQuery = selectStmt.executeQuery(sql);) {
705 while (executeQuery.next()) {
706 eventIDToDescriptionIDs.put(executeQuery.getLong(
"event_id"), executeQuery.getLong(
"event_description_id"));
708 }
catch (SQLException ex) {
709 throw new TskCoreException(
"Error getting event description ids for object id = " + fileObjID, ex);
711 return eventIDToDescriptionIDs;
733 try (CaseDbConnection conn = caseDB.getConnection()) {
734 Map<Long, Long> eventIDs = getEventAndDescriptionIDs(conn, content.getId(),
false);
735 updateEventSourceTaggedFlag(conn, eventIDs.values(), 1);
736 return eventIDs.keySet();
762 try (CaseDbConnection conn = caseDB.getConnection()) {
764 Map<Long, Long> eventIDs = getEventAndDescriptionIDs(conn, content.getId(),
false);
765 updateEventSourceTaggedFlag(conn, eventIDs.values(), 0);
766 return eventIDs.keySet();
768 return Collections.emptySet();
788 try (CaseDbConnection conn = caseDB.getConnection()) {
789 Map<Long, Long> eventIDs = getEventAndDescriptionIDs(conn, artifact.getObjectID(), artifact.getArtifactID());
790 updateEventSourceTaggedFlag(conn, eventIDs.values(), 1);
791 return eventIDs.keySet();
811 try (CaseDbConnection conn = caseDB.getConnection()) {
813 Map<Long, Long> eventIDs = getEventAndDescriptionIDs(conn, artifact.getObjectID(), artifact.getArtifactID());
814 updateEventSourceTaggedFlag(conn, eventIDs.values(), 0);
815 return eventIDs.keySet();
817 return Collections.emptySet();
824 private void updateEventSourceTaggedFlag(CaseDbConnection conn, Collection<Long> eventDescriptionIDs,
int flagValue)
throws TskCoreException {
825 if (eventDescriptionIDs.isEmpty()) {
829 String sql =
"UPDATE tsk_event_descriptions SET tagged = " + flagValue +
" WHERE event_description_id IN (" + buildCSVString(eventDescriptionIDs) +
")";
830 try (Statement updateStatement = conn.createStatement()) {
831 updateStatement.executeUpdate(sql);
832 }
catch (SQLException ex) {
833 throw new TskCoreException(
"Error marking content events tagged: " + sql, ex);
853 try (CaseDbConnection con = caseDB.getConnection(); Statement updateStatement = con.createStatement();) {
854 Map<Long, Long> eventIDs = getEventAndDescriptionIDs(con, content.getId(),
true);
855 if (! eventIDs.isEmpty()) {
856 String sql =
"UPDATE tsk_event_descriptions SET hash_hit = 1" +
" WHERE event_description_id IN (" + buildCSVString(eventIDs.values()) +
")";
858 updateStatement.executeUpdate(sql);
859 return eventIDs.keySet();
860 }
catch (SQLException ex) {
864 return eventIDs.keySet();
866 }
catch (SQLException ex) {
897 long adjustedEndTime = Objects.equals(startTime, endTime) ? endTime + 1 : endTime;
901 String queryString =
"SELECT count(DISTINCT tsk_events.event_id) AS count, " + typeColumn
902 +
" FROM " + getAugmentedEventsTablesSQL(filter)
903 +
" WHERE time >= " + startTime +
" AND time < " + adjustedEndTime +
" AND " + getSQLWhere(filter)
904 +
" GROUP BY " + typeColumn;
907 try (CaseDbConnection con = caseDB.getConnection();
908 Statement stmt = con.createStatement();
909 ResultSet results = stmt.executeQuery(queryString);) {
910 Map<TimelineEventType, Long> typeMap =
new HashMap<>();
911 while (results.next()) {
912 int eventTypeID = results.getInt(typeColumn);
914 .orElseThrow(() -> newEventTypeMappingException(eventTypeID));
916 typeMap.put(eventType, results.getLong(
"count"));
919 }
catch (SQLException ex) {
920 throw new TskCoreException(
"Error getting count of events from db: " + queryString, ex);
926 private static TskCoreException newEventTypeMappingException(
int eventTypeID) {
927 return new TskCoreException(
"Error mapping event type id " + eventTypeID +
" to EventType.");
943 static private String getAugmentedEventsTablesSQL(TimelineFilter.RootFilter filter) {
944 TimelineFilter.FileTypesFilter fileTypesFitler = filter.getFileTypesFilter();
945 boolean needsMimeTypes = fileTypesFitler != null && fileTypesFitler.hasSubFilters();
947 return getAugmentedEventsTablesSQL(needsMimeTypes);
964 static private String getAugmentedEventsTablesSQL(
boolean needMimeTypes) {
977 return "( SELECT event_id, time, tsk_event_descriptions.data_source_obj_id, content_obj_id, artifact_id, "
978 +
" full_description, med_description, short_description, tsk_events.event_type_id, super_type_id,"
979 +
" hash_hit, tagged "
980 + (needMimeTypes ?
", mime_type" :
"")
981 +
" FROM tsk_events "
982 +
" JOIN tsk_event_descriptions ON ( tsk_event_descriptions.event_description_id = tsk_events.event_description_id)"
983 +
" JOIN tsk_event_types ON (tsk_events.event_type_id = tsk_event_types.event_type_id ) "
984 + (needMimeTypes ?
" LEFT OUTER JOIN tsk_files "
985 +
" ON (tsk_event_descriptions.content_obj_id = tsk_files.obj_id)"
997 private static int booleanToInt(
boolean value) {
998 return value ? 1 : 0;
1001 private static boolean intToBoolean(
int value) {
1018 List<TimelineEvent> events =
new ArrayList<>();
1020 Long startTime = timeRange.getStartMillis() / 1000;
1021 Long endTime = timeRange.getEndMillis() / 1000;
1023 if (Objects.equals(startTime, endTime)) {
1027 if (filter == null) {
1031 if (endTime < startTime) {
1036 String querySql =
"SELECT time, content_obj_id, data_source_obj_id, artifact_id, "
1040 +
" event_type_id, super_type_id, "
1041 +
" full_description, med_description, short_description "
1042 +
" FROM " + getAugmentedEventsTablesSQL(filter)
1043 +
" WHERE time >= " + startTime +
" AND time < " + endTime +
" AND " + getSQLWhere(filter)
1047 try (CaseDbConnection con = caseDB.getConnection();
1048 Statement stmt = con.createStatement();
1049 ResultSet resultSet = stmt.executeQuery(querySql);) {
1051 while (resultSet.next()) {
1052 int eventTypeID = resultSet.getInt(
"event_type_id");
1054 ->
new TskCoreException(
"Error mapping event type id " + eventTypeID +
"to EventType."));
1057 resultSet.getLong(
"event_id"),
1058 resultSet.getLong(
"data_source_obj_id"),
1059 resultSet.getLong(
"content_obj_id"),
1060 resultSet.getLong(
"artifact_id"),
1061 resultSet.getLong(
"time"),
1063 resultSet.getString(
"full_description"),
1064 resultSet.getString(
"med_description"),
1065 resultSet.getString(
"short_description"),
1066 resultSet.getInt(
"hash_hit") != 0,
1067 resultSet.getInt(
"tagged") != 0);
1072 }
catch (SQLException ex) {
1073 throw new TskCoreException(
"Error getting events from db: " + querySql, ex);
1088 private static String typeColumnHelper(
final boolean useSubTypes) {
1089 return useSubTypes ?
"event_type_id" :
"super_type_id";
1100 String getSQLWhere(TimelineFilter.RootFilter filter) {
1103 if (filter == null) {
1104 return getTrueLiteral();
1106 result = filter.getSQLWhere(
this);
1112 private String getTrueLiteral() {
1119 throw new UnsupportedOperationException(
"Unsupported DB type: " + caseDB.
getDatabaseType().name());
1137 this.addedEvent = event;
1148 @FunctionalInterface
1149 private interface TSKCoreCheckedFunction<I, O> {
1151 O apply(I input)
throws TskCoreException;
List< Long > getEventIDs(Interval timeRange, TimelineFilter.RootFilter filter)
TimelineEvent getAddedEvent()
TimelineEvent getEventById(long eventID)
ImmutableList< TimelineEventType > getEventTypes()
Interval getSpanningInterval(Interval timeRange, TimelineFilter.RootFilter filter, DateTimeZone timeZone)
Set< Long > getEventIDsForContent(Content content, boolean includeDerivedArtifacts)
TimelineEventType FILE_ACCESSED
Interval getSpanningInterval(Collection< Long > eventIDs)
Set< Long > updateEventsForContentTagAdded(Content content)
List< BlackboardArtifactTag > getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact)
SortedSet<?extends TimelineEventType > getChildren()
Set< Long > updateEventsForContentTagDeleted(Content content)
Set< Long > updateEventsForHashSetHit(Content content)
TimelineEventType WEB_ACTIVITY
AbstractFile getAbstractFileById(long id)
TimelineEventType FILE_MODIFIED
void releaseSingleUserCaseReadLock()
TimelineEventType MISC_TYPES
static String escapeSingleQuotes(String text)
Set< Long > updateEventsForArtifactTagDeleted(BlackboardArtifact artifact)
void acquireSingleUserCaseWriteLock()
void releaseSingleUserCaseWriteLock()
TimelineEventType FILE_CREATED
TimelineEventType FILE_SYSTEM
Map< TimelineEventType, Long > countEventsByType(Long startTime, Long endTime, TimelineFilter.RootFilter filter, TimelineEventType.HierarchyLevel typeHierachyLevel)
List< Long > getEventIDsForArtifact(BlackboardArtifact artifact)
TimelineEventType ROOT_EVENT_TYPE
List< TimelineEvent > getEvents(Interval timeRange, TimelineFilter.RootFilter filter)
TimelineEventType CUSTOM_TYPES
void acquireSingleUserCaseReadLock()
List< ContentTag > getContentTagsByContent(Content content)
Optional< TimelineEventType > getEventType(long eventTypeID)
TimelineEventType FILE_CHANGED
Set< Long > updateEventsForArtifactTagAdded(BlackboardArtifact artifact)