diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 16df4a34f8a1e871b835676df9b201fe820fa7d4..8b68e7e8c8e131fe7a8b684416ead5b58e605305 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -95,6 +95,7 @@ import org.sqlite.SQLiteDataSource; import org.sqlite.SQLiteJDBCLoader; import org.sleuthkit.datamodel.ContentStream.ContentProvider; +import org.sleuthkit.datamodel.TimelineManager.TimelineEventAddedEvent; /** * Represents the case database with methods that provide abstractions for @@ -13940,6 +13941,7 @@ public static final class CaseDbTransaction { // Score changes are stored as a map keyed by objId to prevent duplicates. private Map<Long, ScoreChange> scoreChangeMap = new HashMap<>(); private List<Host> hostsAdded = new ArrayList<>(); + private List<TimelineEventAddedEvent> timelineEvents = new ArrayList<>(); private List<OsAccount> accountsChanged = new ArrayList<>(); private List<OsAccount> accountsAdded = new ArrayList<>(); private List<TskEvent.MergedAccountsPair> accountsMerged = new ArrayList<>(); @@ -13987,6 +13989,16 @@ CaseDbConnection getConnection() { void registerScoreChange(ScoreChange scoreChange) { scoreChangeMap.put(scoreChange.getObjectId(), scoreChange); } + + /** + * Register timeline event to be fired when transaction finishes. + * @param timelineEvent The timeline event. + */ + void registerTimelineEvent(TimelineEventAddedEvent timelineEvent) { + if (timelineEvent != null) { + timelineEvents.add(timelineEvent); + } + } /** * Saves a host that has been added as a part of this transaction. @@ -14085,6 +14097,11 @@ public void commit() throws TskCoreException { sleuthkitCase.fireTSKEvent(new TskEvent.AggregateScoresChangedEvent(entry.getKey(), ImmutableSet.copyOf(entry.getValue()))); } } + if (!timelineEvents.isEmpty()) { + for (TimelineEventAddedEvent evt : timelineEvents) { + sleuthkitCase.fireTSKEvent(evt); + } + } if (!hostsAdded.isEmpty()) { sleuthkitCase.fireTSKEvent(new TskEvent.HostsAddedTskEvent(hostsAdded)); } diff --git a/bindings/java/src/org/sleuthkit/datamodel/TimelineManager.java b/bindings/java/src/org/sleuthkit/datamodel/TimelineManager.java index cf560705d3ad91e16ead90f9087e334191a62ec2..2970a49deb581dd69a04b743ac563ea7ccf132d0 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/TimelineManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TimelineManager.java @@ -48,6 +48,7 @@ import static org.sleuthkit.datamodel.CollectionUtils.isNotEmpty; import static org.sleuthkit.datamodel.CommManagerSqlStringUtils.buildCSVString; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection; +import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; import static org.sleuthkit.datamodel.SleuthkitCase.escapeSingleQuotes; /** @@ -790,6 +791,54 @@ private Optional<TimelineEvent> addOtherEventDesc(BlackboardArtifact artifact) t return addArtifactEvent(evtWDesc, evtType, artifact); } + + + /** + * Adds a timeline event to the database in a transaction. + * @param eventType The event type. + * @param shortDesc The short description. + * @param medDesc The medium description. + * @param longDesc The long description. + * @param dataSourceId The data source id of the event. + * @param contentId The content id of the event. + * @param artifactId The artifact id of the event (can be null). + * @param time Unix epoch offset time of the event in seconds. + * @param hashHit True if a hash hit. + * @param tagged True if tagged. + * @param trans The transaction. + * @return The added event. + * @throws TskCoreException + */ + @Beta + public TimelineEvent addTimelineEvent( + TimelineEventType eventType, String shortDesc, String medDesc, String longDesc, + long dataSourceId, long contentId, Long artifactId, long time, + boolean hashHit, boolean tagged, + CaseDbTransaction trans + ) throws TskCoreException { + caseDB.acquireSingleUserCaseWriteLock(); + try { + Long descriptionID = addEventDescription(dataSourceId, contentId, artifactId, + longDesc, medDesc, shortDesc, hashHit, tagged, trans.getConnection()); + + if (descriptionID == null) { + descriptionID = getEventDescription(dataSourceId, contentId, artifactId, longDesc, trans.getConnection()); + } + if (descriptionID != null) { + long eventID = addEventWithExistingDescription(time, eventType, descriptionID, trans.getConnection()); + return new TimelineEvent(eventID, descriptionID, contentId, artifactId, time, eventType, + longDesc, medDesc, shortDesc, hashHit, tagged); + } else { + // GVDTODO + throw new TskCoreException(String.format("Failed to get event description")); + } + } catch (DuplicateException dupEx) { + logger.log(Level.SEVERE, "Attempt to make file event duplicate.", dupEx); + return null; + } finally { + caseDB.releaseSingleUserCaseWriteLock(); + } + } /** * Add an event of the given type from the given artifact to the database.