From 9189ffa60a33dfa049da0399470ecde7d3f94fd2 Mon Sep 17 00:00:00 2001
From: Kelly Kelly <kelly@basistech.com>
Date: Wed, 3 Feb 2021 14:38:39 -0500
Subject: [PATCH] Initial commit

---
 bindings/java/doxygen/artifact_catalog.dox    |   1 +
 .../org/sleuthkit/datamodel/Bundle.properties |  17 ++-
 .../datamodel/Bundle.properties-MERGED        |  17 ++-
 ...ineEventArtifactTypeSingleDescription.java |   2 +-
 .../datamodel/TimelineEventType.java          | 105 ++++++++++++++++--
 .../sleuthkit/datamodel/TimelineManager.java  |  36 +++++-
 6 files changed, 154 insertions(+), 24 deletions(-)

diff --git a/bindings/java/doxygen/artifact_catalog.dox b/bindings/java/doxygen/artifact_catalog.dox
index 39d548bf7..684e1e052 100644
--- a/bindings/java/doxygen/artifact_catalog.dox
+++ b/bindings/java/doxygen/artifact_catalog.dox
@@ -777,6 +777,7 @@ A Web cookie found.
 - TSK_VALUE (The Web cookie value attribute)
 
 ### OPTIONAL ATTRIBUTES
+- TSK_DATETIME_ACCESSED (Datetime the Web Cookie was last accessed, in seconds since 1970-01-01T00:00:00Z)
 - TSK_DATETIME_CREATED (Datetime the Web cookie was created, in seconds since 1970-01-01T00:00:00Z)
 - TSK_DATETIME_START (Datetime the Web cookie session was started, in seconds since 1970-01-01T00:00:00Z)
 - TSK_DATETIME_END (Expiration datetime of the Web cookie, in seconds since 1970-01-01T00:00:00Z)
diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
index 1cfc1a623..43fa3fa38 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
+++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
@@ -329,8 +329,10 @@ FileSystemTypes.fileChanged.name=File Changed
 MiscTypes.message.name=Messages
 MiscTypes.GPSRoutes.name=GPS Routes
 MiscTypes.GPSTrackpoint.name=GPS Trackpoint
-MiscTypes.Calls.name=Calls
-MiscTypes.Email.name=Email
+MiscTypes.Calls.name=Call Start
+MiscTypes.CallsEnd.name=Call End
+MiscTypes.Email.name=Email Sent
+MiscTypes.EmailRcvd.name=Email Received
 MiscTypes.recentDocuments.name=Recent Documents
 MiscTypes.installedPrograms.name=Installed Programs
 MiscTypes.exif.name=Exif
@@ -347,12 +349,17 @@ MiscTypes.metadataCreated.name=Document Created
 MiscTypes.programexecuted.name=Program Execution
 RootEventType.eventTypes.name=Event Types
 WebTypes.webDownloads.name=Web Downloads
-WebTypes.webCookies.name=Web Cookies
+WebTypes.webCookies.name=Web Cookies Create
+WebTypes.webCookiesAccessed.name=Web Cookies Accessed
+WebTypes.webCookiesStart.name=Web Cookies Start
+WebTypes.webCookiesEnd.name=Web Cookies End
 WebTypes.webBookmarks.name=Web Bookmarks
 WebTypes.webHistory.name=Web History
 WebTypes.webSearch.name=Web Searches
-WebTypes.webFormAutoFill.name=Web Form Autofill
-WebTypes.webFormAddress.name=Web Form Address
+WebTypes.webFormAutoFill.name=Web Form Autofill Created
+WebTypes.webFormAddress.name=Web Form Address Created
+WebTypes.webFormAddressModified.name=Web Form Address Modified
+WebTypes.webFormAutofillAccessed.name=Web Form Autofill Accessed
 CustomTypes.other.name=Standard Types
 CustomTypes.userCreated.name=Custom Types
 BaseTypes.customTypes.name=Other
diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED
index 1cfc1a623..43fa3fa38 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED
+++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED
@@ -329,8 +329,10 @@ FileSystemTypes.fileChanged.name=File Changed
 MiscTypes.message.name=Messages
 MiscTypes.GPSRoutes.name=GPS Routes
 MiscTypes.GPSTrackpoint.name=GPS Trackpoint
-MiscTypes.Calls.name=Calls
-MiscTypes.Email.name=Email
+MiscTypes.Calls.name=Call Start
+MiscTypes.CallsEnd.name=Call End
+MiscTypes.Email.name=Email Sent
+MiscTypes.EmailRcvd.name=Email Received
 MiscTypes.recentDocuments.name=Recent Documents
 MiscTypes.installedPrograms.name=Installed Programs
 MiscTypes.exif.name=Exif
@@ -347,12 +349,17 @@ MiscTypes.metadataCreated.name=Document Created
 MiscTypes.programexecuted.name=Program Execution
 RootEventType.eventTypes.name=Event Types
 WebTypes.webDownloads.name=Web Downloads
-WebTypes.webCookies.name=Web Cookies
+WebTypes.webCookies.name=Web Cookies Create
+WebTypes.webCookiesAccessed.name=Web Cookies Accessed
+WebTypes.webCookiesStart.name=Web Cookies Start
+WebTypes.webCookiesEnd.name=Web Cookies End
 WebTypes.webBookmarks.name=Web Bookmarks
 WebTypes.webHistory.name=Web History
 WebTypes.webSearch.name=Web Searches
-WebTypes.webFormAutoFill.name=Web Form Autofill
-WebTypes.webFormAddress.name=Web Form Address
+WebTypes.webFormAutoFill.name=Web Form Autofill Created
+WebTypes.webFormAddress.name=Web Form Address Created
+WebTypes.webFormAddressModified.name=Web Form Address Modified
+WebTypes.webFormAutofillAccessed.name=Web Form Autofill Accessed
 CustomTypes.other.name=Standard Types
 CustomTypes.userCreated.name=Custom Types
 BaseTypes.customTypes.name=Other
diff --git a/bindings/java/src/org/sleuthkit/datamodel/TimelineEventArtifactTypeSingleDescription.java b/bindings/java/src/org/sleuthkit/datamodel/TimelineEventArtifactTypeSingleDescription.java
index 84f71ba7d..f1c297f21 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/TimelineEventArtifactTypeSingleDescription.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/TimelineEventArtifactTypeSingleDescription.java
@@ -44,7 +44,7 @@ public TimelineEventDescriptionWithTime makeEventDescription(BlackboardArtifact
 		}
 
 		long time = timeAttribute.getValueLong();
-		return new TimelineEventDescriptionWithTime(time, null, null, description);
+		return new TimelineEventDescriptionWithTime(time, timeAttribute.getDisplayString(), null, description);
 	}
 
 	TimelineEventArtifactTypeSingleDescription(int typeID, String displayName,
diff --git a/bindings/java/src/org/sleuthkit/datamodel/TimelineEventType.java b/bindings/java/src/org/sleuthkit/datamodel/TimelineEventType.java
index 43f6749b4..8c188f6b4 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/TimelineEventType.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/TimelineEventType.java
@@ -1,7 +1,7 @@
 /*
  * Sleuth Kit Data Model
  *
- * Copyright 2018-2019 Basis Technology Corp.
+ * Copyright 2018-2021 Basis Technology Corp.
  * Contact: carrier <at> sleuthkit <dot> org
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -202,8 +202,12 @@ public SortedSet< TimelineEventType> getChildren() {
 			HierarchyLevel.CATEGORY, ROOT_EVENT_TYPE) {
 		@Override
 		public SortedSet< TimelineEventType> getChildren() {
-			return ImmutableSortedSet.of(WEB_DOWNLOADS, WEB_COOKIE, WEB_BOOKMARK,
-					WEB_HISTORY, WEB_SEARCH, WEB_FORM_AUTOFILL, WEB_FORM_ADDRESSES);
+			return ImmutableSortedSet.of(WEB_DOWNLOADS, WEB_COOKIE, 
+					WEB_COOKIE_ACCESSED, WEB_COOKIE_START, 
+					WEB_COOKIE_END, WEB_BOOKMARK,
+					WEB_HISTORY, WEB_SEARCH, WEB_FORM_AUTOFILL, 
+					WEB_FORM_ADDRESSES, WEB_FORM_ADDRESSES_MODIFIED, 
+					WEB_FORM_AUTOFILL_ACCESSED);
 		}
 	};
 
@@ -221,7 +225,7 @@ public int compare(TimelineEventType o1, TimelineEventType o2) {
 				}
 			});
 
-			builder.add(CALL_LOG, DEVICES_ATTACHED, EMAIL,
+			builder.add(CALL_LOG, CALL_LOG_END, DEVICES_ATTACHED, EMAIL, EMAIL_RCVD,
 					EXIF, GPS_BOOKMARK, GPS_LAST_KNOWN_LOCATION, GPS_TRACKPOINT,
 					GPS_ROUTE, GPS_SEARCH, GPS_TRACK, INSTALLED_PROGRAM, LOG_ENTRY, MESSAGE,
 					METADATA_LAST_PRINTED, METADATA_LAST_SAVED, METADATA_CREATED, PROGRAM_EXECUTION,
@@ -258,7 +262,7 @@ public int compare(TimelineEventType o1, TimelineEventType o2) {
 			getBundle().getString("WebTypes.webCookies.name"),// NON-NLS
 			WEB_ACTIVITY,
 			new BlackboardArtifact.Type(TSK_WEB_COOKIE),
-			new Type(TSK_DATETIME),
+			new Type(TSK_DATETIME_CREATED),
 			new Type(TSK_URL));
 	
 	TimelineEventType WEB_BOOKMARK = new URLArtifactEventType(10,
@@ -358,7 +362,7 @@ public int compare(TimelineEventType o1, TimelineEventType o2) {
 					phoneNumber = getAttributeSafe(artf, new Type(TSK_PHONE_NUMBER_FROM));
 				}
 
-				return stringValueOf(phoneNumber);
+				return "Start: " + stringValueOf(phoneNumber);
 			},
 			new AttributeExtractor(new Type(TSK_DIRECTION)));
 
@@ -376,7 +380,7 @@ public int compare(TimelineEventType o1, TimelineEventType o2) {
 				if (emailTo.length() > TimelineEventArtifactTypeImpl.EMAIL_TO_FROM_LENGTH_MAX) {
 					emailTo = emailTo.substring(0, TimelineEventArtifactTypeImpl.EMAIL_TO_FROM_LENGTH_MAX);
 				}
-				return emailFrom + " to " + emailTo; // NON-NLS
+				return "Sent from: " + emailFrom + "Sent to: " + emailTo; // NON-NLS
 			},
 			new AttributeExtractor(new Type(TSK_SUBJECT)),
 			artf -> {
@@ -473,7 +477,7 @@ public SortedSet< TimelineEventType> getChildren() {
 				final BlackboardAttribute name = getAttributeSafe(artf, new Type(TSK_NAME));
 				final BlackboardAttribute value = getAttributeSafe(artf, new Type(TSK_VALUE));
 				final BlackboardAttribute count = getAttributeSafe(artf, new Type(TSK_COUNT));
-				return stringValueOf(name) + ":" + stringValueOf(value) + " count: " + stringValueOf(count); // NON-NLS
+				return stringValueOf(name) + ":" + stringValueOf(value); // NON-NLS
 			}, new EmptyExtractor(), new EmptyExtractor());
 
 	TimelineEventType WEB_FORM_ADDRESSES = new URLArtifactEventType(28,
@@ -569,6 +573,91 @@ public SortedSet< TimelineEventType> getChildren() {
 				     }
 	                 return "";},
 			new AttributeExtractor(new Type(TSK_COMMENT)));
+	
+	TimelineEventType WEB_FORM_AUTOFILL_ACCESSED = new TimelineEventArtifactTypeImpl(37,
+		getBundle().getString("WebTypes.webFormAutofillAccessed.name"),
+		WEB_ACTIVITY,
+		new BlackboardArtifact.Type(TSK_WEB_FORM_AUTOFILL),
+		new Type(TSK_DATETIME_ACCESSED),
+		artf -> {
+			final BlackboardAttribute name = getAttributeSafe(artf, new Type(TSK_NAME));
+			final BlackboardAttribute value = getAttributeSafe(artf, new Type(TSK_VALUE));
+			final BlackboardAttribute count = getAttributeSafe(artf, new Type(TSK_COUNT));
+			return stringValueOf(name) + ":" + stringValueOf(value) + " Access count: " + stringValueOf(count); // NON-NLS
+		}, new EmptyExtractor(), new EmptyExtractor());
+	
+	TimelineEventType CALL_LOG_END = new TimelineEventArtifactTypeImpl(38,
+			getBundle().getString("MiscTypes.CallsEnd.name"), // NON-NLS
+			MISC_TYPES,
+			new BlackboardArtifact.Type(TSK_CALLLOG),
+			new Type(TSK_DATETIME_END),
+			new AttributeExtractor(new Type(TSK_NAME)),
+			artf -> {
+				BlackboardAttribute phoneNumber = getAttributeSafe(artf, new Type(TSK_PHONE_NUMBER));
+				if (phoneNumber == null) {
+					phoneNumber = getAttributeSafe(artf, new Type(TSK_PHONE_NUMBER_TO));
+				}
+				if (phoneNumber == null) {
+					phoneNumber = getAttributeSafe(artf, new Type(TSK_PHONE_NUMBER_FROM));
+				}
+
+				return "End: " + stringValueOf(phoneNumber);
+			},
+			new AttributeExtractor(new Type(TSK_DIRECTION)));
+	
+	TimelineEventType EMAIL_RCVD = new TimelineEventArtifactTypeImpl(38,
+			getBundle().getString("MiscTypes.EmailRcvd.name"), // NON-NLS
+			MISC_TYPES,
+			new BlackboardArtifact.Type(TSK_EMAIL_MSG),
+			new Type(TSK_DATETIME_RCVD),
+			artf -> {
+				String emailFrom = stringValueOf(getAttributeSafe(artf, new Type(TSK_EMAIL_FROM)));
+				if (emailFrom.length() > TimelineEventArtifactTypeImpl.EMAIL_TO_FROM_LENGTH_MAX) {
+					emailFrom = emailFrom.substring(0, TimelineEventArtifactTypeImpl.EMAIL_TO_FROM_LENGTH_MAX);
+				}
+				String emailTo = stringValueOf(getAttributeSafe(artf, new Type(TSK_EMAIL_TO)));
+				if (emailTo.length() > TimelineEventArtifactTypeImpl.EMAIL_TO_FROM_LENGTH_MAX) {
+					emailTo = emailTo.substring(0, TimelineEventArtifactTypeImpl.EMAIL_TO_FROM_LENGTH_MAX);
+				}
+				return "Received from: " + emailFrom + " Received by: " + emailTo; // NON-NLS
+			},
+			new AttributeExtractor(new Type(TSK_SUBJECT)),
+			artf -> {
+				final BlackboardAttribute msgAttribute = getAttributeSafe(artf, new Type(TSK_EMAIL_CONTENT_PLAIN));
+				String msg = stringValueOf(msgAttribute);
+				if (msg.length() > TimelineEventArtifactTypeImpl.EMAIL_FULL_DESCRIPTION_LENGTH_MAX) {
+					msg = msg.substring(0, TimelineEventArtifactTypeImpl.EMAIL_FULL_DESCRIPTION_LENGTH_MAX);
+				}
+				return msg;
+			});
+	
+	TimelineEventType WEB_FORM_ADDRESSES_MODIFIED = new URLArtifactEventType(39,
+		getBundle().getString("WebTypes.webFormAddressModified.name"),//NON-NLS
+		WEB_ACTIVITY,
+		new BlackboardArtifact.Type(TSK_WEB_FORM_ADDRESS),
+		new Type(TSK_DATETIME_MODIFIED),
+		new Type(TSK_EMAIL));
+	
+	TimelineEventType WEB_COOKIE_ACCESSED = new URLArtifactEventType(40,
+		getBundle().getString("WebTypes.webCookiesAccessed.name"),// NON-NLS
+		WEB_ACTIVITY,
+		new BlackboardArtifact.Type(TSK_WEB_COOKIE),
+		new Type(TSK_DATETIME_ACCESSED),
+		new Type(TSK_URL));
+		
+	TimelineEventType WEB_COOKIE_END = new URLArtifactEventType(41,
+		getBundle().getString("WebTypes.webCookiesEnd.name"),// NON-NLS
+		WEB_ACTIVITY,
+		new BlackboardArtifact.Type(TSK_WEB_COOKIE),
+		new Type(TSK_DATETIME_END),
+		new Type(TSK_URL));
+	
+	TimelineEventType WEB_COOKIE_START = new URLArtifactEventType(42,
+		getBundle().getString("WebTypes.webCookiesStart.name"),// NON-NLS
+		WEB_ACTIVITY,
+		new BlackboardArtifact.Type(TSK_WEB_COOKIE),
+		new Type(TSK_DATETIME_START),
+		new Type(TSK_URL));
 			
 	static SortedSet<? extends TimelineEventType> getCategoryTypes() {
 		return ROOT_EVENT_TYPE.getChildren();
diff --git a/bindings/java/src/org/sleuthkit/datamodel/TimelineManager.java b/bindings/java/src/org/sleuthkit/datamodel/TimelineManager.java
index a3771f382..b1d948e62 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/TimelineManager.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/TimelineManager.java
@@ -495,11 +495,12 @@ private long addEventDescription(long dataSourceObjId, long fileObjId, Long arti
 			// if no inserted rows, there is a conflict due to a duplicate event 
 			// description.  If that happens, return null as no id was inserted.
 			if (row < 1) {
-				throw new DuplicateException(String.format(
-						"An event description already exists for [fullDescription: %s, contentId: %d, artifactId: %s]",
-						fullDescription == null ? "<null>" : fullDescription,
-						fileObjId,
-						artifactID == null ? "<null>" : Long.toString(artifactID)));
+//				throw new DuplicateException(String.format(
+//						"An event description already exists for [fullDescription: %s, contentId: %d, artifactId: %s]",
+//						fullDescription == null ? "<null>" : fullDescription,
+//						fileObjId,
+//						artifactID == null ? "<null>" : Long.toString(artifactID)));
+
 			}
 
 			try (ResultSet generatedKeys = insertDescriptionStmt.getGeneratedKeys()) {
@@ -519,6 +520,31 @@ private long addEventDescription(long dataSourceObjId, long fileObjId, Long arti
 			caseDB.releaseSingleUserCaseWriteLock();
 		}
 	}
+	
+	private long getEventDescription(long dataSourceObjId, long fileObjId, Long artifactID,
+			String fullDescription, String medDescription, String shortDescription, CaseDbConnection connection) throws SQLException {
+		
+		String query = "SELECT event_description_id FROM tsk_event_descriptions "
+				+ "WHERE data_source_obj_id = " + dataSourceObjId
+				+ " AND content_obj_id = " + fileObjId
+				+ " AND artifact_id = " + artifactID
+				+ " AND full_description " + (fullDescription != null ?  "= '" + fullDescription + "'" : "IS null")
+				+ " AND med_description " + (medDescription != null ?  "= '" + medDescription + "'" : "IS null")
+				+ " AND short_description " + (shortDescription != null ?  "= '" + shortDescription + "'" : "IS null");
+		
+		caseDB.acquireSingleUserCaseReadLock();
+		try(ResultSet resultSet = connection.createStatement().executeQuery(query)) {
+
+			if(resultSet.next()) {
+				long id = resultSet.getLong(1);
+				return id;
+			}
+		} finally {
+			caseDB.releaseSingleUserCaseReadLock();
+		}
+		
+		return -1;
+	}
 
 	Collection<TimelineEvent> addEventsForNewFile(AbstractFile file, CaseDbConnection connection) throws TskCoreException {
 		Set<TimelineEvent> events = addEventsForNewFileQuiet(file, connection);
-- 
GitLab