mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 17:06:16 +00:00
show tag icons on detail view event groups, and tag counts in tooltip;
cleanup; use NbBundle.messages to generate bundle strings
This commit is contained in:
parent
6259869baf
commit
703c780f27
@ -27,37 +27,51 @@ import org.sleuthkit.autopsy.timeline.events.type.EventType;
|
|||||||
import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
|
import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
|
||||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
||||||
|
|
||||||
/** An event that represent a set of other events aggregated together. All the
|
/** Represents a set of other (TimeLineEvent) events aggregated together. All
|
||||||
* sub events should have the same type and matching descriptions at the
|
* the sub events should have the same type and matching descriptions at the
|
||||||
* designated 'zoom level'.
|
* designated 'zoom level'.
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
public class AggregateEvent {
|
public class AggregateEvent {
|
||||||
|
|
||||||
|
/** the smallest time interval containing all the aggregated events */
|
||||||
final private Interval span;
|
final private Interval span;
|
||||||
|
|
||||||
|
/** the type of all the aggregted events */
|
||||||
final private EventType type;
|
final private EventType type;
|
||||||
|
|
||||||
final private Set<Long> eventIDs;
|
/** the common description of all the aggregated events */
|
||||||
|
|
||||||
final private String description;
|
final private String description;
|
||||||
|
|
||||||
|
/** the description level of detail that the events were aggregated at. */
|
||||||
private final DescriptionLOD lod;
|
private final DescriptionLOD lod;
|
||||||
|
|
||||||
|
/** the set of ids of the aggregated events */
|
||||||
|
final private Set<Long> eventIDs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the ids of the subset of aggregated events that have at least one tag
|
||||||
|
* applied to them
|
||||||
|
*/
|
||||||
|
private final Set<Long> tagged;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the ids of the subset of aggregated events that have at least one hash
|
||||||
|
* set hit
|
||||||
|
*/
|
||||||
private final Set<Long> hashHits;
|
private final Set<Long> hashHits;
|
||||||
|
|
||||||
public AggregateEvent(Interval spanningInterval, EventType type, Set<Long> eventIDs, Set<Long> hashHits, String description, DescriptionLOD lod) {
|
public AggregateEvent(Interval spanningInterval, EventType type, Set<Long> eventIDs, Set<Long> hashHits, Set<Long> tagged, String description, DescriptionLOD lod) {
|
||||||
|
|
||||||
this.span = spanningInterval;
|
this.span = spanningInterval;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.hashHits = hashHits;
|
this.hashHits = hashHits;
|
||||||
|
this.tagged = tagged;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
|
||||||
this.eventIDs = eventIDs;
|
this.eventIDs = eventIDs;
|
||||||
this.lod = lod;
|
this.lod = lod;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the actual interval from the first event to the last event */
|
|
||||||
public Interval getSpan() {
|
public Interval getSpan() {
|
||||||
return span;
|
return span;
|
||||||
}
|
}
|
||||||
@ -70,6 +84,10 @@ public class AggregateEvent {
|
|||||||
return Collections.unmodifiableSet(hashHits);
|
return Collections.unmodifiableSet(hashHits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<Long> getEventIDsWithTags() {
|
||||||
|
return Collections.unmodifiableSet(tagged);
|
||||||
|
}
|
||||||
|
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
@ -78,30 +96,33 @@ public class AggregateEvent {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* merge two aggregate events into one new aggregate event.
|
|
||||||
*
|
|
||||||
* @param ag1
|
|
||||||
* @param ag2
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static AggregateEvent merge(AggregateEvent ag1, AggregateEvent ag2) {
|
|
||||||
|
|
||||||
if (ag1.getType() != ag2.getType()) {
|
|
||||||
throw new IllegalArgumentException("aggregate events are not compatible they have different types");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ag1.getDescription().equals(ag2.getDescription())) {
|
|
||||||
throw new IllegalArgumentException("aggregate events are not compatible they have different descriptions");
|
|
||||||
}
|
|
||||||
Sets.SetView<Long> idsUnion = Sets.union(ag1.getEventIDs(), ag2.getEventIDs());
|
|
||||||
Sets.SetView<Long> hashHitsUnion = Sets.union(ag1.getEventIDsWithHashHits(), ag2.getEventIDsWithHashHits());
|
|
||||||
|
|
||||||
return new AggregateEvent(IntervalUtils.span(ag1.span, ag2.span), ag1.getType(), idsUnion, hashHitsUnion, ag1.getDescription(), ag1.lod);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DescriptionLOD getLOD() {
|
public DescriptionLOD getLOD() {
|
||||||
return lod;
|
return lod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* merge two aggregate events into one new aggregate event.
|
||||||
|
*
|
||||||
|
* @param aggEvent1
|
||||||
|
* @param aggEVent2
|
||||||
|
*
|
||||||
|
* @return a new aggregate event that is the result of merging the given
|
||||||
|
* events
|
||||||
|
*/
|
||||||
|
public static AggregateEvent merge(AggregateEvent aggEvent1, AggregateEvent ag2) {
|
||||||
|
|
||||||
|
if (aggEvent1.getType() != ag2.getType()) {
|
||||||
|
throw new IllegalArgumentException("aggregate events are not compatible they have different types");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aggEvent1.getDescription().equals(ag2.getDescription())) {
|
||||||
|
throw new IllegalArgumentException("aggregate events are not compatible they have different descriptions");
|
||||||
|
}
|
||||||
|
Sets.SetView<Long> idsUnion = Sets.union(aggEvent1.getEventIDs(), ag2.getEventIDs());
|
||||||
|
Sets.SetView<Long> hashHitsUnion = Sets.union(aggEvent1.getEventIDsWithHashHits(), ag2.getEventIDsWithHashHits());
|
||||||
|
Sets.SetView<Long> taggedUnion = Sets.union(aggEvent1.getEventIDsWithTags(), ag2.getEventIDsWithTags());
|
||||||
|
|
||||||
|
return new AggregateEvent(IntervalUtils.span(aggEvent1.span, ag2.span), aggEvent1.getType(), idsUnion, hashHitsUnion, taggedUnion, aggEvent1.getDescription(), aggEvent1.lod);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.timeline.events;
|
package org.sleuthkit.autopsy.timeline.events;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import org.sleuthkit.autopsy.timeline.events.type.EventType;
|
import org.sleuthkit.autopsy.timeline.events.type.EventType;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ public class TimeLineEvent {
|
|||||||
private final Long eventID;
|
private final Long eventID;
|
||||||
|
|
||||||
private final Long fileID;
|
private final Long fileID;
|
||||||
|
|
||||||
private final Long time;
|
private final Long time;
|
||||||
|
|
||||||
private final Long artifactID;
|
private final Long artifactID;
|
||||||
@ -42,7 +43,7 @@ public class TimeLineEvent {
|
|||||||
|
|
||||||
private final boolean hashHit;
|
private final boolean hashHit;
|
||||||
|
|
||||||
public TimeLineEvent(Long eventID, Long objID, Long artifactID, Long time, EventType type, String fullDescription, String medDescription, String shortDescription, TskData.FileKnown known, boolean hashHit) {
|
public TimeLineEvent(Long eventID, Long objID, @Nullable Long artifactID, Long time, EventType type, String fullDescription, String medDescription, String shortDescription, TskData.FileKnown known, boolean hashHit) {
|
||||||
this.eventID = eventID;
|
this.eventID = eventID;
|
||||||
this.fileID = objID;
|
this.fileID = objID;
|
||||||
this.artifactID = artifactID;
|
this.artifactID = artifactID;
|
||||||
@ -60,6 +61,7 @@ public class TimeLineEvent {
|
|||||||
return hashHit;
|
return hashHit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public Long getArtifactID() {
|
public Long getArtifactID() {
|
||||||
return artifactID;
|
return artifactID;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
EventsRepository.progressWindow.msg.reinit_db=(re)initializing events database
|
|
||||||
EventsRepository.progressWindow.msg.populateMacEventsFiles=populating mac events for files\:
|
|
||||||
EventsRepository.progressWindow.msg.populateMacEventsFiles2=populating mac events for files\:
|
|
||||||
EventsRepository.progressWindow.msg.commitingDb=committing events db
|
|
||||||
EventsRepository.msgdlg.problem.text=There was a problem populating the timeline. Not all events may be present or accurate. See the log for details.
|
|
||||||
EventsRepository.progressWindow.populatingXevents=populating {0} events
|
|
@ -88,9 +88,10 @@ import org.sqlite.SQLiteJDBCLoader;
|
|||||||
*/
|
*/
|
||||||
public class EventDB {
|
public class EventDB {
|
||||||
|
|
||||||
private PreparedStatement insertHashSetStmt;
|
private PreparedStatement dropEventsTableStmt;
|
||||||
private PreparedStatement insertHashHitStmt;
|
private PreparedStatement dropHashSetHitsTableStmt;
|
||||||
private PreparedStatement selectHashSetStmt;
|
private PreparedStatement dropHashSetsTableStmt;
|
||||||
|
private PreparedStatement dropDBInfoTableStmt;
|
||||||
|
|
||||||
/** enum to represent columns in the events table */
|
/** enum to represent columns in the events table */
|
||||||
enum EventTableColumn {
|
enum EventTableColumn {
|
||||||
@ -105,8 +106,9 @@ public class EventDB {
|
|||||||
FULL_DESCRIPTION("full_description"), // NON-NLS
|
FULL_DESCRIPTION("full_description"), // NON-NLS
|
||||||
MED_DESCRIPTION("med_description"), // NON-NLS
|
MED_DESCRIPTION("med_description"), // NON-NLS
|
||||||
SHORT_DESCRIPTION("short_description"), // NON-NLS
|
SHORT_DESCRIPTION("short_description"), // NON-NLS
|
||||||
TIME("time"),
|
TIME("time"), // NON-NLS
|
||||||
HASH_HIT("hash_hit"); // NON-NLS
|
HASH_HIT("hash_hit"), // NON-NLS
|
||||||
|
TAGGED("tagged"); // NON-NLS
|
||||||
|
|
||||||
private final String columnName;
|
private final String columnName;
|
||||||
|
|
||||||
@ -183,12 +185,14 @@ public class EventDB {
|
|||||||
private PreparedStatement getDataSourceIDsStmt;
|
private PreparedStatement getDataSourceIDsStmt;
|
||||||
private PreparedStatement insertRowStmt;
|
private PreparedStatement insertRowStmt;
|
||||||
private PreparedStatement recordDBInfoStmt;
|
private PreparedStatement recordDBInfoStmt;
|
||||||
|
private PreparedStatement insertHashSetStmt;
|
||||||
|
private PreparedStatement insertHashHitStmt;
|
||||||
|
private PreparedStatement selectHashSetStmt;
|
||||||
|
private PreparedStatement countAllEventsStmt;
|
||||||
|
|
||||||
private final Set<PreparedStatement> preparedStatements = new HashSet<>();
|
private final Set<PreparedStatement> preparedStatements = new HashSet<>();
|
||||||
|
|
||||||
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true); //use fairness policy
|
private final Lock DBLock = new ReentrantReadWriteLock(true).writeLock(); //using exclusive lock for all db ops for now
|
||||||
|
|
||||||
private final Lock DBLock = rwLock.writeLock(); //using exclusive lock for all db ops for now
|
|
||||||
|
|
||||||
private EventDB(Case autoCase) throws SQLException, Exception {
|
private EventDB(Case autoCase) throws SQLException, Exception {
|
||||||
//should this go into module output (or even cache, we should be able to rebuild it)?
|
//should this go into module output (or even cache, we should be able to rebuild it)?
|
||||||
@ -205,30 +209,6 @@ public class EventDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Interval getSpanningInterval(Collection<Long> eventIDs) {
|
|
||||||
|
|
||||||
Interval span = null;
|
|
||||||
DBLock.lock();
|
|
||||||
try (Statement stmt = con.createStatement();
|
|
||||||
//You can't inject multiple values into one ? paramater in prepared statement,
|
|
||||||
//so we make new statement each time...
|
|
||||||
ResultSet rs = stmt.executeQuery("select Min(time), Max(time) from events where event_id in (" + StringUtils.join(eventIDs, ", ") + ")");) { // NON-NLS
|
|
||||||
while (rs.next()) {
|
|
||||||
span = new Interval(rs.getLong("Min(time)"), rs.getLong("Max(time)") + 1, DateTimeZone.UTC); // NON-NLS
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (SQLException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Error executing get spanning interval query.", ex); // NON-NLS
|
|
||||||
} finally {
|
|
||||||
DBLock.unlock();
|
|
||||||
}
|
|
||||||
return span;
|
|
||||||
}
|
|
||||||
|
|
||||||
EventTransaction beginTransaction() {
|
|
||||||
return new EventTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
void closeDBCon() {
|
void closeDBCon() {
|
||||||
if (con != null) {
|
if (con != null) {
|
||||||
try {
|
try {
|
||||||
@ -241,6 +221,27 @@ public class EventDB {
|
|||||||
con = null;
|
con = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Interval getSpanningInterval(Collection<Long> eventIDs) {
|
||||||
|
DBLock.lock();
|
||||||
|
try (Statement stmt = con.createStatement();
|
||||||
|
//You can't inject multiple values into one ? paramater in prepared statement,
|
||||||
|
//so we make new statement each time...
|
||||||
|
ResultSet rs = stmt.executeQuery("select Min(time), Max(time) from events where event_id in (" + StringUtils.join(eventIDs, ", ") + ")");) { // NON-NLS
|
||||||
|
while (rs.next()) {
|
||||||
|
return new Interval(rs.getLong("Min(time)"), rs.getLong("Max(time)") + 1, DateTimeZone.UTC); // NON-NLS
|
||||||
|
}
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Error executing get spanning interval query.", ex); // NON-NLS
|
||||||
|
} finally {
|
||||||
|
DBLock.unlock();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventTransaction beginTransaction() {
|
||||||
|
return new EventTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
void commitTransaction(EventTransaction tr, Boolean notify) {
|
void commitTransaction(EventTransaction tr, Boolean notify) {
|
||||||
if (tr.isClosed()) {
|
if (tr.isClosed()) {
|
||||||
throw new IllegalArgumentException("can't close already closed transaction"); // NON-NLS
|
throw new IllegalArgumentException("can't close already closed transaction"); // NON-NLS
|
||||||
@ -248,24 +249,34 @@ public class EventDB {
|
|||||||
tr.commit(notify);
|
tr.commit(notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the total number of events in the database or,
|
||||||
|
* -1 if there is an error.
|
||||||
|
*/
|
||||||
int countAllEvents() {
|
int countAllEvents() {
|
||||||
int result = -1;
|
|
||||||
DBLock.lock();
|
DBLock.lock();
|
||||||
//TODO convert this to prepared statement -jm
|
try (ResultSet rs = countAllEventsStmt.executeQuery()) { // NON-NLS
|
||||||
try (ResultSet rs = con.createStatement().executeQuery("select count(*) as count from events")) { // NON-NLS
|
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
result = rs.getInt("count"); // NON-NLS
|
return rs.getInt("count"); // NON-NLS
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
Exceptions.printStackTrace(ex);
|
LOGGER.log(Level.SEVERE, "Error counting all events", ex);
|
||||||
} finally {
|
} finally {
|
||||||
DBLock.unlock();
|
DBLock.unlock();
|
||||||
}
|
}
|
||||||
return result;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<EventType, Long> countEvents(ZoomParams params) {
|
/**
|
||||||
|
* get the count of all events that fit the given zoom params organized by
|
||||||
|
* the EvenType of the level spcified in the ZoomParams
|
||||||
|
*
|
||||||
|
* @param params the params that control what events to count and how to
|
||||||
|
* organize the returned map
|
||||||
|
*
|
||||||
|
* @return a map from event type( of the requested level) to event counts
|
||||||
|
*/
|
||||||
|
Map<EventType, Long> countEventsByType(ZoomParams params) {
|
||||||
if (params.getTimeRange() != null) {
|
if (params.getTimeRange() != null) {
|
||||||
return countEvents(params.getTimeRange().getStartMillis() / 1000,
|
return countEvents(params.getTimeRange().getStartMillis() / 1000,
|
||||||
params.getTimeRange().getEndMillis() / 1000,
|
params.getTimeRange().getEndMillis() / 1000,
|
||||||
@ -275,22 +286,25 @@ public class EventDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dropEventsTable() {
|
/**
|
||||||
//TODO: use prepared statement - jm
|
* drop the tables from this database and recreate them in order to start
|
||||||
|
* over.
|
||||||
|
*/
|
||||||
|
void reInitializeDB() {
|
||||||
DBLock.lock();
|
DBLock.lock();
|
||||||
try (Statement createStatement = con.createStatement()) {
|
try {
|
||||||
createStatement.execute("drop table if exists events"); // NON-NLS
|
dropEventsTableStmt.executeUpdate();
|
||||||
|
dropHashSetHitsTableStmt.executeUpdate();
|
||||||
|
dropHashSetsTableStmt.executeUpdate();
|
||||||
|
dropDBInfoTableStmt.executeUpdate();
|
||||||
|
initializeDB();;
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "could not drop old events table", ex); // NON-NLS
|
LOGGER.log(Level.SEVERE, "could not drop old tables table", ex); // NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
DBLock.unlock();
|
DBLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AggregateEvent> getAggregatedEvents(ZoomParams params) {
|
|
||||||
return getAggregatedEvents(params.getTimeRange(), params.getFilter(), params.getTypeZoomLevel(), params.getDescrLOD());
|
|
||||||
}
|
|
||||||
|
|
||||||
Interval getBoundingEventsInterval(Interval timeRange, RootFilter filter) {
|
Interval getBoundingEventsInterval(Interval timeRange, RootFilter filter) {
|
||||||
long start = timeRange.getStartMillis() / 1000;
|
long start = timeRange.getStartMillis() / 1000;
|
||||||
long end = timeRange.getEndMillis() / 1000;
|
long end = timeRange.getEndMillis() / 1000;
|
||||||
@ -378,7 +392,7 @@ public class EventDB {
|
|||||||
boolean hasNewColumns() {
|
boolean hasNewColumns() {
|
||||||
/* this relies on the fact that no tskObj has ID 0 but 0 is the default
|
/* this relies on the fact that no tskObj has ID 0 but 0 is the default
|
||||||
* value for the datasource_id column in the events table. */
|
* value for the datasource_id column in the events table. */
|
||||||
return hasHashHitColumn() && hasDataSourceIDColumn()
|
return hasHashHitColumn() && hasDataSourceIDColumn() && hasTaggedColumn()
|
||||||
&& (getDataSourceIDs().isEmpty() == false);
|
&& (getDataSourceIDs().isEmpty() == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,7 +499,7 @@ public class EventDB {
|
|||||||
+ "PRIMARY KEY (key))"; // NON-NLS
|
+ "PRIMARY KEY (key))"; // NON-NLS
|
||||||
stmt.execute(sql);
|
stmt.execute(sql);
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "problem creating db_info table", ex); // NON-NLS
|
LOGGER.log(Level.SEVERE, "problem creating db_info table", ex); // NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
try (Statement stmt = con.createStatement()) {
|
try (Statement stmt = con.createStatement()) {
|
||||||
@ -516,6 +530,15 @@ public class EventDB {
|
|||||||
LOGGER.log(Level.SEVERE, "problem upgrading events table", ex); // NON-NLS
|
LOGGER.log(Level.SEVERE, "problem upgrading events table", ex); // NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (hasTaggedColumn() == false) {
|
||||||
|
try (Statement stmt = con.createStatement()) {
|
||||||
|
String sql = "ALTER TABLE events ADD COLUMN tagged INTEGER"; // NON-NLS
|
||||||
|
stmt.execute(sql);
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
|
||||||
|
LOGGER.log(Level.SEVERE, "problem upgrading events table", ex); // NON-NLS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hasHashHitColumn() == false) {
|
if (hasHashHitColumn() == false) {
|
||||||
try (Statement stmt = con.createStatement()) {
|
try (Statement stmt = con.createStatement()) {
|
||||||
@ -553,8 +576,8 @@ public class EventDB {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
insertRowStmt = prepareStatement(
|
insertRowStmt = prepareStatement(
|
||||||
"INSERT INTO events (datasource_id,file_id ,artifact_id, time, sub_type, base_type, full_description, med_description, short_description, known_state, hash_hit) " // NON-NLS
|
"INSERT INTO events (datasource_id,file_id ,artifact_id, time, sub_type, base_type, full_description, med_description, short_description, known_state, hash_hit, tagged) " // NON-NLS
|
||||||
+ "VALUES (?,?,?,?,?,?,?,?,?,?,?)"); // NON-NLS
|
+ "VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"); // NON-NLS
|
||||||
|
|
||||||
getDataSourceIDsStmt = prepareStatement("select distinct datasource_id from events"); // NON-NLS
|
getDataSourceIDsStmt = prepareStatement("select distinct datasource_id from events"); // NON-NLS
|
||||||
getMaxTimeStmt = prepareStatement("select Max(time) as max from events"); // NON-NLS
|
getMaxTimeStmt = prepareStatement("select Max(time) as max from events"); // NON-NLS
|
||||||
@ -565,6 +588,11 @@ public class EventDB {
|
|||||||
insertHashSetStmt = prepareStatement("insert or ignore into hash_sets (hash_set_name) values (?)");
|
insertHashSetStmt = prepareStatement("insert or ignore into hash_sets (hash_set_name) values (?)");
|
||||||
selectHashSetStmt = prepareStatement("select hash_set_id from hash_sets where hash_set_name = ?");
|
selectHashSetStmt = prepareStatement("select hash_set_id from hash_sets where hash_set_name = ?");
|
||||||
insertHashHitStmt = prepareStatement("insert or ignore into hash_set_hits (hash_set_id, event_id) values (?,?)");
|
insertHashHitStmt = prepareStatement("insert or ignore into hash_set_hits (hash_set_id, event_id) values (?,?)");
|
||||||
|
countAllEventsStmt = prepareStatement("select count(*) as count from events");
|
||||||
|
dropEventsTableStmt = prepareStatement("drop table if exists events");
|
||||||
|
dropHashSetHitsTableStmt = prepareStatement("drop table if exists hash_set_hits");
|
||||||
|
dropHashSetsTableStmt = prepareStatement("drop table if exists hash_sets");
|
||||||
|
dropDBInfoTableStmt = prepareStatement("drop table if exists db_ino");
|
||||||
} catch (SQLException sQLException) {
|
} catch (SQLException sQLException) {
|
||||||
LOGGER.log(Level.SEVERE, "failed to prepareStatment", sQLException); // NON-NLS
|
LOGGER.log(Level.SEVERE, "failed to prepareStatment", sQLException); // NON-NLS
|
||||||
}
|
}
|
||||||
@ -624,17 +652,21 @@ public class EventDB {
|
|||||||
return hasDBColumn(EventTableColumn.DATA_SOURCE_ID);
|
return hasDBColumn(EventTableColumn.DATA_SOURCE_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasTaggedColumn() {
|
||||||
|
return hasDBColumn(EventTableColumn.TAGGED);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasHashHitColumn() {
|
private boolean hasHashHitColumn() {
|
||||||
return hasDBColumn(EventTableColumn.HASH_HIT);
|
return hasDBColumn(EventTableColumn.HASH_HIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void insertEvent(long time, EventType type, long datasourceID, Long objID,
|
void insertEvent(long time, EventType type, long datasourceID, Long objID,
|
||||||
Long artifactID, String fullDescription, String medDescription,
|
Long artifactID, String fullDescription, String medDescription,
|
||||||
String shortDescription, TskData.FileKnown known, Set<String> hashSets) {
|
String shortDescription, TskData.FileKnown known, Set<String> hashSets, boolean tagged) {
|
||||||
|
|
||||||
EventTransaction trans = beginTransaction();
|
EventTransaction transaction = beginTransaction();
|
||||||
insertEvent(time, type, datasourceID, objID, artifactID, fullDescription, medDescription, shortDescription, known, hashSets, trans);
|
insertEvent(time, type, datasourceID, objID, artifactID, fullDescription, medDescription, shortDescription, known, hashSets, tagged, transaction);
|
||||||
commitTransaction(trans, true);
|
commitTransaction(transaction, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -646,6 +678,7 @@ public class EventDB {
|
|||||||
void insertEvent(long time, EventType type, long datasourceID, Long objID,
|
void insertEvent(long time, EventType type, long datasourceID, Long objID,
|
||||||
Long artifactID, String fullDescription, String medDescription,
|
Long artifactID, String fullDescription, String medDescription,
|
||||||
String shortDescription, TskData.FileKnown known, Set<String> hashSetNames,
|
String shortDescription, TskData.FileKnown known, Set<String> hashSetNames,
|
||||||
|
boolean tagged,
|
||||||
EventTransaction transaction) {
|
EventTransaction transaction) {
|
||||||
|
|
||||||
if (transaction.isClosed()) {
|
if (transaction.isClosed()) {
|
||||||
@ -660,7 +693,7 @@ public class EventDB {
|
|||||||
DBLock.lock();
|
DBLock.lock();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
//"INSERT INTO events (datasource_id,file_id ,artifact_id, time, sub_type, base_type, full_description, med_description, short_description, known_state, hashHit) "
|
//"INSERT INTO events (datasource_id,file_id ,artifact_id, time, sub_type, base_type, full_description, med_description, short_description, known_state, hashHit, tagged) "
|
||||||
insertRowStmt.clearParameters();
|
insertRowStmt.clearParameters();
|
||||||
insertRowStmt.setLong(1, datasourceID);
|
insertRowStmt.setLong(1, datasourceID);
|
||||||
if (objID != null) {
|
if (objID != null) {
|
||||||
@ -689,6 +722,7 @@ public class EventDB {
|
|||||||
insertRowStmt.setByte(10, known == null ? TskData.FileKnown.UNKNOWN.getFileKnownValue() : known.getFileKnownValue());
|
insertRowStmt.setByte(10, known == null ? TskData.FileKnown.UNKNOWN.getFileKnownValue() : known.getFileKnownValue());
|
||||||
|
|
||||||
insertRowStmt.setInt(11, hashSetNames.isEmpty() ? 0 : 1);
|
insertRowStmt.setInt(11, hashSetNames.isEmpty() ? 0 : 1);
|
||||||
|
insertRowStmt.setInt(12, tagged ? 1 : 0);
|
||||||
|
|
||||||
insertRowStmt.executeUpdate();
|
insertRowStmt.executeUpdate();
|
||||||
|
|
||||||
@ -866,6 +900,10 @@ public class EventDB {
|
|||||||
return typeMap;
|
return typeMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<AggregateEvent> getAggregatedEvents(ZoomParams params) {
|
||||||
|
return getAggregatedEvents(params.getTimeRange(), params.getFilter(), params.getTypeZoomLevel(), params.getDescrLOD());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* //TODO: update javadoc //TODO: split this into helper methods
|
* //TODO: update javadoc //TODO: split this into helper methods
|
||||||
*
|
*
|
||||||
@ -938,6 +976,14 @@ public class EventDB {
|
|||||||
hashHits.add(executeQuery.getLong(EventTableColumn.EVENT_ID.toString()));
|
hashHits.add(executeQuery.getLong(EventTableColumn.EVENT_ID.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
HashSet<Long> tagged = new HashSet<>();
|
||||||
|
try (Statement st3 = con.createStatement();) {
|
||||||
|
|
||||||
|
ResultSet executeQuery = st3.executeQuery("select event_id from events where event_id in (" + eventIDS + ") and tagged = 1");
|
||||||
|
while (executeQuery.next()) {
|
||||||
|
tagged.add(executeQuery.getLong(EventTableColumn.EVENT_ID.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EventType type = useSubTypes ? RootEventType.allTypes.get(rs.getInt(EventTableColumn.SUB_TYPE.toString())) : BaseTypes.values()[rs.getInt(EventTableColumn.BASE_TYPE.toString())];
|
EventType type = useSubTypes ? RootEventType.allTypes.get(rs.getInt(EventTableColumn.SUB_TYPE.toString())) : BaseTypes.values()[rs.getInt(EventTableColumn.BASE_TYPE.toString())];
|
||||||
|
|
||||||
@ -946,6 +992,7 @@ public class EventDB {
|
|||||||
type,
|
type,
|
||||||
Stream.of(eventIDS.split(",")).map(Long::valueOf).collect(Collectors.toSet()), // NON-NLS
|
Stream.of(eventIDS.split(",")).map(Long::valueOf).collect(Collectors.toSet()), // NON-NLS
|
||||||
hashHits,
|
hashHits,
|
||||||
|
tagged,
|
||||||
rs.getString(descriptionColumn), lod);
|
rs.getString(descriptionColumn), lod);
|
||||||
|
|
||||||
//put events in map from type/descrition -> event
|
//put events in map from type/descrition -> event
|
||||||
|
@ -44,12 +44,18 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import org.joda.time.Interval;
|
import org.joda.time.Interval;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||||
import org.sleuthkit.autopsy.coreutils.HashHitUtils;
|
import org.sleuthkit.autopsy.coreutils.HashHitUtils;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.timeline.ProgressWindow;
|
import org.sleuthkit.autopsy.timeline.ProgressWindow;
|
||||||
import org.sleuthkit.autopsy.timeline.events.AggregateEvent;
|
import org.sleuthkit.autopsy.timeline.events.AggregateEvent;
|
||||||
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
|
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
|
||||||
import org.sleuthkit.autopsy.timeline.events.TimeLineEvent;
|
import org.sleuthkit.autopsy.timeline.events.TimeLineEvent;
|
||||||
|
import static org.sleuthkit.autopsy.timeline.events.db.Bundle.msgdlg_problem_text;
|
||||||
|
import static org.sleuthkit.autopsy.timeline.events.db.Bundle.progressWindow_msg_commitingDb;
|
||||||
|
import static org.sleuthkit.autopsy.timeline.events.db.Bundle.progressWindow_msg_populateMacEventsFiles;
|
||||||
|
import static org.sleuthkit.autopsy.timeline.events.db.Bundle.progressWindow_msg_reinit_db;
|
||||||
|
import static org.sleuthkit.autopsy.timeline.events.db.Bundle.progressWindow_populatingXevents;
|
||||||
import org.sleuthkit.autopsy.timeline.events.type.ArtifactEventType;
|
import org.sleuthkit.autopsy.timeline.events.type.ArtifactEventType;
|
||||||
import org.sleuthkit.autopsy.timeline.events.type.EventType;
|
import org.sleuthkit.autopsy.timeline.events.type.EventType;
|
||||||
import org.sleuthkit.autopsy.timeline.events.type.FileSystemTypes;
|
import org.sleuthkit.autopsy.timeline.events.type.FileSystemTypes;
|
||||||
@ -131,14 +137,13 @@ public class EventsRepository {
|
|||||||
}).build(CacheLoader.from(eventDB::getEventById));
|
}).build(CacheLoader.from(eventDB::getEventById));
|
||||||
eventCountsCache = CacheBuilder.newBuilder().maximumSize(1000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<ZoomParams, Map<EventType, Long>> rn) -> {
|
eventCountsCache = CacheBuilder.newBuilder().maximumSize(1000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<ZoomParams, Map<EventType, Long>> rn) -> {
|
||||||
//LOGGER.log(Level.INFO, "evicting counts: {0}", rn.toString());
|
//LOGGER.log(Level.INFO, "evicting counts: {0}", rn.toString());
|
||||||
}).build(CacheLoader.from(eventDB::countEvents));
|
}).build(CacheLoader.from(eventDB::countEventsByType));
|
||||||
aggregateEventsCache = CacheBuilder.newBuilder().maximumSize(1000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<ZoomParams, List<AggregateEvent>> rn) -> {
|
aggregateEventsCache = CacheBuilder.newBuilder().maximumSize(1000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<ZoomParams, List<AggregateEvent>> rn) -> {
|
||||||
//LOGGER.log(Level.INFO, "evicting aggregated events: {0}", rn.toString());
|
//LOGGER.log(Level.INFO, "evicting aggregated events: {0}", rn.toString());
|
||||||
}).build(CacheLoader.from(eventDB::getAggregatedEvents));
|
}).build(CacheLoader.from(eventDB::getAggregatedEvents));
|
||||||
maxCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMaxTime));
|
maxCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMaxTime));
|
||||||
minCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMinTime));
|
minCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMinTime));
|
||||||
this.modelInstance = new FilteredEventsModel(this, currentStateProperty);
|
this.modelInstance = new FilteredEventsModel(this, currentStateProperty);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return min time (in seconds from unix epoch) */
|
/** @return min time (in seconds from unix epoch) */
|
||||||
@ -231,30 +236,35 @@ public class EventsRepository {
|
|||||||
|
|
||||||
//TODO: can we avoid this with a state listener? does it amount to the same thing?
|
//TODO: can we avoid this with a state listener? does it amount to the same thing?
|
||||||
//post population operation to execute
|
//post population operation to execute
|
||||||
private final Runnable r;
|
private final Runnable postPopulationOperation;
|
||||||
|
private final SleuthkitCase skCase;
|
||||||
|
private final TagsManager tagsManager;
|
||||||
|
|
||||||
public DBPopulationWorker(Runnable r) {
|
public DBPopulationWorker(Runnable postPopulationOperation) {
|
||||||
progressDialog = new ProgressWindow(null, true, this);
|
progressDialog = new ProgressWindow(null, true, this);
|
||||||
progressDialog.setVisible(true);
|
progressDialog.setVisible(true);
|
||||||
this.r = r;
|
|
||||||
|
skCase = autoCase.getSleuthkitCase();
|
||||||
|
tagsManager = autoCase.getServices().getTagsManager();
|
||||||
|
|
||||||
|
this.postPopulationOperation = postPopulationOperation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@NbBundle.Messages({"progressWindow.msg.populateMacEventsFiles=populating mac events for files:",
|
||||||
|
"progressWindow.msg.reinit_db=(re)initializing events database",
|
||||||
|
"progressWindow.msg.commitingDb=committing events db"})
|
||||||
protected Void doInBackground() throws Exception {
|
protected Void doInBackground() throws Exception {
|
||||||
process(Arrays.asList(new ProgressWindow.ProgressUpdate(0, -1, NbBundle.getMessage(this.getClass(),
|
process(Arrays.asList(new ProgressWindow.ProgressUpdate(0, -1, progressWindow_msg_reinit_db(), "")));
|
||||||
"EventsRepository.progressWindow.msg.reinit_db"), "")));
|
|
||||||
//reset database
|
//reset database
|
||||||
//TODO: can we do more incremental updates? -jm
|
//TODO: can we do more incremental updates? -jm
|
||||||
eventDB.dropEventsTable();
|
eventDB.reInitializeDB();
|
||||||
eventDB.initializeDB();
|
|
||||||
|
|
||||||
//grab ids of all files
|
//grab ids of all files
|
||||||
SleuthkitCase skCase = autoCase.getSleuthkitCase();
|
|
||||||
List<Long> files = skCase.findAllFileIdsWhere("name != '.' AND name != '..'");
|
List<Long> files = skCase.findAllFileIdsWhere("name != '.' AND name != '..'");
|
||||||
|
|
||||||
final int numFiles = files.size();
|
final int numFiles = files.size();
|
||||||
process(Arrays.asList(new ProgressWindow.ProgressUpdate(0, numFiles, NbBundle.getMessage(this.getClass(),
|
process(Arrays.asList(new ProgressWindow.ProgressUpdate(0, numFiles, progressWindow_msg_populateMacEventsFiles(), "")));
|
||||||
"EventsRepository.progressWindow.msg.populateMacEventsFiles"), "")));
|
|
||||||
|
|
||||||
//insert file events into db
|
//insert file events into db
|
||||||
int i = 1;
|
int i = 1;
|
||||||
@ -266,7 +276,9 @@ public class EventsRepository {
|
|||||||
try {
|
try {
|
||||||
AbstractFile f = skCase.getAbstractFileById(fID);
|
AbstractFile f = skCase.getAbstractFileById(fID);
|
||||||
|
|
||||||
if (f != null) {
|
if (f == null) {
|
||||||
|
LOGGER.log(Level.WARNING, "Failed to get data for file : {0}", fID); // NON-NLS
|
||||||
|
} else {
|
||||||
//TODO: This is broken for logical files? fix -jm
|
//TODO: This is broken for logical files? fix -jm
|
||||||
//TODO: logical files don't necessarily have valid timestamps, so ... -jm
|
//TODO: logical files don't necessarily have valid timestamps, so ... -jm
|
||||||
final String uniquePath = f.getUniquePath();
|
final String uniquePath = f.getUniquePath();
|
||||||
@ -279,26 +291,24 @@ public class EventsRepository {
|
|||||||
final TskData.FileKnown known = f.getKnown();
|
final TskData.FileKnown known = f.getKnown();
|
||||||
boolean hashHit = f.getArtifactsCount(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT) > 0;
|
boolean hashHit = f.getArtifactsCount(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT) > 0;
|
||||||
Set<String> hashSets = hashHit ? HashHitUtils.getHashSetNamesForFile(skCase, f.getId()) : Collections.emptySet();
|
Set<String> hashSets = hashHit ? HashHitUtils.getHashSetNamesForFile(skCase, f.getId()) : Collections.emptySet();
|
||||||
|
boolean tagged = !tagsManager.getContentTagsByContent(f).isEmpty();
|
||||||
|
|
||||||
//insert it into the db if time is > 0 => time is legitimate (drops logical files)
|
//insert it into the db if time is > 0 => time is legitimate (drops logical files)
|
||||||
if (f.getAtime() > 0) {
|
if (f.getAtime() > 0) {
|
||||||
eventDB.insertEvent(f.getAtime(), FileSystemTypes.FILE_ACCESSED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, hashSets, trans);
|
eventDB.insertEvent(f.getAtime(), FileSystemTypes.FILE_ACCESSED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, hashSets, tagged, trans);
|
||||||
}
|
}
|
||||||
if (f.getMtime() > 0) {
|
if (f.getMtime() > 0) {
|
||||||
eventDB.insertEvent(f.getMtime(), FileSystemTypes.FILE_MODIFIED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, hashSets, trans);
|
eventDB.insertEvent(f.getMtime(), FileSystemTypes.FILE_MODIFIED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, hashSets, tagged, trans);
|
||||||
}
|
}
|
||||||
if (f.getCtime() > 0) {
|
if (f.getCtime() > 0) {
|
||||||
eventDB.insertEvent(f.getCtime(), FileSystemTypes.FILE_CHANGED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, hashSets, trans);
|
eventDB.insertEvent(f.getCtime(), FileSystemTypes.FILE_CHANGED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, hashSets, tagged, trans);
|
||||||
}
|
}
|
||||||
if (f.getCrtime() > 0) {
|
if (f.getCrtime() > 0) {
|
||||||
eventDB.insertEvent(f.getCrtime(), FileSystemTypes.FILE_CREATED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, hashSets, trans);
|
eventDB.insertEvent(f.getCrtime(), FileSystemTypes.FILE_CREATED, datasourceID, fID, null, uniquePath, medD, shortDesc, known, hashSets, tagged, trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
process(Arrays.asList(new ProgressWindow.ProgressUpdate(i, numFiles,
|
process(Arrays.asList(new ProgressWindow.ProgressUpdate(i, numFiles,
|
||||||
NbBundle.getMessage(this.getClass(),
|
progressWindow_msg_populateMacEventsFiles(), f.getName())));
|
||||||
"EventsRepository.progressWindow.msg.populateMacEventsFiles2"), f.getName())));
|
|
||||||
} else {
|
|
||||||
LOGGER.log(Level.WARNING, "failed to look up data for file : {0}", fID); // NON-NLS
|
|
||||||
}
|
}
|
||||||
} catch (TskCoreException tskCoreException) {
|
} catch (TskCoreException tskCoreException) {
|
||||||
LOGGER.log(Level.WARNING, "failed to insert mac event for file : " + fID, tskCoreException); // NON-NLS
|
LOGGER.log(Level.WARNING, "failed to insert mac event for file : " + fID, tskCoreException); // NON-NLS
|
||||||
@ -315,12 +325,11 @@ public class EventsRepository {
|
|||||||
}
|
}
|
||||||
//skip file_system events, they are already handled above.
|
//skip file_system events, they are already handled above.
|
||||||
if (type instanceof ArtifactEventType) {
|
if (type instanceof ArtifactEventType) {
|
||||||
populateEventType((ArtifactEventType) type, trans, skCase);
|
populateEventType((ArtifactEventType) type, trans);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
process(Arrays.asList(new ProgressWindow.ProgressUpdate(0, -1, NbBundle.getMessage(this.getClass(),
|
process(Arrays.asList(new ProgressWindow.ProgressUpdate(0, -1, progressWindow_msg_commitingDb(), "")));
|
||||||
"EventsRepository.progressWindow.msg.commitingDb"), "")));
|
|
||||||
if (isCancelled()) {
|
if (isCancelled()) {
|
||||||
eventDB.rollBackTransaction(trans);
|
eventDB.rollBackTransaction(trans);
|
||||||
} else {
|
} else {
|
||||||
@ -346,6 +355,8 @@ public class EventsRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@NbBundle.Messages("msgdlg.problem.text=There was a problem populating the timeline."
|
||||||
|
+ " Not all events may be present or accurate. See the log for details.")
|
||||||
protected void done() {
|
protected void done() {
|
||||||
super.done();
|
super.done();
|
||||||
try {
|
try {
|
||||||
@ -356,14 +367,12 @@ public class EventsRepository {
|
|||||||
LOGGER.log(Level.INFO, "Database population was cancelled by the user. Not all events may be present or accurate. See the log for details.", ex); // NON-NLS
|
LOGGER.log(Level.INFO, "Database population was cancelled by the user. Not all events may be present or accurate. See the log for details.", ex); // NON-NLS
|
||||||
} catch (InterruptedException | ExecutionException ex) {
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
LOGGER.log(Level.WARNING, "Exception while populating database.", ex); // NON-NLS
|
LOGGER.log(Level.WARNING, "Exception while populating database.", ex); // NON-NLS
|
||||||
JOptionPane.showMessageDialog(null, NbBundle.getMessage(this.getClass(),
|
JOptionPane.showMessageDialog(null, msgdlg_problem_text());
|
||||||
"EventsRepository.msgdlg.problem.text"));
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
LOGGER.log(Level.WARNING, "Unexpected exception while populating database.", ex); // NON-NLS
|
LOGGER.log(Level.WARNING, "Unexpected exception while populating database.", ex); // NON-NLS
|
||||||
JOptionPane.showMessageDialog(null, NbBundle.getMessage(this.getClass(),
|
JOptionPane.showMessageDialog(null, msgdlg_problem_text());
|
||||||
"EventsRepository.msgdlg.problem.text"));
|
|
||||||
}
|
}
|
||||||
r.run(); //execute post db population operation
|
postPopulationOperation.run(); //execute post db population operation
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -373,16 +382,15 @@ public class EventsRepository {
|
|||||||
* @param trans the db transaction to use
|
* @param trans the db transaction to use
|
||||||
* @param skCase a reference to the sleuthkit case
|
* @param skCase a reference to the sleuthkit case
|
||||||
*/
|
*/
|
||||||
private void populateEventType(final ArtifactEventType type, EventDB.EventTransaction trans, SleuthkitCase skCase) {
|
@NbBundle.Messages({"# {0} - event type ", "progressWindow.populatingXevents=populating {0} events"})
|
||||||
|
private void populateEventType(final ArtifactEventType type, EventDB.EventTransaction trans) {
|
||||||
try {
|
try {
|
||||||
//get all the blackboard artifacts corresponding to the given event sub_type
|
//get all the blackboard artifacts corresponding to the given event sub_type
|
||||||
final ArrayList<BlackboardArtifact> blackboardArtifacts = skCase.getBlackboardArtifacts(type.getArtifactType());
|
final ArrayList<BlackboardArtifact> blackboardArtifacts = skCase.getBlackboardArtifacts(type.getArtifactType());
|
||||||
final int numArtifacts = blackboardArtifacts.size();
|
final int numArtifacts = blackboardArtifacts.size();
|
||||||
|
|
||||||
process(Arrays.asList(new ProgressWindow.ProgressUpdate(0, numArtifacts,
|
process(Arrays.asList(new ProgressWindow.ProgressUpdate(0, numArtifacts,
|
||||||
NbBundle.getMessage(this.getClass(),
|
progressWindow_populatingXevents(type.toString()), "")));
|
||||||
"EventsRepository.progressWindow.populatingXevents",
|
|
||||||
type.toString()), "")));
|
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (final BlackboardArtifact bbart : blackboardArtifacts) {
|
for (final BlackboardArtifact bbart : blackboardArtifacts) {
|
||||||
@ -395,7 +403,11 @@ public class EventsRepository {
|
|||||||
AbstractFile f = skCase.getAbstractFileById(bbart.getObjectID());
|
AbstractFile f = skCase.getAbstractFileById(bbart.getObjectID());
|
||||||
boolean hashHit = f.getArtifactsCount(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT) > 0;
|
boolean hashHit = f.getArtifactsCount(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT) > 0;
|
||||||
Set<String> hashSets = hashHit ? HashHitUtils.getHashSetNamesForFile(skCase, f.getId()) : Collections.emptySet();
|
Set<String> hashSets = hashHit ? HashHitUtils.getHashSetNamesForFile(skCase, f.getId()) : Collections.emptySet();
|
||||||
eventDB.insertEvent(eventDescription.getTime(), type, datasourceID, bbart.getObjectID(), bbart.getArtifactID(), eventDescription.getFullDescription(), eventDescription.getMedDescription(), eventDescription.getShortDescription(), null, hashSets, trans);
|
|
||||||
|
boolean tagged = tagsManager.getContentTagsByContent(f).isEmpty() == false;
|
||||||
|
tagged |= tagsManager.getBlackboardArtifactTagsByArtifact(bbart).isEmpty() == false;
|
||||||
|
|
||||||
|
eventDB.insertEvent(eventDescription.getTime(), type, datasourceID, bbart.getObjectID(), bbart.getArtifactID(), eventDescription.getFullDescription(), eventDescription.getMedDescription(), eventDescription.getShortDescription(), null, hashSets, tagged, trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
|
@ -22,7 +22,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -73,9 +72,13 @@ import org.sleuthkit.autopsy.timeline.filters.TextFilter;
|
|||||||
import org.sleuthkit.autopsy.timeline.filters.TypeFilter;
|
import org.sleuthkit.autopsy.timeline.filters.TypeFilter;
|
||||||
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
|
||||||
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
|
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
|
||||||
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/** Represents an {@link AggregateEvent} in a {@link EventDetailChart}. */
|
/** Represents an {@link AggregateEvent} in a {@link EventDetailChart}. */
|
||||||
@ -84,6 +87,7 @@ public class AggregateEventNode extends StackPane {
|
|||||||
private static final Image HASH_PIN = new Image(AggregateEventNode.class.getResourceAsStream("/org/sleuthkit/autopsy/images/hashset_hits.png"));
|
private static final Image HASH_PIN = new Image(AggregateEventNode.class.getResourceAsStream("/org/sleuthkit/autopsy/images/hashset_hits.png"));
|
||||||
private final static Image PLUS = new Image("/org/sleuthkit/autopsy/timeline/images/plus-button.png"); // NON-NLS
|
private final static Image PLUS = new Image("/org/sleuthkit/autopsy/timeline/images/plus-button.png"); // NON-NLS
|
||||||
private final static Image MINUS = new Image("/org/sleuthkit/autopsy/timeline/images/minus-button.png"); // NON-NLS
|
private final static Image MINUS = new Image("/org/sleuthkit/autopsy/timeline/images/minus-button.png"); // NON-NLS
|
||||||
|
private final static Image TAG = new Image("/org/sleuthkit/autopsy/images/green-tag-icon-16.png"); // NON-NLS
|
||||||
|
|
||||||
private static final CornerRadii CORNER_RADII = new CornerRadii(3);
|
private static final CornerRadii CORNER_RADII = new CornerRadii(3);
|
||||||
|
|
||||||
@ -145,7 +149,7 @@ public class AggregateEventNode extends StackPane {
|
|||||||
private DescriptionVisibility descrVis;
|
private DescriptionVisibility descrVis;
|
||||||
private final SleuthkitCase sleuthkitCase;
|
private final SleuthkitCase sleuthkitCase;
|
||||||
private final FilteredEventsModel eventsModel;
|
private final FilteredEventsModel eventsModel;
|
||||||
private Map<String, Long> hashSetCounts = null;
|
|
||||||
private Tooltip tooltip;
|
private Tooltip tooltip;
|
||||||
|
|
||||||
public AggregateEventNode(final AggregateEvent event, AggregateEventNode parentEventNode, EventDetailChart chart) {
|
public AggregateEventNode(final AggregateEvent event, AggregateEventNode parentEventNode, EventDetailChart chart) {
|
||||||
@ -157,10 +161,14 @@ public class AggregateEventNode extends StackPane {
|
|||||||
eventsModel = chart.getController().getEventsModel();
|
eventsModel = chart.getController().getEventsModel();
|
||||||
final Region region = new Region();
|
final Region region = new Region();
|
||||||
HBox.setHgrow(region, Priority.ALWAYS);
|
HBox.setHgrow(region, Priority.ALWAYS);
|
||||||
ImageView imageView = new ImageView(HASH_PIN);
|
ImageView hashIV = new ImageView(HASH_PIN);
|
||||||
final HBox hBox = new HBox(descrLabel, countLabel, region, imageView, minusButton, plusButton);
|
ImageView tagIV = new ImageView(TAG);
|
||||||
|
final HBox hBox = new HBox(descrLabel, countLabel, region, hashIV, tagIV, minusButton, plusButton);
|
||||||
if (event.getEventIDsWithHashHits().isEmpty()) {
|
if (event.getEventIDsWithHashHits().isEmpty()) {
|
||||||
hBox.getChildren().remove(imageView);
|
hBox.getChildren().remove(hashIV);
|
||||||
|
}
|
||||||
|
if (event.getEventIDsWithTags().isEmpty()) {
|
||||||
|
hBox.getChildren().remove(tagIV);
|
||||||
}
|
}
|
||||||
hBox.setPrefWidth(USE_COMPUTED_SIZE);
|
hBox.setPrefWidth(USE_COMPUTED_SIZE);
|
||||||
hBox.setMinWidth(USE_PREF_SIZE);
|
hBox.setMinWidth(USE_PREF_SIZE);
|
||||||
@ -252,39 +260,70 @@ public class AggregateEventNode extends StackPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void installTooltip() {
|
private void installTooltip() {
|
||||||
|
//TODO: all this work should probably go on a background thread...
|
||||||
if (tooltip == null) {
|
if (tooltip == null) {
|
||||||
String collect = "";
|
|
||||||
|
HashMap<String, Long> hashSetCounts = new HashMap<>();
|
||||||
if (!event.getEventIDsWithHashHits().isEmpty()) {
|
if (!event.getEventIDsWithHashHits().isEmpty()) {
|
||||||
if (Objects.isNull(hashSetCounts)) {
|
try {
|
||||||
hashSetCounts = new HashMap<>();
|
for (TimeLineEvent tle : eventsModel.getEventsById(event.getEventIDsWithHashHits())) {
|
||||||
try {
|
ArrayList<BlackboardArtifact> blackboardArtifacts = sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, tle.getFileID());
|
||||||
for (TimeLineEvent tle : eventsModel.getEventsById(event.getEventIDsWithHashHits())) {
|
for (BlackboardArtifact artf : blackboardArtifacts) {
|
||||||
ArrayList<BlackboardArtifact> blackboardArtifacts = sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, tle.getFileID());
|
for (BlackboardAttribute attr : artf.getAttributes()) {
|
||||||
for (BlackboardArtifact artf : blackboardArtifacts) {
|
if (attr.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
|
||||||
for (BlackboardAttribute attr : artf.getAttributes()) {
|
hashSetCounts.merge(attr.getValueString(), 1L, Long::sum);
|
||||||
if (attr.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
|
|
||||||
hashSetCounts.merge(attr.getValueString(), 1L, Long::sum);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
Logger.getLogger(AggregateEventNode.class.getName()).log(Level.SEVERE, "Error getting hashset hit info for event.", ex);
|
|
||||||
}
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
Logger.getLogger(AggregateEventNode.class.getName()).log(Level.SEVERE, "Error getting hashset hit info for event.", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
collect = hashSetCounts.entrySet().stream()
|
|
||||||
.map((Map.Entry<String, Long> t) -> t.getKey() + " : " + t.getValue())
|
|
||||||
.collect(Collectors.joining("\n"));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<Long, TagName> tags = new HashMap<>();
|
||||||
|
if (!event.getEventIDsWithTags().isEmpty()) {
|
||||||
|
try {
|
||||||
|
for (TimeLineEvent tle : eventsModel.getEventsById(event.getEventIDsWithTags())) {
|
||||||
|
|
||||||
|
AbstractFile abstractFileById = sleuthkitCase.getAbstractFileById(tle.getFileID());
|
||||||
|
List<ContentTag> contentTagsByContent = sleuthkitCase.getContentTagsByContent(abstractFileById);
|
||||||
|
for (ContentTag tag : contentTagsByContent) {
|
||||||
|
tags.putIfAbsent(tag.getId(), tag.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
Long artifactID = tle.getArtifactID();
|
||||||
|
if (artifactID != 0) {
|
||||||
|
BlackboardArtifact blackboardArtifact = sleuthkitCase.getBlackboardArtifact(artifactID);
|
||||||
|
List<BlackboardArtifactTag> blackboardArtifactTagsByArtifact = sleuthkitCase.getBlackboardArtifactTagsByArtifact(blackboardArtifact);
|
||||||
|
for (BlackboardArtifactTag tag : blackboardArtifactTagsByArtifact) {
|
||||||
|
tags.putIfAbsent(tag.getId(), tag.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
Logger.getLogger(AggregateEventNode.class.getName()).log(Level.SEVERE, "Error getting tag info for event.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Long> tagCounts = tags.values().stream()
|
||||||
|
.collect(Collectors.toMap(TagName::getDisplayName, anything -> 1L, Long::sum));
|
||||||
|
|
||||||
|
String hashSetCountsString = hashSetCounts.entrySet().stream()
|
||||||
|
.map((Map.Entry<String, Long> t) -> t.getKey() + " : " + t.getValue())
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
String tagCountsString = tagCounts.entrySet().stream()
|
||||||
|
.map((Map.Entry<String, Long> t) -> t.getKey() + " : " + t.getValue())
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
|
||||||
tooltip = new Tooltip(
|
tooltip = new Tooltip(
|
||||||
NbBundle.getMessage(this.getClass(), "AggregateEventNode.installTooltip.text",
|
NbBundle.getMessage(this.getClass(), "AggregateEventNode.installTooltip.text",
|
||||||
getEvent().getEventIDs().size(), getEvent().getType(), getEvent().getDescription(),
|
getEvent().getEventIDs().size(), getEvent().getType(), getEvent().getDescription(),
|
||||||
getEvent().getSpan().getStart().toString(TimeLineController.getZonedFormatter()),
|
getEvent().getSpan().getStart().toString(TimeLineController.getZonedFormatter()),
|
||||||
getEvent().getSpan().getEnd().toString(TimeLineController.getZonedFormatter()))
|
getEvent().getSpan().getEnd().toString(TimeLineController.getZonedFormatter()))
|
||||||
+ (collect.isEmpty() ? "" : "\n\nHash Set Hits\n" + collect));
|
+ (hashSetCountsString.isEmpty() ? "" : "\n\nHash Set Hits\n" + hashSetCountsString)
|
||||||
|
+ (tagCountsString.isEmpty() ? "" : "\n\nTags\n" + tagCountsString)
|
||||||
|
);
|
||||||
Tooltip.install(AggregateEventNode.this, tooltip);
|
Tooltip.install(AggregateEventNode.this, tooltip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user