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 {
 
  490                 Set<TimelineEvent> events = addEventsForNewFileQuiet(file, connection);
 
  492                                 .map(TimelineEventAddedEvent::new)
 
  493                                 .forEach(caseDB::fireTSKEvent);
 
  512         Set<TimelineEvent> addEventsForNewFileQuiet(AbstractFile file, CaseDbConnection connection) 
throws TskCoreException {
 
  514                 Map<TimelineEventType, Long> timeMap = ImmutableMap.of(TimelineEventType.FILE_CREATED, file.getCrtime(),
 
  515                                 TimelineEventType.FILE_ACCESSED, file.getAtime(),
 
  516                                 TimelineEventType.FILE_CHANGED, file.getCtime(),
 
  517                                 TimelineEventType.FILE_MODIFIED, file.getMtime());
 
  523                 if (Collections.max(timeMap.values()) <= 0) {
 
  524                         return Collections.emptySet();
 
  527                 String description = file.getParentPath() + file.getName();
 
  528                 long fileObjId = file.getId();
 
  529                 Set<TimelineEvent> events = 
new HashSet<>();
 
  532                         long descriptionID = addEventDescription(file.getDataSourceObjectId(), fileObjId, null,
 
  533                                         description, null, null, 
false, 
false, connection);
 
  535                         for (Map.Entry<TimelineEventType, Long> timeEntry : timeMap.entrySet()) {
 
  536                                 Long time = timeEntry.getValue();
 
  537                                 if (time > 0 && time < MAX_TIMESTAMP_TO_ADD) {
 
  538                                         TimelineEventType type = timeEntry.getKey();
 
  539                                         long eventID = addEventWithExistingDescription(time, type, descriptionID, connection);
 
  546                                         events.add(
new TimelineEvent(eventID, descriptionID, fileObjId, null, time, type,
 
  547                                                         description, null, null, 
false, 
false));
 
  549                                         if (time >= MAX_TIMESTAMP_TO_ADD) {
 
  550                                                 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()));
 
  575         Set<TimelineEvent> addArtifactEvents(BlackboardArtifact artifact) 
throws TskCoreException {
 
  576                 Set<TimelineEvent> newEvents = 
new HashSet<>();
 
  583                 if (artifact.getArtifactTypeID() == TSK_TL_EVENT.getTypeID()) {
 
  584                         TimelineEventType eventType;
 
  585                         BlackboardAttribute attribute = artifact.getAttribute(
new BlackboardAttribute.Type(TSK_TL_EVENT_TYPE));
 
  586                         if (attribute == null) {
 
  587                                 eventType = TimelineEventType.OTHER;
 
  589                                 long eventTypeID = attribute.getValueLong();
 
  590                                 eventType = eventTypeIDMap.getOrDefault(eventTypeID, TimelineEventType.OTHER);
 
  594                         addArtifactEvent(((TimelineEventArtifactTypeImpl) TimelineEventType.OTHER)::makeEventDescription, eventType, artifact)
 
  595                                         .ifPresent(newEvents::add);
 
  601                         Set<TimelineEventArtifactTypeImpl> eventTypesForArtifact = eventTypeIDMap.values().stream()
 
  602                                         .filter(TimelineEventArtifactTypeImpl.class::isInstance)
 
  603                                         .map(TimelineEventArtifactTypeImpl.class::cast)
 
  604                                         .filter(eventType -> eventType.getArtifactTypeID() == artifact.getArtifactTypeID())
 
  605                                         .collect(Collectors.toSet());
 
  607                         for (TimelineEventArtifactTypeImpl eventType : eventTypesForArtifact) {
 
  608                                 addArtifactEvent(eventType::makeEventDescription, eventType, artifact)
 
  609                                                 .ifPresent(newEvents::add);
 
  613                                 .map(TimelineEventAddedEvent::new)
 
  614                                 .forEach(caseDB::fireTSKEvent);
 
  635         private Optional<TimelineEvent> addArtifactEvent(TSKCoreCheckedFunction<BlackboardArtifact, TimelineEventDescriptionWithTime> payloadExtractor,
 
  636                         TimelineEventType eventType, BlackboardArtifact artifact) 
throws TskCoreException {
 
  637                 TimelineEventDescriptionWithTime eventPayload = payloadExtractor.apply(artifact);
 
  638                 if (eventPayload == null) {
 
  639                         return Optional.empty();
 
  641                 long time = eventPayload.getTime();
 
  643                 if (time <= 0 || time >= MAX_TIMESTAMP_TO_ADD) {
 
  644                         if (time >= MAX_TIMESTAMP_TO_ADD) {
 
  645                                 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()));
 
  647                         return Optional.empty();
 
  649                 String fullDescription = eventPayload.getDescription(TimelineLevelOfDetail.HIGH);
 
  650                 String medDescription = eventPayload.getDescription(TimelineLevelOfDetail.MEDIUM);
 
  651                 String shortDescription = eventPayload.getDescription(TimelineLevelOfDetail.LOW);
 
  652                 long artifactID = artifact.getArtifactID();
 
  653                 long fileObjId = artifact.getObjectID();
 
  654                 long dataSourceObjectID = artifact.getDataSourceObjectID();
 
  657                 boolean hasHashHits = 
false;
 
  660                         hasHashHits = isNotEmpty(file.getHashSetNames());
 
  666                 try (CaseDbConnection connection = caseDB.getConnection();) {
 
  668                         long descriptionID = addEventDescription(dataSourceObjectID, fileObjId, artifactID,
 
  669                                         fullDescription, medDescription, shortDescription,
 
  670                                         hasHashHits, tagged, connection);
 
  672                         long eventID = addEventWithExistingDescription(time, eventType, descriptionID, connection);
 
  674                         event = 
new TimelineEvent(eventID, dataSourceObjectID, fileObjId, artifactID,
 
  675                                         time, eventType, fullDescription, medDescription, shortDescription,
 
  676                                         hasHashHits, tagged);
 
  681                 return Optional.of(event);
 
  684         private long addEventWithExistingDescription(Long time, TimelineEventType type, 
long descriptionID, CaseDbConnection connection) 
throws TskCoreException {
 
  685                 String insertEventSql
 
  686                                 = 
"INSERT INTO tsk_events ( event_type_id, event_description_id , time) " 
  687                                 + 
" VALUES (" + type.getTypeID() + 
", " + descriptionID + 
", " + time + 
")";
 
  690                 try (Statement insertRowStmt = connection.createStatement();) {
 
  691                         connection.executeUpdate(insertRowStmt, insertEventSql, PreparedStatement.RETURN_GENERATED_KEYS);
 
  693                         try (ResultSet generatedKeys = insertRowStmt.getGeneratedKeys();) {
 
  694                                 generatedKeys.next();
 
  695                                 return generatedKeys.getLong(1);
 
  697                 } 
catch (SQLException ex) {
 
  698                         throw new TskCoreException(
"Failed to insert event for existing description.", ex); 
 
  704         static private String quotePreservingNull(String value) {
 
  705                 return isNull(value) ? 
" NULL " : 
"'" + escapeSingleQuotes(value) + 
"'";
 
  708         private Map<Long, Long> getEventAndDescriptionIDs(CaseDbConnection conn, 
long contentObjID, 
boolean includeArtifacts) 
throws TskCoreException {
 
  709                 return getEventAndDescriptionIDsHelper(conn, contentObjID, (includeArtifacts ? 
"" : 
" AND artifact_id IS NULL"));
 
  712         private Map<Long, Long> getEventAndDescriptionIDs(CaseDbConnection conn, 
long contentObjID, Long artifactID) 
throws TskCoreException {
 
  713                 return getEventAndDescriptionIDsHelper(conn, contentObjID, 
" AND artifact_id = " + artifactID);
 
  716         private Map<Long, Long> getEventAndDescriptionIDsHelper(CaseDbConnection con, 
long fileObjID, String artifactClause) 
throws TskCoreException {
 
  718                 Map<Long, Long> eventIDToDescriptionIDs = 
new HashMap<>();
 
  719                 String sql = 
"SELECT event_id, tsk_events.event_description_id" 
  720                                 + 
" FROM tsk_events " 
  721                                 + 
" LEFT JOIN tsk_event_descriptions ON ( tsk_events.event_description_id = tsk_event_descriptions.event_description_id )" 
  722                                 + 
" WHERE content_obj_id = " + fileObjID
 
  724                 try (Statement selectStmt = con.createStatement(); ResultSet executeQuery = selectStmt.executeQuery(sql);) {
 
  725                         while (executeQuery.next()) {
 
  726                                 eventIDToDescriptionIDs.put(executeQuery.getLong(
"event_id"), executeQuery.getLong(
"event_description_id")); 
 
  728                 } 
catch (SQLException ex) {
 
  729                         throw new TskCoreException(
"Error getting event description ids for object id = " + fileObjID, ex);
 
  731                 return eventIDToDescriptionIDs;
 
  753                 try (CaseDbConnection conn = caseDB.getConnection()) {
 
  754                         Map<Long, Long> eventIDs = getEventAndDescriptionIDs(conn, content.getId(), 
false);
 
  755                         updateEventSourceTaggedFlag(conn, eventIDs.values(), 1);
 
  756                         return eventIDs.keySet();
 
  782                 try (CaseDbConnection conn = caseDB.getConnection()) {
 
  784                                 Map<Long, Long> eventIDs = getEventAndDescriptionIDs(conn, content.getId(), 
false);
 
  785                                 updateEventSourceTaggedFlag(conn, eventIDs.values(), 0);
 
  786                                 return eventIDs.keySet();
 
  788                                 return Collections.emptySet();
 
  808                 try (CaseDbConnection conn = caseDB.getConnection()) {
 
  809                         Map<Long, Long> eventIDs = getEventAndDescriptionIDs(conn, artifact.getObjectID(), artifact.getArtifactID());
 
  810                         updateEventSourceTaggedFlag(conn, eventIDs.values(), 1);
 
  811                         return eventIDs.keySet();
 
  831                 try (CaseDbConnection conn = caseDB.getConnection()) {
 
  833                                 Map<Long, Long> eventIDs = getEventAndDescriptionIDs(conn, artifact.getObjectID(), artifact.getArtifactID());
 
  834                                 updateEventSourceTaggedFlag(conn, eventIDs.values(), 0);
 
  835                                 return eventIDs.keySet();
 
  837                                 return Collections.emptySet();
 
  844         private void updateEventSourceTaggedFlag(CaseDbConnection conn, Collection<Long> eventDescriptionIDs, 
int flagValue) 
throws TskCoreException {
 
  845                 if (eventDescriptionIDs.isEmpty()) {
 
  849                 String sql = 
"UPDATE tsk_event_descriptions SET tagged = " + flagValue + 
" WHERE event_description_id IN (" + buildCSVString(eventDescriptionIDs) + 
")"; 
 
  850                 try (Statement updateStatement = conn.createStatement()) {
 
  851                         updateStatement.executeUpdate(sql);
 
  852                 } 
catch (SQLException ex) {
 
  853                         throw new TskCoreException(
"Error marking content events tagged: " + sql, ex);
 
  873                 try (CaseDbConnection con = caseDB.getConnection(); Statement updateStatement = con.createStatement();) {
 
  874                         Map<Long, Long> eventIDs = getEventAndDescriptionIDs(con, content.getId(), 
true);
 
  875                         if (! eventIDs.isEmpty()) {
 
  876                                 String sql = 
"UPDATE tsk_event_descriptions SET hash_hit = 1" + 
" WHERE event_description_id IN (" + buildCSVString(eventIDs.values()) + 
")"; 
 
  878                                         updateStatement.executeUpdate(sql); 
 
  879                                         return eventIDs.keySet();
 
  880                                 } 
catch (SQLException ex) {
 
  884                                 return eventIDs.keySet();
 
  886                 } 
catch (SQLException ex) {
 
  917                 long adjustedEndTime = Objects.equals(startTime, endTime) ? endTime + 1 : endTime;
 
  921                 String queryString = 
"SELECT count(DISTINCT tsk_events.event_id) AS count, " + typeColumn
 
  922                                 + 
" FROM " + getAugmentedEventsTablesSQL(filter)
 
  923                                 + 
" WHERE time >= " + startTime + 
" AND time < " + adjustedEndTime + 
" AND " + getSQLWhere(filter) 
 
  924                                 + 
" GROUP BY " + typeColumn; 
 
  927                 try (CaseDbConnection con = caseDB.getConnection();
 
  928                                 Statement stmt = con.createStatement();
 
  929                                 ResultSet results = stmt.executeQuery(queryString);) {
 
  930                         Map<TimelineEventType, Long> typeMap = 
new HashMap<>();
 
  931                         while (results.next()) {
 
  932                                 int eventTypeID = results.getInt(typeColumn);
 
  934                                                 .orElseThrow(() -> newEventTypeMappingException(eventTypeID));
 
  936                                 typeMap.put(eventType, results.getLong(
"count")); 
 
  939                 } 
catch (SQLException ex) {
 
  940                         throw new TskCoreException(
"Error getting count of events from db: " + queryString, ex); 
 
  946         private static TskCoreException newEventTypeMappingException(
int eventTypeID) {
 
  947                 return new TskCoreException(
"Error mapping event type id " + eventTypeID + 
" to EventType.");
 
  963         static private String getAugmentedEventsTablesSQL(TimelineFilter.RootFilter filter) {
 
  964                 TimelineFilter.FileTypesFilter fileTypesFitler = filter.getFileTypesFilter();
 
  965                 boolean needsMimeTypes = fileTypesFitler != null && fileTypesFitler.hasSubFilters();
 
  967                 return getAugmentedEventsTablesSQL(needsMimeTypes);
 
  984         static private String getAugmentedEventsTablesSQL(
boolean needMimeTypes) {
 
  997                 return "( SELECT event_id, time, tsk_event_descriptions.data_source_obj_id, content_obj_id, artifact_id, " 
  998                                 + 
" full_description, med_description, short_description, tsk_events.event_type_id, super_type_id," 
  999                                 + 
" hash_hit, tagged " 
 1000                                 + (needMimeTypes ? 
", mime_type" : 
"")
 
 1001                                 + 
" FROM tsk_events " 
 1002                                 + 
" JOIN tsk_event_descriptions ON ( tsk_event_descriptions.event_description_id = tsk_events.event_description_id)" 
 1003                                 + 
" JOIN tsk_event_types ON (tsk_events.event_type_id = tsk_event_types.event_type_id )  " 
 1004                                 + (needMimeTypes ? 
" LEFT OUTER JOIN tsk_files " 
 1005                                                 + 
"     ON (tsk_event_descriptions.content_obj_id = tsk_files.obj_id)" 
 1007                                 + 
")  AS tsk_events";
 
 1017         private static int booleanToInt(
boolean value) {
 
 1018                 return value ? 1 : 0;
 
 1021         private static boolean intToBoolean(
int value) {
 
 1038                 List<TimelineEvent> events = 
new ArrayList<>();
 
 1040                 Long startTime = timeRange.getStartMillis() / 1000;
 
 1041                 Long endTime = timeRange.getEndMillis() / 1000;
 
 1043                 if (Objects.equals(startTime, endTime)) {
 
 1047                 if (filter == null) {
 
 1051                 if (endTime < startTime) {
 
 1056                 String querySql = 
"SELECT time, content_obj_id, data_source_obj_id, artifact_id, "  
 1060                                 + 
" event_type_id, super_type_id, " 
 1061                                 + 
" full_description, med_description, short_description "  
 1062                                 + 
" FROM " + getAugmentedEventsTablesSQL(filter) 
 
 1063                                 + 
" WHERE time >= " + startTime + 
" AND time < " + endTime + 
" AND " + getSQLWhere(filter) 
 
 1067                 try (CaseDbConnection con = caseDB.getConnection();
 
 1068                                 Statement stmt = con.createStatement();
 
 1069                                 ResultSet resultSet = stmt.executeQuery(querySql);) {
 
 1071                         while (resultSet.next()) {
 
 1072                                 int eventTypeID = resultSet.getInt(
"event_type_id");
 
 1074                                                 -> 
new TskCoreException(
"Error mapping event type id " + eventTypeID + 
"to EventType."));
 
 1077                                                 resultSet.getLong(
"event_id"), 
 
 1078                                                 resultSet.getLong(
"data_source_obj_id"), 
 
 1079                                                 resultSet.getLong(
"content_obj_id"), 
 
 1080                                                 resultSet.getLong(
"artifact_id"), 
 
 1081                                                 resultSet.getLong(
"time"), 
 
 1083                                                 resultSet.getString(
"full_description"), 
 
 1084                                                 resultSet.getString(
"med_description"), 
 
 1085                                                 resultSet.getString(
"short_description"), 
 
 1086                                                 resultSet.getInt(
"hash_hit") != 0, 
 
 1087                                                 resultSet.getInt(
"tagged") != 0);
 
 1092                 } 
catch (SQLException ex) {
 
 1093                         throw new TskCoreException(
"Error getting events from db: " + querySql, ex); 
 
 1108         private static String typeColumnHelper(
final boolean useSubTypes) {
 
 1109                 return useSubTypes ? 
"event_type_id" : 
"super_type_id"; 
 
 1120         String getSQLWhere(TimelineFilter.RootFilter filter) {
 
 1123                 if (filter == null) {
 
 1124                         return getTrueLiteral();
 
 1126                         result = filter.getSQLWhere(
this);
 
 1132         private String getTrueLiteral() {
 
 1139                                 throw new UnsupportedOperationException(
"Unsupported DB type: " + caseDB.
getDatabaseType().name());
 
 1157                         this.addedEvent = event;
 
 1168         @FunctionalInterface
 
 1169         private interface TSKCoreCheckedFunction<I, O> {
 
 1171                 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)