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.sql.Types;
29 import java.text.MessageFormat;
30 import java.time.Instant;
31 import java.util.ArrayList;
32 import java.util.Collection;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.List;
38 import java.util.Objects;
39 import java.util.Optional;
41 import java.util.logging.Level;
42 import java.util.logging.Logger;
43 import java.util.stream.Collectors;
44 import java.util.stream.Stream;
45 import org.joda.time.DateTimeZone;
46 import org.joda.time.Interval;
60 private static final Logger logger = Logger.getLogger(
TimelineManager.class.getName());
65 private static final ImmutableList<TimelineEventType> ROOT_CATEGORY_AND_FILESYSTEM_TYPES
82 private static final ImmutableList<TimelineEventType> PREDEFINED_EVENT_TYPES
90 .map(artType -> artType.getTypeID())
91 .collect(Collectors.toSet());
99 private static final Long MAX_TIMESTAMP_TO_ADD = Instant.now().getEpochSecond() + 394200000;
104 private final Map<Long, TimelineEventType> eventTypeIDMap =
new HashMap<>();
116 this.caseDB = caseDB;
118 List<TimelineEventType> fullList =
new ArrayList<>();
119 fullList.addAll(ROOT_CATEGORY_AND_FILESYSTEM_TYPES);
120 fullList.addAll(PREDEFINED_EVENT_TYPES);
123 try (
final CaseDbConnection con = caseDB.getConnection();
124 final PreparedStatement pStatement = con.prepareStatement(
125 insertOrIgnore(
" INTO tsk_event_types(event_type_id, display_name, super_type_id) VALUES (?, ?, ?)"),
126 Statement.NO_GENERATED_KEYS)) {
128 pStatement.setLong(1, type.getTypeID());
129 pStatement.setString(2, escapeSingleQuotes(type.getDisplayName()));
130 if (type != type.getParent()) {
131 pStatement.setLong(3, type.getParent().getTypeID());
133 pStatement.setNull(3, java.sql.Types.INTEGER);
136 con.executeUpdate(pStatement);
137 eventTypeIDMap.put(type.getTypeID(), type);
139 }
catch (SQLException ex) {
140 throw new TskCoreException(
"Failed to initialize timeline event types", ex);
158 if (eventIDs.isEmpty()) {
161 final String query =
"SELECT Min(time) as minTime, Max(time) as maxTime FROM tsk_events WHERE event_id IN (" + buildCSVString(eventIDs) +
")";
163 try (CaseDbConnection con = caseDB.getConnection();
164 Statement stmt = con.createStatement();
165 ResultSet results = stmt.executeQuery(query);) {
166 if (results.next()) {
167 return new Interval(results.getLong(
"minTime") * 1000, (results.getLong(
"maxTime") + 1) * 1000, DateTimeZone.UTC);
169 }
catch (SQLException ex) {
170 throw new TskCoreException(
"Error executing get spanning interval query: " + query, ex);
190 long start = timeRange.getStartMillis() / 1000;
191 long end = timeRange.getEndMillis() / 1000;
192 String sqlWhere = getSQLWhere(filter);
193 String augmentedEventsTablesSQL = getAugmentedEventsTablesSQL(filter);
194 String queryString =
" SELECT (SELECT Max(time) FROM " + augmentedEventsTablesSQL
195 +
" WHERE time <=" + start +
" AND " + sqlWhere +
") AS start,"
196 +
" (SELECT Min(time) FROM " + augmentedEventsTablesSQL
197 +
" WHERE time >= " + end +
" AND " + sqlWhere +
") AS end";
199 try (CaseDbConnection con = caseDB.getConnection();
200 Statement stmt = con.createStatement();
201 ResultSet results = stmt.executeQuery(queryString);) {
203 if (results.next()) {
204 long start2 = results.getLong(
"start");
205 long end2 = results.getLong(
"end");
210 return new Interval(start2 * 1000, (end2 + 1) * 1000, timeZone);
212 }
catch (SQLException ex) {
230 String sql =
"SELECT * FROM " + getAugmentedEventsTablesSQL(
false) +
" WHERE event_id = " + eventID;
232 try (CaseDbConnection con = caseDB.getConnection();
233 Statement stmt = con.createStatement();) {
234 try (ResultSet results = stmt.executeQuery(sql);) {
235 if (results.next()) {
236 int typeID = results.getInt(
"event_type_id");
239 results.getLong(
"data_source_obj_id"),
240 results.getLong(
"content_obj_id"),
241 results.getLong(
"artifact_id"),
242 results.getLong(
"time"),
243 type, results.getString(
"full_description"),
244 results.getString(
"med_description"),
245 results.getString(
"short_description"),
246 intToBoolean(results.getInt(
"hash_hit")),
247 intToBoolean(results.getInt(
"tagged")));
250 }
catch (SQLException sqlEx) {
270 Long startTime = timeRange.getStartMillis() / 1000;
271 Long endTime = timeRange.getEndMillis() / 1000;
273 if (Objects.equals(startTime, endTime)) {
277 ArrayList<Long> resultIDs =
new ArrayList<>();
279 String query =
"SELECT tsk_events.event_id AS event_id FROM " + getAugmentedEventsTablesSQL(filter)
280 +
" WHERE time >= " + startTime +
" AND time <" + endTime +
" AND " + getSQLWhere(filter) +
" ORDER BY time ASC";
282 try (CaseDbConnection con = caseDB.getConnection();
283 Statement stmt = con.createStatement();
284 ResultSet results = stmt.executeQuery(query);) {
285 while (results.next()) {
286 resultIDs.add(results.getLong(
"event_id"));
289 }
catch (SQLException sqlEx) {
290 throw new TskCoreException(
"Error while executing query " + query, sqlEx);
308 try (CaseDbConnection con = caseDB.getConnection();
309 Statement stms = con.createStatement();
310 ResultSet results = stms.executeQuery(STATEMENTS.GET_MAX_TIME.getSQL());) {
311 if (results.next()) {
312 return results.getLong(
"max");
314 }
catch (SQLException ex) {
315 throw new TskCoreException(
"Error while executing query " + STATEMENTS.GET_MAX_TIME.getSQL(), ex);
332 try (CaseDbConnection con = caseDB.getConnection();
333 Statement stms = con.createStatement();
334 ResultSet results = stms.executeQuery(STATEMENTS.GET_MIN_TIME.getSQL());) {
335 if (results.next()) {
336 return results.getLong(
"min");
338 }
catch (SQLException ex) {
339 throw new TskCoreException(
"Error while executing query " + STATEMENTS.GET_MAX_TIME.getSQL(), ex);
361 return Optional.ofNullable(eventTypeIDMap.get(eventTypeID));
370 return ImmutableList.copyOf(eventTypeIDMap.values());
373 private String insertOrIgnore(String query) {
376 return " INSERT " + query +
" ON CONFLICT DO NOTHING ";
378 return " INSERT OR IGNORE " + query;
380 throw new UnsupportedOperationException(
"Unsupported DB type: " + caseDB.
getDatabaseType().name());
387 private enum STATEMENTS {
389 GET_MAX_TIME(
"SELECT Max(time) AS max FROM tsk_events"),
390 GET_MIN_TIME(
"SELECT Min(time) AS min FROM tsk_events");
392 private final String sql;
394 private STATEMENTS(String sql) {
414 ArrayList<Long> eventIDs =
new ArrayList<>();
417 =
"SELECT event_id FROM tsk_events "
418 +
" LEFT JOIN tsk_event_descriptions on ( tsk_events.event_description_id = tsk_event_descriptions.event_description_id ) "
419 +
" WHERE artifact_id = " + artifact.getArtifactID();
421 try (CaseDbConnection con = caseDB.getConnection();
422 Statement stmt = con.createStatement();
423 ResultSet results = stmt.executeQuery(query);) {
424 while (results.next()) {
425 eventIDs.add(results.getLong(
"event_id"));
427 }
catch (SQLException ex) {
428 throw new TskCoreException(
"Error executing getEventIDsForArtifact query.", ex);
450 try (CaseDbConnection conn = caseDB.getConnection()) {
451 return getEventAndDescriptionIDs(conn, content.getId(), includeDerivedArtifacts).keySet();
475 private Long addEventDescription(
long dataSourceObjId,
long fileObjId, Long artifactID,
476 String fullDescription, String medDescription, String shortDescription,
477 boolean hasHashHits,
boolean tagged, CaseDbConnection connection)
throws TskCoreException, DuplicateException {
478 String tableValuesClause
479 =
"tsk_event_descriptions ( "
480 +
"data_source_obj_id, content_obj_id, artifact_id, "
481 +
" full_description, med_description, short_description, "
482 +
" hash_hit, tagged "
484 +
"(?, ?, ?, ?, ?, ?, ?, ?)";
486 String insertDescriptionSql = getSqlIgnoreConflict(tableValuesClause);
490 PreparedStatement insertDescriptionStmt = connection.getPreparedStatement(insertDescriptionSql, PreparedStatement.RETURN_GENERATED_KEYS);
491 insertDescriptionStmt.clearParameters();
492 insertDescriptionStmt.setLong(1, dataSourceObjId);
493 insertDescriptionStmt.setLong(2, fileObjId);
495 if (artifactID == null) {
496 insertDescriptionStmt.setNull(3, Types.INTEGER);
498 insertDescriptionStmt.setLong(3, artifactID);
501 insertDescriptionStmt.setString(4, fullDescription);
502 insertDescriptionStmt.setString(5, medDescription);
503 insertDescriptionStmt.setString(6, shortDescription);
504 insertDescriptionStmt.setInt(7, booleanToInt(hasHashHits));
505 insertDescriptionStmt.setInt(8, booleanToInt(tagged));
506 int row = insertDescriptionStmt.executeUpdate();
513 try (ResultSet generatedKeys = insertDescriptionStmt.getGeneratedKeys()) {
514 if (generatedKeys.next()) {
515 return generatedKeys.getLong(1);
520 }
catch (SQLException ex) {
521 throw new TskCoreException(
"Failed to insert event description.", ex);
540 private Long getEventDescription(
long dataSourceObjId,
long fileObjId, Long artifactID,
541 String fullDescription, CaseDbConnection connection)
throws TskCoreException {
543 String query =
"SELECT event_description_id FROM tsk_event_descriptions "
544 +
"WHERE data_source_obj_id = " + dataSourceObjId
545 +
" AND content_obj_id = " + fileObjId
546 +
" AND artifact_id " + (artifactID != null ?
" = " + artifactID :
"IS null")
547 +
" AND full_description " + (fullDescription != null ?
"= '"
548 + SleuthkitCase.escapeSingleQuotes(fullDescription) +
"'" :
"IS null");
551 try (ResultSet resultSet = connection.createStatement().executeQuery(query)) {
553 if (resultSet.next()) {
554 long id = resultSet.getLong(1);
557 }
catch (SQLException ex) {
558 throw new TskCoreException(String.format(
"Failed to get description, dataSource=%d, fileObjId=%d, artifactId=%d", dataSourceObjId, fileObjId, artifactID), ex);
566 Collection<TimelineEvent> addEventsForNewFile(AbstractFile file, CaseDbConnection connection)
throws TskCoreException {
567 Set<TimelineEvent> events = addEventsForNewFileQuiet(file, connection);
569 .map(TimelineEventAddedEvent::new)
570 .forEach(caseDB::fireTSKEvent);
589 Set<TimelineEvent> addEventsForNewFileQuiet(AbstractFile file, CaseDbConnection connection)
throws TskCoreException {
592 Map<TimelineEventType, Long> timeMap = ImmutableMap.of(TimelineEventType.FILE_CREATED, file.getCrtime(),
593 TimelineEventType.FILE_ACCESSED, file.getAtime(),
594 TimelineEventType.FILE_CHANGED, file.getCtime(),
595 TimelineEventType.FILE_MODIFIED, file.getMtime());
601 if (Collections.max(timeMap.values()) <= 0) {
602 return Collections.emptySet();
605 String description = file.getParentPath() + file.getName();
606 long fileObjId = file.getId();
607 Set<TimelineEvent> events =
new HashSet<>();
610 Long descriptionID = addEventDescription(file.getDataSourceObjectId(), fileObjId, null,
611 description, null, null,
false,
false, connection);
613 if(descriptionID == null) {
614 descriptionID = getEventDescription(file.getDataSourceObjectId(), fileObjId, null, description, connection);
616 if(descriptionID != null) {
617 for (Map.Entry<TimelineEventType, Long> timeEntry : timeMap.entrySet()) {
618 Long time = timeEntry.getValue();
619 if (time > 0 && time < MAX_TIMESTAMP_TO_ADD) {
620 TimelineEventType type = timeEntry.getKey();
621 long eventID = addEventWithExistingDescription(time, type, descriptionID, connection);
628 events.add(
new TimelineEvent(eventID, descriptionID, fileObjId, null, time, type,
629 description, null, null,
false,
false));
631 if (time >= MAX_TIMESTAMP_TO_ADD) {
632 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()));
637 throw new TskCoreException(String.format(
"Failed to get event description for file id = %d", fileObjId));
639 }
catch (DuplicateException dupEx) {
640 logger.log(Level.SEVERE,
"Attempt to make file event duplicate.", dupEx);
661 Set<TimelineEvent> addArtifactEvents(BlackboardArtifact artifact)
throws TskCoreException {
662 Set<TimelineEvent> newEvents =
new HashSet<>();
669 if (artifact.getArtifactTypeID() == TSK_TL_EVENT.getTypeID()) {
670 TimelineEventType eventType;
671 BlackboardAttribute attribute = artifact.getAttribute(
new BlackboardAttribute.Type(TSK_TL_EVENT_TYPE));
672 if (attribute == null) {
673 eventType = TimelineEventType.STANDARD_ARTIFACT_CATCH_ALL;
675 long eventTypeID = attribute.getValueLong();
676 eventType = eventTypeIDMap.getOrDefault(eventTypeID, TimelineEventType.STANDARD_ARTIFACT_CATCH_ALL);
681 addArtifactEvent(((TimelineEventArtifactTypeImpl) TimelineEventType.STANDARD_ARTIFACT_CATCH_ALL).makeEventDescription(artifact), eventType, artifact)
682 .ifPresent(newEvents::add);
683 }
catch (DuplicateException ex) {
684 logger.log(Level.SEVERE, getDuplicateExceptionMessage(artifact,
"Attempt to make a timeline event artifact duplicate"), ex);
691 Set<TimelineEventArtifactTypeImpl> eventTypesForArtifact = eventTypeIDMap.values().stream()
692 .filter(TimelineEventArtifactTypeImpl.class::isInstance)
693 .map(TimelineEventArtifactTypeImpl.class::cast)
694 .filter(eventType -> eventType.getArtifactTypeID() == artifact.getArtifactTypeID())
695 .collect(Collectors.toSet());
697 boolean duplicateExists =
false;
698 for (TimelineEventArtifactTypeImpl eventType : eventTypesForArtifact) {
700 addArtifactEvent(eventType.makeEventDescription(artifact), eventType, artifact)
701 .ifPresent(newEvents::add);
702 }
catch (DuplicateException ex) {
703 duplicateExists =
true;
704 logger.log(Level.SEVERE, getDuplicateExceptionMessage(artifact,
"Attempt to make artifact event duplicate"), ex);
709 if (!duplicateExists && newEvents.isEmpty()) {
711 addOtherEventDesc(artifact).ifPresent(newEvents::add);
712 }
catch (DuplicateException ex) {
713 logger.log(Level.SEVERE, getDuplicateExceptionMessage(artifact,
"Attempt to make 'other' artifact event duplicate"), ex);
718 .map(TimelineEventAddedEvent::new)
719 .forEach(caseDB::fireTSKEvent);
735 private String getDuplicateExceptionMessage(BlackboardArtifact artifact, String error) {
736 String artifactIDStr = null;
737 String sourceStr = null;
739 if (artifact != null) {
740 artifactIDStr = Long.toString(artifact.getId());
743 sourceStr = artifact.getAttributes().stream()
744 .filter(attr -> attr != null && attr.getSources() != null && !attr.getSources().isEmpty())
745 .map(attr -> String.join(
",", attr.getSources()))
748 }
catch (TskCoreException ex) {
749 logger.log(Level.WARNING, String.format(
"Could not fetch artifacts for artifact id: %d.", artifact.getId()), ex);
753 artifactIDStr = (artifactIDStr == null) ?
"<null>" : artifactIDStr;
754 sourceStr = (sourceStr == null) ?
"<null>" : sourceStr;
756 return String.format(
"%s (artifactID=%s, Source=%s).", error, artifactIDStr, sourceStr);
770 private Optional<TimelineEvent> addOtherEventDesc(BlackboardArtifact artifact)
throws TskCoreException, DuplicateException {
771 if (artifact == null) {
772 return Optional.empty();
775 Long timeVal = artifact.getAttributes().stream()
776 .filter((attr) -> attr.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME)
777 .map(attr -> attr.getValueLong())
781 if (timeVal == null) {
782 return Optional.empty();
785 String description = String.format(
"%s: %d", artifact.getDisplayName(), artifact.getId());
787 TimelineEventDescriptionWithTime evtWDesc =
new TimelineEventDescriptionWithTime(timeVal, description, description, description);
789 TimelineEventType evtType = (ARTIFACT_TYPE_IDS.contains(artifact.getArtifactTypeID()))
790 ? TimelineEventType.STANDARD_ARTIFACT_CATCH_ALL
791 : TimelineEventType.CUSTOM_ARTIFACT_CATCH_ALL;
793 return addArtifactEvent(evtWDesc, evtType, artifact);
816 long dataSourceId,
long contentId, Long artifactId,
long time,
817 boolean hashHit,
boolean tagged,
819 )
throws TskCoreException {
822 Long descriptionID = addEventDescription(dataSourceId, contentId, artifactId,
823 longDesc, medDesc, shortDesc, hashHit, tagged, trans.getConnection());
825 if (descriptionID == null) {
826 descriptionID = getEventDescription(dataSourceId, contentId, artifactId, longDesc, trans.getConnection());
828 if (descriptionID != null) {
829 long eventID = addEventWithExistingDescription(time, eventType, descriptionID, trans.getConnection());
831 longDesc, medDesc, shortDesc, hashHit, tagged);
836 throw new TskCoreException(MessageFormat.format(
837 "Failed to get event description for [shortDesc: {0}, dataSourceId: {1}, contentId: {2}, artifactId: {3}]",
838 shortDesc, dataSourceId, contentId, artifactId == null ?
"<null>" : artifactId));
840 }
catch (DuplicateException dupEx) {
841 logger.log(Level.WARNING,
"Attempt to make duplicate", dupEx);
861 private Optional<TimelineEvent> addArtifactEvent(TimelineEventDescriptionWithTime eventPayload,
866 if (eventPayload == null || eventType.isDeprecated()) {
867 return Optional.empty();
869 long time = eventPayload.getTime();
871 if (time <= 0 || time >= MAX_TIMESTAMP_TO_ADD) {
872 if (time >= MAX_TIMESTAMP_TO_ADD) {
873 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()));
875 return Optional.empty();
877 String fullDescription = eventPayload.getDescription(TimelineLevelOfDetail.HIGH);
878 String medDescription = eventPayload.getDescription(TimelineLevelOfDetail.MEDIUM);
879 String shortDescription = eventPayload.getDescription(TimelineLevelOfDetail.LOW);
880 long artifactID = artifact.getArtifactID();
881 long fileObjId = artifact.getObjectID();
882 Long dataSourceObjectID = artifact.getDataSourceObjectID();
884 if(dataSourceObjectID == null) {
885 logger.log(Level.SEVERE, String.format(
"Failed to create timeline event for artifact (%d), artifact data source was null"), artifact.getId());
886 return Optional.empty();
890 boolean hasHashHits =
false;
893 hasHashHits = isNotEmpty(file.getHashSetNames());
899 try (CaseDbConnection connection = caseDB.getConnection();) {
901 Long descriptionID = addEventDescription(dataSourceObjectID, fileObjId, artifactID,
902 fullDescription, medDescription, shortDescription,
903 hasHashHits, tagged, connection);
905 if(descriptionID == null) {
906 descriptionID = getEventDescription(dataSourceObjectID, fileObjId, artifactID,
907 fullDescription, connection);
910 if(descriptionID != null) {
911 long eventID = addEventWithExistingDescription(time, eventType, descriptionID, connection);
913 event =
new TimelineEvent(eventID, dataSourceObjectID, fileObjId, artifactID,
914 time, eventType, fullDescription, medDescription, shortDescription,
915 hasHashHits, tagged);
917 throw new TskCoreException(String.format(
"Failed to get event description for file id = %d, artifactId %d", fileObjId, artifactID));
923 return Optional.of(event);
926 private long addEventWithExistingDescription(Long time, TimelineEventType type,
long descriptionID, CaseDbConnection connection)
throws TskCoreException, DuplicateException {
927 String tableValuesClause
928 =
"tsk_events ( event_type_id, event_description_id , time) VALUES (?, ?, ?)";
930 String insertEventSql = getSqlIgnoreConflict(tableValuesClause);
934 PreparedStatement insertRowStmt = connection.getPreparedStatement(insertEventSql, Statement.RETURN_GENERATED_KEYS);
935 insertRowStmt.clearParameters();
936 insertRowStmt.setLong(1, type.getTypeID());
937 insertRowStmt.setLong(2, descriptionID);
938 insertRowStmt.setLong(3, time);
939 int row = insertRowStmt.executeUpdate();
942 throw new DuplicateException(String.format(
"An event already exists in the event table for this item [time: %s, type: %s, description: %d].",
943 time == null ?
"<null>" : Long.toString(time),
944 type == null ?
"<null>" : type.toString(),
948 try (ResultSet generatedKeys = insertRowStmt.getGeneratedKeys();) {
949 if (generatedKeys.next()) {
950 return generatedKeys.getLong(1);
952 throw new DuplicateException(String.format(
"An event already exists in the event table for this item [time: %s, type: %s, description: %d].",
953 time == null ?
"<null>" : Long.toString(time),
954 type == null ?
"<null>" : type.toString(),
958 }
catch (SQLException ex) {
959 throw new TskCoreException(
"Failed to insert event for existing description.", ex);
965 private Map<Long, Long> getEventAndDescriptionIDs(CaseDbConnection conn,
long contentObjID,
boolean includeArtifacts)
throws TskCoreException {
966 return getEventAndDescriptionIDsHelper(conn, contentObjID, (includeArtifacts ?
"" :
" AND artifact_id IS NULL"));
969 private Map<Long, Long> getEventAndDescriptionIDs(CaseDbConnection conn,
long contentObjID, Long artifactID)
throws TskCoreException {
970 return getEventAndDescriptionIDsHelper(conn, contentObjID,
" AND artifact_id = " + artifactID);
973 private Map<Long, Long> getEventAndDescriptionIDsHelper(CaseDbConnection con,
long fileObjID, String artifactClause)
throws TskCoreException {
975 Map<Long, Long> eventIDToDescriptionIDs =
new HashMap<>();
976 String sql =
"SELECT event_id, tsk_events.event_description_id"
977 +
" FROM tsk_events "
978 +
" LEFT JOIN tsk_event_descriptions ON ( tsk_events.event_description_id = tsk_event_descriptions.event_description_id )"
979 +
" WHERE content_obj_id = " + fileObjID
981 try (Statement selectStmt = con.createStatement(); ResultSet executeQuery = selectStmt.executeQuery(sql);) {
982 while (executeQuery.next()) {
983 eventIDToDescriptionIDs.put(executeQuery.getLong(
"event_id"), executeQuery.getLong(
"event_description_id"));
985 }
catch (SQLException ex) {
986 throw new TskCoreException(
"Error getting event description ids for object id = " + fileObjID, ex);
988 return eventIDToDescriptionIDs;
1010 try (CaseDbConnection conn = caseDB.getConnection()) {
1011 Map<Long, Long> eventIDs = getEventAndDescriptionIDs(conn, content.getId(),
false);
1012 updateEventSourceTaggedFlag(conn, eventIDs.values(), 1);
1013 return eventIDs.keySet();
1039 try (CaseDbConnection conn = caseDB.getConnection()) {
1041 Map<Long, Long> eventIDs = getEventAndDescriptionIDs(conn, content.getId(),
false);
1042 updateEventSourceTaggedFlag(conn, eventIDs.values(), 0);
1043 return eventIDs.keySet();
1045 return Collections.emptySet();
1065 try (CaseDbConnection conn = caseDB.getConnection()) {
1066 Map<Long, Long> eventIDs = getEventAndDescriptionIDs(conn, artifact.getObjectID(), artifact.getArtifactID());
1067 updateEventSourceTaggedFlag(conn, eventIDs.values(), 1);
1068 return eventIDs.keySet();
1088 try (CaseDbConnection conn = caseDB.getConnection()) {
1090 Map<Long, Long> eventIDs = getEventAndDescriptionIDs(conn, artifact.getObjectID(), artifact.getArtifactID());
1091 updateEventSourceTaggedFlag(conn, eventIDs.values(), 0);
1092 return eventIDs.keySet();
1094 return Collections.emptySet();
1101 private void updateEventSourceTaggedFlag(CaseDbConnection conn, Collection<Long> eventDescriptionIDs,
int flagValue)
throws TskCoreException {
1102 if (eventDescriptionIDs.isEmpty()) {
1106 String sql =
"UPDATE tsk_event_descriptions SET tagged = " + flagValue +
" WHERE event_description_id IN (" + buildCSVString(eventDescriptionIDs) +
")";
1107 try (Statement updateStatement = conn.createStatement()) {
1108 updateStatement.executeUpdate(sql);
1109 }
catch (SQLException ex) {
1110 throw new TskCoreException(
"Error marking content events tagged: " + sql, ex);
1130 try (CaseDbConnection con = caseDB.getConnection(); Statement updateStatement = con.createStatement();) {
1131 Map<Long, Long> eventIDs = getEventAndDescriptionIDs(con, content.getId(),
true);
1132 if (!eventIDs.isEmpty()) {
1133 String sql =
"UPDATE tsk_event_descriptions SET hash_hit = 1" +
" WHERE event_description_id IN (" + buildCSVString(eventIDs.values()) +
")";
1135 updateStatement.executeUpdate(sql);
1136 return eventIDs.keySet();
1137 }
catch (SQLException ex) {
1138 throw new TskCoreException(
"Error setting hash_hit of events.", ex);
1141 return eventIDs.keySet();
1143 }
catch (SQLException ex) {
1144 throw new TskCoreException(
"Error setting hash_hit of events.", ex);
1174 long adjustedEndTime = Objects.equals(startTime, endTime) ? endTime + 1 : endTime;
1178 String queryString =
"SELECT count(DISTINCT tsk_events.event_id) AS count, " + typeColumn
1179 +
" FROM " + getAugmentedEventsTablesSQL(filter)
1180 +
" WHERE time >= " + startTime +
" AND time < " + adjustedEndTime +
" AND " + getSQLWhere(filter)
1181 +
" GROUP BY " + typeColumn;
1184 try (CaseDbConnection con = caseDB.getConnection();
1185 Statement stmt = con.createStatement();
1186 ResultSet results = stmt.executeQuery(queryString);) {
1187 Map<TimelineEventType, Long> typeMap =
new HashMap<>();
1188 while (results.next()) {
1189 int eventTypeID = results.getInt(typeColumn);
1191 .orElseThrow(() -> newEventTypeMappingException(eventTypeID));
1193 typeMap.put(eventType, results.getLong(
"count"));
1196 }
catch (SQLException ex) {
1197 throw new TskCoreException(
"Error getting count of events from db: " + queryString, ex);
1203 private static TskCoreException newEventTypeMappingException(
int eventTypeID) {
1204 return new TskCoreException(
"Error mapping event type id " + eventTypeID +
" to EventType.");
1220 static private String getAugmentedEventsTablesSQL(TimelineFilter.RootFilter filter) {
1221 TimelineFilter.FileTypesFilter fileTypesFitler = filter.getFileTypesFilter();
1222 boolean needsMimeTypes = fileTypesFitler != null && fileTypesFitler.hasSubFilters();
1224 return getAugmentedEventsTablesSQL(needsMimeTypes);
1241 static private String getAugmentedEventsTablesSQL(
boolean needMimeTypes) {
1254 return "( SELECT event_id, time, tsk_event_descriptions.data_source_obj_id, content_obj_id, artifact_id, "
1255 +
" full_description, med_description, short_description, tsk_events.event_type_id, super_type_id,"
1256 +
" hash_hit, tagged "
1257 + (needMimeTypes ?
", mime_type" :
"")
1258 +
" FROM tsk_events "
1259 +
" JOIN tsk_event_descriptions ON ( tsk_event_descriptions.event_description_id = tsk_events.event_description_id)"
1260 +
" JOIN tsk_event_types ON (tsk_events.event_type_id = tsk_event_types.event_type_id ) "
1261 + (needMimeTypes ?
" LEFT OUTER JOIN tsk_files "
1262 +
" ON (tsk_event_descriptions.content_obj_id = tsk_files.obj_id)"
1264 +
") AS tsk_events";
1274 private static int booleanToInt(
boolean value) {
1275 return value ? 1 : 0;
1278 private static boolean intToBoolean(
int value) {
1295 List<TimelineEvent> events =
new ArrayList<>();
1297 Long startTime = timeRange.getStartMillis() / 1000;
1298 Long endTime = timeRange.getEndMillis() / 1000;
1300 if (Objects.equals(startTime, endTime)) {
1304 if (filter == null) {
1308 if (endTime < startTime) {
1313 String querySql =
"SELECT time, content_obj_id, data_source_obj_id, artifact_id, "
1317 +
" event_type_id, super_type_id, "
1318 +
" full_description, med_description, short_description "
1319 +
" FROM " + getAugmentedEventsTablesSQL(filter)
1320 +
" WHERE time >= " + startTime +
" AND time < " + endTime +
" AND " + getSQLWhere(filter)
1324 try (CaseDbConnection con = caseDB.getConnection();
1325 Statement stmt = con.createStatement();
1326 ResultSet resultSet = stmt.executeQuery(querySql);) {
1328 while (resultSet.next()) {
1329 int eventTypeID = resultSet.getInt(
"event_type_id");
1331 ->
new TskCoreException(
"Error mapping event type id " + eventTypeID +
"to EventType."));
1334 resultSet.getLong(
"event_id"),
1335 resultSet.getLong(
"data_source_obj_id"),
1336 resultSet.getLong(
"content_obj_id"),
1337 resultSet.getLong(
"artifact_id"),
1338 resultSet.getLong(
"time"),
1340 resultSet.getString(
"full_description"),
1341 resultSet.getString(
"med_description"),
1342 resultSet.getString(
"short_description"),
1343 resultSet.getInt(
"hash_hit") != 0,
1344 resultSet.getInt(
"tagged") != 0);
1349 }
catch (SQLException ex) {
1350 throw new TskCoreException(
"Error getting events from db: " + querySql, ex);
1365 private static String typeColumnHelper(
final boolean useSubTypes) {
1366 return useSubTypes ?
"event_type_id" :
"super_type_id";
1377 String getSQLWhere(TimelineFilter.RootFilter filter) {
1380 if (filter == null) {
1381 return getTrueLiteral();
1383 result = filter.getSQLWhere(
this);
1400 private String getSqlIgnoreConflict(String insertTableValues)
throws TskCoreException {
1403 return "INSERT INTO " + insertTableValues +
" ON CONFLICT DO NOTHING";
1405 return "INSERT OR IGNORE INTO " + insertTableValues;
1407 throw new TskCoreException(
"Unknown DB Type: " + caseDB.
getDatabaseType().name());
1411 private String getTrueLiteral() {
1418 throw new UnsupportedOperationException(
"Unsupported DB type: " + caseDB.
getDatabaseType().name());
1436 this.addedEvent = event;
1443 private static class DuplicateException
extends Exception {
1445 private static final long serialVersionUID = 1L;
1452 DuplicateException(String message) {
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()
int DEPRECATED_OTHER_EVENT_ID
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)
void acquireSingleUserCaseReadLock()
List< ContentTag > getContentTagsByContent(Content content)
Optional< TimelineEventType > getEventType(long eventTypeID)
TimelineEvent addTimelineEvent(TimelineEventType eventType, String shortDesc, String medDesc, String longDesc, long dataSourceId, long contentId, Long artifactId, long time, boolean hashHit, boolean tagged, CaseDbTransaction trans)
TimelineEventType FILE_CHANGED
Set< Long > updateEventsForArtifactTagAdded(BlackboardArtifact artifact)