diff --git a/.travis.yml b/.travis.yml
index dd6d057d4a4803bd63a7c5ec8a99c6c9f28c96a0..2ac28de72882006cb49007ab7e55100a7d0175ae 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,6 +27,7 @@ addons:
     - autopoint
     - libsqlite3-dev
     - ant
+    - ant-optional
     - libcppunit-dev
     - wget
     - openjdk-8-jdk
@@ -64,7 +65,12 @@ before_script:
 script:
   - javac -version
   - ./bootstrap && ./configure --prefix=/usr && make
-  - pushd bindings/java/ && ant -q dist && popd
+  - pushd bindings/java/ && ant -q dist 
+  # don't run tests on osx; libtsk not present due to SIP on osx: VIK-6971
+  - if test ${TRAVIS_OS_NAME} != "osx"; then
+        ant -q test;
+    fi
+  - popd
   - pushd case-uco/java/ && ant -q && popd
   - make check && if [ -f "tests/test-suite.log" ];then cat tests/test-suite.log; fi ; if [ -f "unit_tests/base/test-suite.log" ];then cat unit_tests/base/test-suite.log; fi
   - if test ${TRAVIS_OS_NAME} = "linux"; then
diff --git a/Makefile.am b/Makefile.am
index b020babab2518861f09a0bae9d524c01ef2d91dc..0887b7ab02946dc55e47421c91f94c5ca9d49b64 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,6 +8,7 @@ EXTRA_DIST = README_win32.txt README.md INSTALL.txt ChangeLog.txt NEWS.txt API-C
     win32/BUILDING.txt \
     win32/*/*.vcxproj \
     win32/tsk-win.sln \
+    win32/NugetPackages.props \
     win32/docs/* \
     bindings/java/README.txt \
     bindings/java/*.xml \
diff --git a/appveyor.yml b/appveyor.yml
index 18b9612b2c99c200235a364b2232a0bb451b48fb..5afea87227a05d12d7518ea85f4632519ad7d66b 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -7,30 +7,30 @@ cache:
 
 image: Visual Studio 2015
 
-
-install:  
+install:
   - ps: choco install nuget.commandline
   - ps: choco install ant --ignore-dependencies
   - ps: $env:Path="C:\Program Files\Java\jdk1.8.0\bin;$($env:Path);C:\ProgramData\chocolatey\lib\ant"
   - set PATH=C:\Python36-x64\';%PATH%
 environment:
-    global:
-      TSK_HOME: "%APPVEYOR_BUILD_FOLDER%"
-      PYTHON: "C:\\Python36-x64"
-      JDK_HOME: C:\Program Files\Java\jdk1.8.0
+  global:
+    TSK_HOME: "%APPVEYOR_BUILD_FOLDER%"
+    PYTHON: "C:\\Python36-x64"
+    JDK_HOME: C:\Program Files\Java\jdk1.8.0
 services:
 
 before_build:
   - nuget restore win32\libtsk -PackagesDirectory win32\packages
-  
+
 build_script:
-      - python win32\updateAndBuildAll.py -m 
-      - ps: ant -version
-      - ps: pushd bindings/java
-      - cmd: ant -q dist
-      - ps: popd
-      - ps: pushd case-uco/java
-      - cmd: ant -q
-      - ps: popd
+  - python win32\updateAndBuildAll.py -m
+  - ps: ant -version
+  - ps: pushd bindings/java
+  - cmd: ant -q dist
+  - ps: popd
+  - ps: pushd case-uco/java
+  - cmd: ant -q
+  - ps: popd
 
-test: off
+test_script:
+  - cmd: ant -q -f bindings/java test
diff --git a/bindings/java/build-windows.xml b/bindings/java/build-windows.xml
index bab79933626560aa96489314011954961d90ed04..2f6264a486909e3467bf3bdce362faaf4c0edb19 100644
--- a/bindings/java/build-windows.xml
+++ b/bindings/java/build-windows.xml
@@ -75,26 +75,9 @@
 		<copy file="${tsk.jni.32}" todir="${i686}/win" overwrite="true"/>
 	</target>
 
-	<target name="copyWinTskLibsToBuild-PostgreSQL" depends="copyWinTskLibs64ToBuild-PostgreSQL,copyWinTskLibs32ToBuild-PostgreSQL" description="Copy Windows DLLs to the correct location, PostgreSQL build." />
-
 	<target name="checkTskLibDirs">
 		<available property="win64.TskLib.exists" type="file" file="${basedir}/../../win32/x64/${tsk.config}/libtsk_jni.dll" />
 		<available property="win32.TskLib.exists" type="file" file="${basedir}/../../win32/${tsk.config}/libtsk_jni.dll" />
 	</target>
 
-	<target name="copyWinTskLibs64ToBuild-PostgreSQL" depends="checkTskLibDirs" if="win64.TskLib.exists">
-		<property name="tsk.jni.64" location="${basedir}/../../win32/x64/${tsk.config}/libtsk_jni.dll" />
-
-		<copy file="${tsk.jni.64}" todir="${amd64}/win" overwrite="true"/>
-		<copy file="${tsk.jni.64}" todir="${x86_64}/win" overwrite="true"/>
-	</target>
-
-	<target name="copyWinTskLibs32ToBuild-PostgreSQL" depends="checkTskLibDirs" if="win32.TskLib.exists">
-		<property name="tsk.jni.32" location="${basedir}/../../win32/${tsk.config}/libtsk_jni.dll" />
-
-		<copy file="${tsk.jni.32}" todir="${i386}/win" overwrite="true"/>
-		<copy file="${tsk.jni.32}" todir="${x86}/win" overwrite="true"/>
-		<copy file="${tsk.jni.32}" todir="${i586}/win" overwrite="true"/>
-		<copy file="${tsk.jni.32}" todir="${i686}/win" overwrite="true"/>
-	</target>
 </project>
diff --git a/bindings/java/doxygen/artifact_catalog.dox b/bindings/java/doxygen/artifact_catalog.dox
index 53ad511e8febc358dcec6ee5430605f0f92b02cc..07674595927ad1861bebb3e86bcf3894f0dc0934 100644
--- a/bindings/java/doxygen/artifact_catalog.dox
+++ b/bindings/java/doxygen/artifact_catalog.dox
@@ -35,7 +35,6 @@ TSK_CARD_NUMBER (Credit card number)
 
 ---
 
-
 ## TSK_ASSOCIATED_OBJECT
 Provides a backwards link to an artifact that references the parent file of this artifact.  Example usage is that a downloaded file will have this artifact and it will point back to the TSK_WEB_DOWNLOAD artifact that is associated with a browser's SQLite database. See \ref jni_bb_associated_object.
 
@@ -43,6 +42,20 @@ Provides a backwards link to an artifact that references the parent file of this
 - TSK_ASSOCIATED_ARTIFACT (Artifact ID of associated artifact)
 
 
+---
+## TSK_BACKUP_EVENT
+Details about System/aplication/file backups.
+
+### REQUIRED ATTRIBUTES
+- TSK_DATETIME_START (Date/Time the backup happened)
+     or 
+  TSK_DATETIME
+  
+### OPTIONAL ATTRIBUTES
+- TSK_DATETIME_ENDED (Date/Time the backup ended)
+
+
+
 ---
 ## TSK_BLUETOOTH_ADAPTER
 Details about a Bluetooth adapter.
@@ -136,6 +149,20 @@ Describes how a data source was used, e.g., as a SIM card or an OS drive (such a
 - TSK_DESCRIPTION (Description of the usage, e.g., "OS Drive (Windows Vista)").
 
 
+---
+## TSK_DELETED_PROG
+Programs that have been deleted from the system.
+
+### REQUIRED ATTRIBUTES
+- TSK_DATETIME_DELETED (Date/Time the program was deleted)
+     or 
+  TSK_DATETIME
+- TSK_PROG_NAME (Program that was deleted)
+
+### OPTIONAL Attributes
+- TSK_PATH (Location where the program resided before being deleted)
+
+
 
 ---
 ## TSK_DEVICE_ATTACHED
@@ -244,6 +271,18 @@ None
 ### OPTIONAL ATTRIBUTES
 - TSK_PHOTODNA_HASH (The PhotoDNA hash of an image)
 
+---
+## TSK_GPS_AREA
+An outline of an area.
+
+### REQUIRED ATTRIBUTES
+- TSK_GEO_WAYPOINTS (JSON list of waypoints. Use org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoints class to create/process)
+
+### OPTIONAL ATTRIBUTES
+- TSK_LOCATION (Location of the route, e.g., a state or city)
+- TSK_NAME (Name of the area, e.g., Minute Man Trail)
+- TSK_PROG_NAME (Name of the application that was the source of the GPS route)
+
 ---
 ## TSK_GPS_BOOKMARK
 A bookmarked GPS location or saved waypoint.
@@ -376,6 +415,15 @@ Indication that the source file matches some set of criteria (possibly user defi
 
 
 
+---
+## TSK_IP_DHCP
+DHCP information that is stored.
+
+### REQUIRED ATTRIBUTES
+- TSK_NAME (Description of Information)
+- TSK_VALUE (Value of Information)
+
+
 ---
 ## TSK_KEYWORD_HIT
 Indication that the source artifact or file contains a keyword. Keywords are grouped into named sets.
@@ -508,6 +556,20 @@ Details about an operating system recovered from the data source.
 
 
 
+---
+## TSK_PROG_NOTIFICATION
+Notifications to the user.
+
+### REQUIRED ATTRIBUTES
+- TSK_DATETIME (When the notification was sent/received)
+- TSK_PROG_NAME (Program to send/receive notification)
+
+### OPTIONAL ATTRIBUTES
+- TSK_TITLE (Title of the notification)
+- TSK_VALUE (Message being sent or received)
+
+
+
 ---
 ## TSK_PROG_RUN
 The number of times a program/application was run.
@@ -556,6 +618,18 @@ Details about a remote drive found in the data source.
 
 
 
+---
+## TSK_SCREEN_SHOTS
+Screenshots from a device or application.
+
+### REQUIRED ATTRIBUTES
+- TSK_DATETIME (When the screenshot was taken)
+- TSK_PROG_NAME (Program that took the screenshot)
+
+### OPTIONAL ATTRIBUTES
+- TSK_PATH (Location of screenshot)
+
+
 ---
 ## TSK_SERVICE_ACCOUNT
 An application or web user account.
@@ -623,6 +697,23 @@ An indication that some media file content was generated by the user.
 - TSK_COMMENT (The reason why user-generated content is suspected)
 
 
+---
+
+## TSK_USER_DEVICE_EVENT
+Activity on the system or from an application.  Example usage is a mobile device being locked and unlocked.
+. 
+
+### REQUIRED ATTRIBUTES
+- TSK_DATETIME_START (When activity started)
+    or 
+  TSK_DATETIME
+
+### OPTIONAL ATTRIBUTES
+- TSK_ACTIVITY_TYPE (Activity type i.e.: On or Off)
+- TSK_DATETIME_END (When activity ended)
+- TSK_PROG_NAME (Name of the program doing the activity)
+- TSK_VALUE (Connection type)
+
 
 ---
 ## TSK_VERIFICATION_FAILED
@@ -676,6 +767,17 @@ A web cache entry. The resource that was cached may or may not be present in the
 
 
 
+---
+## TSK_WEB_CATEGORIZATION
+The categorization of a web host using a specific usage type, e.g. mail.google.com would correspond to Web Email.
+
+### REQUIRED ATTRIBUTES
+- TSK_NAME (The usage category identifier, e.g. Web Email)
+- TSK_DOMAIN (The domain of the host, e.g. google.com)
+- TSK_HOST (The full host, e.g. mail.google.com)
+
+
+
 ---
 ## TSK_WEB_COOKIE
 A Web cookie found.
@@ -797,5 +899,14 @@ Details about a WiFi adapter.
 
 
 
+---
+## TSK_YARA_HIT
+Indicates that the some content of the file was a hit for a YARA rule match.
+
+### REQUIRED ATTRIBUTES
+- TSK_RULE (The rule that was a hit for this file)
+- TSK_SET_NAME (Name of the rule set containing the matching rule YARA rule)
+
+
 
 */
diff --git a/bindings/java/ivy.xml b/bindings/java/ivy.xml
index af9f85d09f8d67a0bc96cab2f3647f211098b736..67518a6566d0eba79da9a9bb86e28dd94ead860e 100644
--- a/bindings/java/ivy.xml
+++ b/bindings/java/ivy.xml
@@ -15,7 +15,7 @@
           debian/sleuthkit-java.install so that it gets packaged correctly on Linux -->
 		<dependency org="org.xerial" name="sqlite-jdbc" rev="3.25.2"/>
 
-		<dependency org="org.postgresql" name="postgresql" rev="9.4.1211.jre7" >
+		<dependency org="org.postgresql" name="postgresql" rev="42.2.18" >
 			<artifact name="postgresql" type="jar" />
 		</dependency>
 		<dependency org="com.mchange" name="c3p0" rev="0.9.5" />
diff --git a/bindings/java/nbproject/project.xml b/bindings/java/nbproject/project.xml
index 58ab53b9b262c0a5a2414f1bbbefc6a354627323..57a2b6befa984bdfa69a539c68c6200745375049 100755
--- a/bindings/java/nbproject/project.xml
+++ b/bindings/java/nbproject/project.xml
@@ -114,7 +114,7 @@
         <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/4">
             <compilation-unit>
                 <package-root>src</package-root>
-                <classpath mode="compile">lib;lib/diffutils-1.2.1.jar;lib/junit-4.8.2.jar;lib/postgresql-9.4-1201.jdbc41.jar;lib/c3p0-0.9.5.jar;lib/mchange-commons-java-0.2.9.jar;lib/c3p0-0.9.5-sources.jar;lib/c3p0-0.9.5-javadoc.jar;lib/joda-time-2.4.jar;lib/commons-lang3-3.0.jar;lib/guava-19.0.jar;lib/SparseBitSet-1.1.jar;lib/gson-2.8.5.jar;lib/commons-validator-1.6.jar</classpath>
+                <classpath mode="compile">lib;lib/diffutils-1.2.1.jar;lib/junit-4.8.2.jar;lib/postgresql-42.2.18.jar;lib/c3p0-0.9.5.jar;lib/mchange-commons-java-0.2.9.jar;lib/c3p0-0.9.5-sources.jar;lib/c3p0-0.9.5-javadoc.jar;lib/joda-time-2.4.jar;lib/commons-lang3-3.0.jar;lib/guava-19.0.jar;lib/SparseBitSet-1.1.jar;lib/gson-2.8.5.jar;lib/commons-validator-1.6.jar</classpath>
                 <built-to>build</built-to>
                 <source-level>1.8</source-level>
             </compilation-unit>
diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java
index c73faffbf4523f0de507ade121ea38f944eb6d3c..61aa3e815e32b777182e6478ba562370208d668a 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java
@@ -1026,30 +1026,30 @@ public enum ARTIFACT_TYPE implements SleuthkitVisitableItem {
 		TSK_GEN_INFO(1, "TSK_GEN_INFO", //NON-NLS
 				bundle.getString("BlackboardArtifact.tskGenInfo.text")),
 		/**
-		 * A Web bookmark.
-		 * Use methods in org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper
-		 * to create bookmark artifacts.
+		 * A Web bookmark. Use methods in
+		 * org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper to
+		 * create bookmark artifacts.
 		 */
 		TSK_WEB_BOOKMARK(2, "TSK_WEB_BOOKMARK", //NON-NLS
 				bundle.getString("BlackboardArtifact.tskWebBookmark.text")),
 		/**
-		 * A Web cookie.
-		 * Use methods in org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper
-		 * to create cookie artifacts.
+		 * A Web cookie. Use methods in
+		 * org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper to
+		 * create cookie artifacts.
 		 */
 		TSK_WEB_COOKIE(3, "TSK_WEB_COOKIE",
 				bundle.getString("BlackboardArtifact.tskWebCookie.text")), //NON-NLS				
 		/**
-		 * A Web history.
-		 * Use methods in org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper
-		 * to create history artifacts.
+		 * A Web history. Use methods in
+		 * org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper to
+		 * create history artifacts.
 		 */
 		TSK_WEB_HISTORY(4, "TSK_WEB_HISTORY", //NON-NLS
 				bundle.getString("BlackboardArtifact.tskWebHistory.text")),
 		/**
-		 * A Web download.
-		 * Use methods in org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper
-		 * to create download artifacts.
+		 * A Web download. Use methods in
+		 * org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper to
+		 * create download artifacts.
 		 */
 		TSK_WEB_DOWNLOAD(5, "TSK_WEB_DOWNLOAD", //NON-NLS
 				bundle.getString("BlackboardArtifact.tskWebDownload.text")),
@@ -1153,23 +1153,24 @@ public enum ARTIFACT_TYPE implements SleuthkitVisitableItem {
 				bundle.getString("BlackboardArtifact.tskToolOutput.text")),
 		/**
 		 * A contact extracted from a phone, or from an address
-		 * book/email/messaging application.
-		 * Use methods in org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper
+		 * book/email/messaging application. Use methods in
+		 * org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper
 		 * to create contact artifacts.
 		 */
 		TSK_CONTACT(23, "TSK_CONTACT", //NON-NLS
 				bundle.getString("BlackboardArtifact.tskContact.text")),
 		/**
 		 * An SMS/MMS message extracted from phone, or from another messaging
-		 * application, like IM.
-		 * Use methods in org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper
+		 * application, like IM. Use methods in
+		 * org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper
 		 * to create message artifacts.
 		 */
 		TSK_MESSAGE(24, "TSK_MESSAGE", //NON-NLS
 				bundle.getString("BlackboardArtifact.tskMessage.text")),
 		/**
-		 * A phone call log extracted from a phone or softphone application.
-		 * Use methods in org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper
+		 * A phone call log extracted from a phone or softphone application. Use
+		 * methods in
+		 * org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper
 		 * to create call log artifacts.
 		 */
 		TSK_CALLLOG(25, "TSK_CALLLOG", //NON-NLS
@@ -1226,8 +1227,8 @@ public enum ARTIFACT_TYPE implements SleuthkitVisitableItem {
 		TSK_INTERESTING_ARTIFACT_HIT(35, "TSK_INTERESTING_ARTIFACT_HIT", //NON-NLS
 				bundle.getString("BlackboardArtifact.tskInterestingArtifactHit.text"), Category.ANALYSIS_RESULT),
 		/**
-		 * A route based on GPS coordinates.
-		 * Use org.sleuthkit.datamodel.blackboardutils.GeoArtifactsHelper.addRoute()
+		 * A route based on GPS coordinates. Use
+		 * org.sleuthkit.datamodel.blackboardutils.GeoArtifactsHelper.addRoute()
 		 * to create route artifacts.
 		 */
 		TSK_GPS_ROUTE(36, "TSK_GPS_ROUTE", //NON-NLS
@@ -1293,16 +1294,16 @@ public enum ARTIFACT_TYPE implements SleuthkitVisitableItem {
 		TSK_DATA_SOURCE_USAGE(48, "TSK_DATA_SOURCE_USAGE", //NON-NLS
 				bundle.getString("BlackboardArtifact.tskDataSourceUsage.text")),
 		/**
-		 * Indicates auto fill data from a Web form.
-		 * Use methods in org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper
-		 * to create web form autofill artifacts.
+		 * Indicates auto fill data from a Web form. Use methods in
+		 * org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper to
+		 * create web form autofill artifacts.
 		 */
 		TSK_WEB_FORM_AUTOFILL(49, "TSK_WEB_FORM_AUTOFILL", //NON-NLS
 				bundle.getString("BlackboardArtifact.tskWebFormAutofill.text")),
 		/**
-		 * Indicates an person's address filled in a web form.
-		 * Use methods in org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper
-		 * to create web form address artifacts.
+		 * Indicates an person's address filled in a web form. Use methods in
+		 * org.sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper to
+		 * create web form address artifacts.
 		 */
 		TSK_WEB_FORM_ADDRESS(50, "TSK_WEB_FORM_ADDRESSES ", //NON-NLS
 				bundle.getString("BlackboardArtifact.tskWebFormAddresses.text")),
@@ -1346,8 +1347,8 @@ public enum ARTIFACT_TYPE implements SleuthkitVisitableItem {
 		TSK_METADATA(57, "TSK_METADATA", //NON-NLS
 				bundle.getString("BlackboardArtifact.tskMetadata.text")),
 		/**
-		 * Stores a GPS track log.
-		 * Use org.sleuthkit.datamodel.blackboardutils.GeoArtifactsHelper.addTrack() 
+		 * Stores a GPS track log. Use
+		 * org.sleuthkit.datamodel.blackboardutils.GeoArtifactsHelper.addTrack()
 		 * to create track artifacts.
 		 */
 		TSK_GPS_TRACK(58, "TSK_GPS_TRACK",
@@ -1356,13 +1357,58 @@ public enum ARTIFACT_TYPE implements SleuthkitVisitableItem {
 		 * Stores a role on a given domain.
 		 */
 		TSK_WEB_ACCOUNT_TYPE(59, "TSK_WEB_ACCOUNT_TYPE",
-				bundle.getString("BlackboardArtifact.tskWebAccountType.text"));
+				bundle.getString("BlackboardArtifact.tskWebAccountType.text")),
+		/**
+		 * Screen shots from device or Application.
+		 */
+		TSK_SCREEN_SHOTS(60, "TSK_SCREEN_SHOTS",
+				bundle.getString("BlackboardArtifact.tskScreenShots.text")),
+		/**
+		 * DHCP Information that is store for a device.
+		 */
+		TSK_IP_DHCP(61, "TSK_IP_DHCP",
+				bundle.getString("BlackboardArtifact.tskDhcpInfo.text")),
+		/**
+		 * Notifications Sent to User.
+		 */
+		TSK_PROG_NOTIFICATIONS(62, "TSK_PROG_NOTIFICATIONS",
+				bundle.getString("BlackboardArtifact.tskProgNotifications.text")),
+		/**
+		 * System/Application/File backup.
+		 */
+		TSK_BACKUP_EVENT(63, "TSK_BACKUP_EVENT",
+				bundle.getString("BlackboardArtifact.tskBackupEvent.text")),
+		/**
+		 * Programs that have been deleted.
+		 */
+		TSK_DELETED_PROG(64, "TSK_DELETED_PROG",
+				bundle.getString("BlackboardArtifact.tskDeletedProg.text")),
+		/**
+		 * Activity on the System/Application.
+		 */
+		TSK_USER_DEVICE_EVENT(65, "TSK_USER_DEVICE_EVENT",
+				bundle.getString("BlackboardArtifact.tskUserDeviceEvent.text")),
+		/**
+		 * Indicates that the file had a yara pattern match hit.
+		 */
+		TSK_YARA_HIT(66, "TSK_YARA_HIT",
+				bundle.getString("BlackboardArtifact.tskYaraHit.text")),
+		/**
+		 * Stores the outline of an area using GPS coordinates.
+		 */
+		TSK_GPS_AREA(67, "TSK_GPS_AREA",
+				bundle.getString("BlackboardArtifact.tskGPSArea.text")),
+
+		TSK_WEB_CATEGORIZATION(68, "TSK_WEB_CATEGORIZATION",
+				bundle.getString("BlackboardArtifact.tskWebCategorization.text")),
+
+		;
+
 		/*
 		 * To developers: For each new artifact, ensure that: - The enum value
 		 * has 1-line JavaDoc description - The artifact catalog
 		 * (artifact_catalog.dox) is updated to reflect the attributes it uses
 		 */
-
 		private final String label;
 		private final int typeId;
 		private final String displayName;
diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java
index 0e9d449e28ccab2b4ccd05c739a118ff2c6cf3aa..dbe1b340d8d9d7cb3121ea715cd85af85892603d 100755
--- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java
@@ -58,6 +58,14 @@ public class BlackboardAttribute {
 	private long artifactID;
 	private SleuthkitCase sleuthkitCase;
 	private String sources;
+	
+	// Cached parent artifact. This field is populated lazily upon the first
+	// call to getParentArtifact().
+	private BlackboardArtifact parentArtifact;
+	
+	// The parent data source is defined as being 
+	// the data source of the parent artifact.
+	private Long parentDataSourceID;
 
 	/**
 	 * Constructs a standard attribute with an integer value. The attribute
@@ -244,8 +252,8 @@ public BlackboardAttribute(Type attributeType, String source, double valueDouble
 	 *                                  TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON
 	 */
 	public BlackboardAttribute(ATTRIBUTE_TYPE attributeType, String source, String valueString) throws IllegalArgumentException {
-		if  (attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING
-		     && attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) 		{
+		if (attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING
+				&& attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
 			throw new IllegalArgumentException("Value types do not match");
 		}
 		this.artifactID = 0;
@@ -277,7 +285,7 @@ public BlackboardAttribute(ATTRIBUTE_TYPE attributeType, String source, String v
 	 */
 	public BlackboardAttribute(Type attributeType, String source, String valueString) throws IllegalArgumentException {
 		if (attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING
-			&& attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
+				&& attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
 			throw new IllegalArgumentException("Type mismatched with value type");
 		}
 		this.artifactID = 0;
@@ -474,7 +482,10 @@ public void addSource(String source) throws TskCoreException {
 	 *                          case database.
 	 */
 	public BlackboardArtifact getParentArtifact() throws TskCoreException {
-		return sleuthkitCase.getBlackboardArtifact(artifactID);
+		if (parentArtifact == null) {
+			parentArtifact = sleuthkitCase.getBlackboardArtifact(artifactID);
+		}
+		return parentArtifact;
 	}
 
 	@Override
@@ -531,7 +542,11 @@ public String getDisplayString() {
 
 			case DATETIME: {
 				try {
-					final Content dataSource = getParentArtifact().getDataSource();
+					if (parentDataSourceID == null) {
+						BlackboardArtifact parent = getParentArtifact();
+						parentDataSourceID = parent.getDataSourceObjectID();
+					}
+					final Content dataSource = sleuthkitCase.getContentById(parentDataSourceID);
 					if ((dataSource != null) && (dataSource instanceof Image)) {
 						// return the date/time string in the timezone associated with the datasource,
 						Image image = (Image) dataSource;
@@ -540,11 +555,10 @@ public String getDisplayString() {
 					}
 				} catch (TskException ex) {
 					LOGGER.log(Level.WARNING, "Could not get timezone for image", ex); //NON-NLS
-					// return time string in default timezone
-					return TimeUtilities.epochToTime(getValueLong());
 				}
+				// return time string in default timezone
+				return TimeUtilities.epochToTime(getValueLong());
 			}
-			break;
 			case JSON: {
 				return getValueString();
 			}
@@ -612,6 +626,16 @@ void setCaseDatabase(SleuthkitCase sleuthkitCase) {
 	void setArtifactId(long artifactID) {
 		this.artifactID = artifactID;
 	}
+	
+	/**
+	 * Sets the parent data source id. The parent data source is defined
+	 * as being the data source of the parent artifact.
+	 * 
+	 * @param parentDataSourceID The parent data source id.
+	 */
+	void setParentDataSourceID(long parentDataSourceID) {
+		this.parentDataSourceID = parentDataSourceID;
+	}
 
 	/**
 	 * Gets the sources of this attribute.
@@ -808,7 +832,7 @@ public enum TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE {
 		/**
 		 * The value type of the attribute is a JSON string.
 		 */
-		JSON(6, "Json" );
+		JSON(6, "Json");
 
 		private final long typeId;
 		private final String typeName;
@@ -1336,88 +1360,89 @@ public enum ATTRIBUTE_TYPE {
 		TSK_TL_EVENT_TYPE(132, "TSK_TL_EVENT_TYPE", //NON-NLS
 				bundle.getString("BlackboardAttribute.tskTLEventType.text"),
 				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG),
-		
 		TSK_DATETIME_DELETED(133, "TSK_DATETIME_DELETED", //NON-NLS
 				bundle.getString("BlackboardAttribute.tskdatetimedeleted.text"),
 				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME),
-		
 		TSK_DATETIME_PASSWORD_RESET(134, "TSK_DATETIME_PASSWORD_RESET",
 				bundle.getString("BlackboardAttribute.tskdatetimepwdreset.text"),
 				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME),
-				
 		TSK_DATETIME_PASSWORD_FAIL(135, "TSK_DATETIME_PWD_FAIL",
 				bundle.getString("BlackboardAttribute.tskdatetimepwdfail.text"),
 				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME),
-		
 		TSK_DISPLAY_NAME(136, "TSK_DISPLAY_NAME",
 				bundle.getString("BlackboardAttribute.tskdisplayname.text"),
 				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING),
-		
 		TSK_PASSWORD_SETTINGS(137, "TSK_PASSWORD_SETTINGS",
 				bundle.getString("BlackboardAttribute.tskpasswordsettings.text"),
 				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING),
-		
 		TSK_ACCOUNT_SETTINGS(138, "TSK_ACCOUNT_SETTINGS",
 				bundle.getString("BlackboardAttribute.tskaccountsettings.text"),
 				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING),
-		
-		TSK_PASSWORD_HINT(139, "TSK_PASSWORD_HINT", 
-			bundle.getString("BlackboardAttribute.tskpasswordhint.text"), 
-			TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING),
-		
-		TSK_GROUPS (140, "TSK_GROUPS", 
+		TSK_PASSWORD_HINT(139, "TSK_PASSWORD_HINT",
+				bundle.getString("BlackboardAttribute.tskpasswordhint.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING),
+		TSK_GROUPS(140, "TSK_GROUPS",
 				bundle.getString("BlackboardAttribute.tskgroups.text"),
 				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING),
-		
 		/*
-		 * Use org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments to create and
-		 * process TSK_ATTACHMENTS attributes.
+		 * Use
+		 * org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments
+		 * to create and process TSK_ATTACHMENTS attributes.
 		 */
-		TSK_ATTACHMENTS (141, "TSK_ATTACHMENTS", 
+		TSK_ATTACHMENTS(141, "TSK_ATTACHMENTS",
 				bundle.getString("BlackboardAttribute.tskattachments.text"),
 				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON),
-		
 		/*
-		 * Use org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints to create and
-		 * process TSK_GEO_TRACKPOINTS attributes.
+		 * Use org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints
+		 * to create and process TSK_GEO_TRACKPOINTS attributes.
 		 */
 		TSK_GEO_TRACKPOINTS(142, "TSK_GEO_TRACKPOINTS",
-			bundle.getString("BlackboardAttribute.tskgeopath.text"),
-			TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON),
-		
+				bundle.getString("BlackboardAttribute.tskgeopath.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON),
 		/*
-		 * Use org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoints to create and
-		 * process TSK_GEO_WAYPOINTS attributes.
+		 * Use org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoints
+		 * to create and process TSK_GEO_WAYPOINTS attributes.
 		 */
 		TSK_GEO_WAYPOINTS(143, "TSK_GEO_WAYPOINTS",
-			bundle.getString("BlackboardAttribute.tskgeowaypoints.text"),
-			TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON),
-
+				bundle.getString("BlackboardAttribute.tskgeowaypoints.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON),
 		TSK_DISTANCE_TRAVELED(144, "TSK_DISTANCE_TRAVELED",
-			bundle.getString("BlackboardAttribute.tskdistancetraveled.text"),
-			TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE),
-		
+				bundle.getString("BlackboardAttribute.tskdistancetraveled.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE),
 		TSK_DISTANCE_FROM_HOMEPOINT(145, "TSK_DISTANCE_FROM_HOMEPOINT",
-			bundle.getString("BlackboardAttribute.tskdistancefromhome.text"),
-			TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE),
-		
+				bundle.getString("BlackboardAttribute.tskdistancefromhome.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE),
 		TSK_HASH_PHOTODNA(146, "TSK_HASH_PHOTODNA",
-			bundle.getString("BlackboardAttribute.tskhashphotodna.text"),
-			TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING),
-		
+				bundle.getString("BlackboardAttribute.tskhashphotodna.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING),
 		TSK_BYTES_SENT(147, "TSK_BYTES_SENT",
-	        bundle.getString("BlackboardAttribute.tskbytessent.text"),
-	        TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG),
-		
+				bundle.getString("BlackboardAttribute.tskbytessent.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG),
 		TSK_BYTES_RECEIVED(148, "TSK_BYTES_RECEIVED",
-	        bundle.getString("BlackboardAttribute.tskbytesreceived.text"),
-	        TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG),
-		
+				bundle.getString("BlackboardAttribute.tskbytesreceived.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG),
 		TSK_LAST_PRINTED_DATETIME(149, "TSK_LAST_PRINTED_DATETIME",
-	        bundle.getString("BlackboardAttribute.tsklastprinteddatetime.text"),
-	        TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME),
-		
-		
+				bundle.getString("BlackboardAttribute.tsklastprinteddatetime.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME),
+		TSK_RULE(150, "TSK_RULE",
+				bundle.getString("BlackboardAttribute.tskrule.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING),
+		TSK_ACTIVITY_TYPE(151, "TSK_ACTIVITY_TYPE",
+				bundle.getString("BlackboardAttribute.tskActivityType.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING),
+		/*
+		 * Use org.sleuthkit.datamodel.blackboardutils.attributes.GeoAreaPoints
+		 * to create and process TSK_GEO_AREAPOINTS attributes.
+		 */
+		TSK_GEO_AREAPOINTS(152, "TSK_GEO_AREAPOINTS",
+				bundle.getString("BlackboardAttribute.tskgeoareapoints.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON),
+		TSK_REALM(153, "TSK_REALM",
+				bundle.getString("BlackboardAttribute.tskRealm.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING),
+		TSK_HOST(154, "TSK_HOST",
+				bundle.getString("BlackboardAttribute.tskHost.text"),
+				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING),
 		;
 
 		private final int typeID;
diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
index 4bf4e9467b7b6f2f12ce2b935b078e4ace89f057..d5565d9b9449885c356b45714ac00e68b3a015fc 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
+++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
@@ -18,7 +18,7 @@ BlackboardArtifact.tagFile.text=Tagged Files
 BlackboardArtifact.tskTagArtifact.text=Tagged Results
 BlackboardArtifact.tskOsInfo.text=Operating System Information
 BlackboardArtifact.tskOsAccount.text=Operating System User Account
-BlackboardArtifact.tskServiceAccount.text=Accounts
+BlackboardArtifact.tskServiceAccount.text=Web Accounts
 BlackboardArtifact.tskToolOutput.text=Raw Tool Output
 BlackboardArtifact.tskContact.text=Contacts
 BlackboardArtifact.tskMessage.text=Messages
@@ -55,8 +55,17 @@ BlackboardArtifact.tskUserContentSuspected.text=User Content Suspected
 BlackboardArtifact.tskMetadata.text=Metadata
 BlackboardArtifact.tskTrack.text=GPS Track
 BlackboardArtifact.tskWebAccountType.text=Web Account Type
+BlackboardArtifact.tskScreenShots.text=Screenshots
+BlackboardArtifact.tskDhcpInfo.text=DHCP Information
+BlackboardArtifact.tskProgNotifications.text=Program Notifications
+BlackboardArtifact.tskBackupEvent.text=Backup Events
+BlackboardArtifact.tskDeletedProg.text=Deleted Programs
+BlackboardArtifact.tskUserDeviceEvent.text=User Device Events
 BlackboardArtifact.shortDescriptionDate.text=at {0}
 BlackboardArtifact.tskAssociatedObject.text=Associated Object
+BlackboardArtifact.tskWebCategorization.text=Web Categories
+BlackboardArtifact.tskYaraHit.text=YARA Hit
+BlackboardArtifact.tskGPSArea.text=GPS Area
 BlackboardAttribute.tskAccountType.text=Account Type
 BlackboardAttribute.tskUrl.text=URL
 BlackboardAttribute.tskDatetime.text=Date/Time
@@ -200,6 +209,11 @@ BlackboardAttribute.tskhashphotodna.text=PhotoDNA Hash
 BlackboardAttribute.tskbytessent.text=Bytes Sent
 BlackboardAttribute.tskbytesreceived.text=Bytes Received
 BlackboardAttribute.tsklastprinteddatetime.text=Last Printed Date
+BlackboardAttribute.tskgeoareapoints.text=List of points making up the outline of an area
+BlackboardAttribute.tskrule.text = Rule
+BlackboardAttribute.tskActivityType.text=Activity Type
+BlackboardAttribute.tskRealm.text=Realm
+BlackboardAttribute.tskHost.text=Host
 AbstractFile.readLocal.exception.msg4.text=Error reading local file\: {0}
 AbstractFile.readLocal.exception.msg1.text=Error reading local file, local path is not set
 AbstractFile.readLocal.exception.msg2.text=Error reading local file, it does not exist at local path\: {0}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED
index 4bf4e9467b7b6f2f12ce2b935b078e4ace89f057..d5565d9b9449885c356b45714ac00e68b3a015fc 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED
+++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED
@@ -18,7 +18,7 @@ BlackboardArtifact.tagFile.text=Tagged Files
 BlackboardArtifact.tskTagArtifact.text=Tagged Results
 BlackboardArtifact.tskOsInfo.text=Operating System Information
 BlackboardArtifact.tskOsAccount.text=Operating System User Account
-BlackboardArtifact.tskServiceAccount.text=Accounts
+BlackboardArtifact.tskServiceAccount.text=Web Accounts
 BlackboardArtifact.tskToolOutput.text=Raw Tool Output
 BlackboardArtifact.tskContact.text=Contacts
 BlackboardArtifact.tskMessage.text=Messages
@@ -55,8 +55,17 @@ BlackboardArtifact.tskUserContentSuspected.text=User Content Suspected
 BlackboardArtifact.tskMetadata.text=Metadata
 BlackboardArtifact.tskTrack.text=GPS Track
 BlackboardArtifact.tskWebAccountType.text=Web Account Type
+BlackboardArtifact.tskScreenShots.text=Screenshots
+BlackboardArtifact.tskDhcpInfo.text=DHCP Information
+BlackboardArtifact.tskProgNotifications.text=Program Notifications
+BlackboardArtifact.tskBackupEvent.text=Backup Events
+BlackboardArtifact.tskDeletedProg.text=Deleted Programs
+BlackboardArtifact.tskUserDeviceEvent.text=User Device Events
 BlackboardArtifact.shortDescriptionDate.text=at {0}
 BlackboardArtifact.tskAssociatedObject.text=Associated Object
+BlackboardArtifact.tskWebCategorization.text=Web Categories
+BlackboardArtifact.tskYaraHit.text=YARA Hit
+BlackboardArtifact.tskGPSArea.text=GPS Area
 BlackboardAttribute.tskAccountType.text=Account Type
 BlackboardAttribute.tskUrl.text=URL
 BlackboardAttribute.tskDatetime.text=Date/Time
@@ -200,6 +209,11 @@ BlackboardAttribute.tskhashphotodna.text=PhotoDNA Hash
 BlackboardAttribute.tskbytessent.text=Bytes Sent
 BlackboardAttribute.tskbytesreceived.text=Bytes Received
 BlackboardAttribute.tsklastprinteddatetime.text=Last Printed Date
+BlackboardAttribute.tskgeoareapoints.text=List of points making up the outline of an area
+BlackboardAttribute.tskrule.text = Rule
+BlackboardAttribute.tskActivityType.text=Activity Type
+BlackboardAttribute.tskRealm.text=Realm
+BlackboardAttribute.tskHost.text=Host
 AbstractFile.readLocal.exception.msg4.text=Error reading local file\: {0}
 AbstractFile.readLocal.exception.msg1.text=Error reading local file, local path is not set
 AbstractFile.readLocal.exception.msg2.text=Error reading local file, it does not exist at local path\: {0}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/CommunicationsUtils.java b/bindings/java/src/org/sleuthkit/datamodel/CommunicationsUtils.java
index 3228611feddbb27bb993517aede040d4b4677bca..6ad4e5adf99289c64c6adac3a6df0940f2e06195 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/CommunicationsUtils.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/CommunicationsUtils.java
@@ -177,7 +177,7 @@ private static boolean isValidPhoneChar(char ch) {
 	 * side to allow us to differentiate between app-specific identifiers and
 	 * email addresses. We realize that some emails can be sent within
 	 * enterprises without a ".', but that this is less common than encountering
-	 * app-specific identifiers of the form a@b.
+	 * app-specific identifiers of the form a(at)b.
 	 *
 	 * @param emailAddress String to check.
 	 *
diff --git a/bindings/java/src/org/sleuthkit/datamodel/HashUtility.java b/bindings/java/src/org/sleuthkit/datamodel/HashUtility.java
index 4ce6e192eea18ad4fcb1b32c5ed5ff4056071073..f152f8587991e76a4a1395f2407bd090b1122e07 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/HashUtility.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/HashUtility.java
@@ -218,7 +218,7 @@ static public String calculateMd5(AbstractFile file) throws IOException {
 	 *
 	 * @throws java.io.IOException
 	 * 
-	 * @decprecated Use calculateHashes() instead
+	 * @deprecated Use calculateHashes() instead
 	 */
 	@Deprecated
 	static public String calculateMd5Hash(Content content) throws IOException {
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
index 3514d7ace1404aff92b51fc5c9e54613c7b08137..753a130bc961d67f5052dd937deeac496e74f47f 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
@@ -4472,6 +4472,7 @@ public ArrayList<BlackboardAttribute> getBlackboardAttributes(final BlackboardAr
 						rs.getString("value_text"),
 						rs.getBytes("value_byte"), this
 				);
+				attr.setParentDataSourceID(artifact.getDataSourceObjectID());
 				attributes.add(attr);
 			}
 			return attributes;
diff --git a/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java b/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java
index c66755ac36cabafe5c6a2ba38df60417ecc73adc..cee51945acc4b4f0b86cc06ab7dcab0e29548c2d 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java
@@ -394,8 +394,14 @@ private long addBatchedFilesToDb() {
                         parentDirCache.put(key, objId);
                     }
                 } catch (TskCoreException ex) {
-                    logger.log(Level.SEVERE, "Error adding file to the database - parent object ID: " + computedParentObjId
+                    if (computedParentObjId > 0) {
+                        // Most likely a database error occurred
+                        logger.log(Level.SEVERE, "Error adding file to the database - parent object ID: " + computedParentObjId
                             + ", file system object ID: " + fileInfo.fsObjId + ", name: " + fileInfo.name, ex);
+                    } else {
+                        // The parent lookup failed
+                        logger.log(Level.SEVERE, "Error adding file to the database", ex);
+                    }
                 }
             }
             commitTransaction();
@@ -436,7 +442,7 @@ private long getParentObjId(FileInfo fileInfo) throws TskCoreException {
         } else {
             // There's no reason to do a database query since every folder added is being
             // stored in the cache.
-            throw new TskCoreException("Parent not found in cache (fsObjId: " +fileInfo.fsObjId + ", parMetaAddr: " + fileInfo.parMetaAddr
+            throw new TskCoreException("Could not find parent (fsObjId: " +fileInfo.fsObjId + ", parMetaAddr: " + fileInfo.parMetaAddr
                 + ", parSeq: " + fileInfo.parSeq + ", parentPath: " + parentPath + ")");
         }
     }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java
index 52c433a6a04435ffc0765cc714460906b2a7ceb6..b0cf6e64c4d151336013ee3a65ccaf28ad335283 100755
--- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java
@@ -29,6 +29,7 @@
 import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
 import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoints;
 import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints;
+import org.sleuthkit.datamodel.blackboardutils.attributes.GeoAreaPoints;
 
 /**
  * An artifact creation helper that adds geolocation artifacts to the case
@@ -38,6 +39,7 @@ public final class GeoArtifactsHelper extends ArtifactHelperBase {
 
 	private static final BlackboardAttribute.Type WAYPOINTS_ATTR_TYPE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS);
 	private static final BlackboardAttribute.Type TRACKPOINTS_ATTR_TYPE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS);
+	private static final BlackboardAttribute.Type AREAPOINTS_ATTR_TYPE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_AREAPOINTS);
 	private final String programName;
 
 	/**
@@ -168,5 +170,52 @@ public BlackboardArtifact addRoute(String routeName, Long creationTime, GeoWaypo
 
 		return artifact;
 	}
+	/**
+	 * Adds a TSK_GPS_AREA artifact to the case database. A Global Positioning
+	 * System (GPS) area artifact records an area on the map outlines by
+	 * an ordered set of GPS coordinates.
+	 *
+	 * @param areaName       The name of the GPS area, may be null.
+	 * @param areaPoints     The points that make up the outline of the area.  This list
+	 *                       should be non-null and non-empty.
+	 * @param moreAttributes Additional attributes for the TSK_GPS_AREA
+	 *                       artifact, may be null.
+	 *
+	 * @return	The TSK_GPS_AREA artifact that was added to the case database.
+	 *
+	 * @throws TskCoreException	        If there is an error creating the
+	 *                                  artifact.
+	 * @throws BlackboardException      If there is a error posting the artifact
+	 *                                  to the blackboard.
+	 * @throws IllegalArgumentException If the area points provided are null or
+	 *                                  empty.
+	 */
+	public BlackboardArtifact addArea(String areaName, GeoAreaPoints areaPoints, List<BlackboardAttribute> moreAttributes) throws TskCoreException, BlackboardException {
+		if (areaPoints == null || areaPoints.isEmpty()) {
+			throw new IllegalArgumentException(String.format("addArea was passed a null or empty list of points"));
+		}
+		
+		BlackboardArtifact artifact = getContent().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_AREA);
+		List<BlackboardAttribute> attributes = new ArrayList<>();
+		attributes.add(BlackboardJsonAttrUtil.toAttribute(AREAPOINTS_ATTR_TYPE, getModuleName(), areaPoints));
+
+		if (areaName != null) {
+			attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, getModuleName(), areaName));
+		}
+
+		if (programName != null) {
+			attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getModuleName(), programName));
+		}
+
+		if (moreAttributes != null) {
+			attributes.addAll(moreAttributes);
+		}
+
+		artifact.addAttributes(attributes);
+
+		getSleuthkitCase().getBlackboard().postArtifact(artifact, getModuleName());
+
+		return artifact;
+	}
 
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoAreaPoints.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoAreaPoints.java
new file mode 100644
index 0000000000000000000000000000000000000000..7707b4313e464cd31503e05864d0c5ec75c7d639
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoAreaPoints.java
@@ -0,0 +1,123 @@
+/*
+ * Sleuth Kit Data Model
+ *
+ * Copyright 2020 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.datamodel.blackboardutils.attributes;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A GeoAreaPoints object is a collection of AreaPoint objects.
+ * Every AreaPoint is a location in a geographic coordinate
+ * system with latitude and longitude axes.
+ *
+ * GeoWaypoints objects are designed to be used as the string value of the
+ * TSK_GEO_AREAPOINTS attribute of a TSK_GPS_AREA artifact. TSK_GPS_AREA
+ * artifacts are used to record a series of locations used to outline an
+ * area on the map.
+ */
+public class GeoAreaPoints implements Iterable<GeoAreaPoints.AreaPoint> {
+
+	private final List<AreaPoint> points;
+
+	/**
+	 * Constructs an empty GeoAreaPoints object.
+	 */
+	public GeoAreaPoints() {
+		points = new ArrayList<>();
+	}
+
+	/**
+	 * Adds an area point to this list of points outlining the area.
+	 *
+	 * @param areaPoint A point.
+	 */
+	public void addPoint(AreaPoint areaPoint) {
+		if (areaPoint == null) {
+			throw new IllegalArgumentException("addPoint was passed a null waypoint");
+		}
+
+		points.add(areaPoint);
+	}
+
+	/**
+	 * Returns whether or not this list of area points is empty.
+	 *
+	 * @return True or false.
+	 */
+	public boolean isEmpty() {
+		return points.isEmpty();
+	}
+
+	@Override
+	public Iterator<AreaPoint> iterator() {
+		return points.iterator();
+	}
+
+	/**
+	 * A representation of an area point, which is a a location in
+	 * a geographic coordinate system with latitude and longitude axes.
+	 * Area points are used to mark the outline of an area on the map.
+	 */
+	public static class AreaPoint {
+
+		@SerializedName("TSK_GEO_LATITUDE")
+		private final Double latitude;
+		@SerializedName("TSK_GEO_LONGITUDE")
+		private final Double longitude;
+
+		/**
+		 * Constructs a representation of an area point.
+		 *
+		 * @param latitude  The latitude of the area point.
+		 * @param longitude The longitude of the area point.
+		 */
+		public AreaPoint(Double latitude, Double longitude) {
+			if (latitude == null) {
+				throw new IllegalArgumentException("Constructor was passed null latitude");
+			}
+
+			if (longitude == null) {
+				throw new IllegalArgumentException("Constructor was passed null longitude");
+			}
+
+			this.latitude = latitude;
+			this.longitude = longitude;
+		}
+
+		/**
+		 * Gets the latitude of this area point.
+		 *
+		 * @return The latitude.
+		 */
+		public Double getLatitude() {
+			return latitude;
+		}
+
+		/**
+		 * Gets the longitude of this area point.
+		 *
+		 * @return The longitude.
+		 */
+		public Double getLongitude() {
+			return longitude;
+		}
+	}
+}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java
index fb61d5a53bd4234cf2d023a3e1a51fd0d20b807c..0fbf3db96d28238886b6e5281ec97d9b62eb652e 100755
--- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java
@@ -219,8 +219,12 @@ public Long getTimeStamp() {
 		public int compareTo(TrackPoint otherTP) {
 			Long otherTimeStamp = otherTP.getTimeStamp();
 
-			if (timestamp == null && otherTimeStamp != null) {
-				return -1;
+			if (timestamp == null) {
+				if (otherTimeStamp != null) {
+					return -1;
+				} else {
+					return 0;
+				}
 			} else if (timestamp != null && otherTimeStamp == null) {
 				return 1;
 			} else {
diff --git a/bindings/java/test/org/sleuthkit/datamodel/BottomUpTest.java b/bindings/java/test/org/sleuthkit/datamodel/BottomUpTest.java
index 96684cb857e3c5ba1533b5fa024e7e653cd11326..1add49a71da697538db64aaebca88a9ebcac53a9 100644
--- a/bindings/java/test/org/sleuthkit/datamodel/BottomUpTest.java
+++ b/bindings/java/test/org/sleuthkit/datamodel/BottomUpTest.java
@@ -77,6 +77,10 @@ public void testBottomUpDiff() {
 			String tempDirPath = dbFile.getAbsolutePath();
 			String dbPath = DataModelTestSuite.buildPath(tempDirPath, title, DataModelTestSuite.BTTMUP, ".db");
 			dbFile.delete();
+			if (dbFile.getParentFile() != null) {
+				dbFile.getParentFile().mkdirs();
+			}
+			
 			SleuthkitCase sk = SleuthkitCase.newCase(dbPath);
 			String timezone = "";
 			title = title + DataModelTestSuite.BTTMUP + ".txt";
diff --git a/bindings/java/test/org/sleuthkit/datamodel/CommunicationsManagerTest.java b/bindings/java/test/org/sleuthkit/datamodel/CommunicationsManagerTest.java
index d2e025b95d3fd6a6b0cc8baf5afe6cf0874934b2..a1c60a251ceffce30b9903dd899e13e18039f8b3 100644
--- a/bindings/java/test/org/sleuthkit/datamodel/CommunicationsManagerTest.java
+++ b/bindings/java/test/org/sleuthkit/datamodel/CommunicationsManagerTest.java
@@ -18,6 +18,7 @@
  */
 package org.sleuthkit.datamodel;
 
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -238,13 +239,15 @@ public CommunicationsManagerTest() {
 	@BeforeClass
 	public static void setUpClass() {
 		String tempDirPath = System.getProperty("java.io.tmpdir");
-		tempDirPath = tempDirPath.substring(0, tempDirPath.length() - 1);
 		try {
-			dbPath = tempDirPath + java.io.File.separator + TEST_DB;
+			dbPath = Paths.get(tempDirPath, TEST_DB).toString();
 
 			// Delete the DB file, in case 
 			java.io.File dbFile = new java.io.File(dbPath);
 			dbFile.delete();
+			if (dbFile.getParentFile() != null) {
+				dbFile.getParentFile().mkdirs();
+			}
 
 			// Create new case db
 			caseDB = SleuthkitCase.newCase(dbPath);
diff --git a/bindings/java/test/org/sleuthkit/datamodel/DataModelTestSuite.java b/bindings/java/test/org/sleuthkit/datamodel/DataModelTestSuite.java
index 0c22fbd617a741331104c709b3e782f44893c71d..2ec2da48f63072d83262cf107fb7c1375888d298 100644
--- a/bindings/java/test/org/sleuthkit/datamodel/DataModelTestSuite.java
+++ b/bindings/java/test/org/sleuthkit/datamodel/DataModelTestSuite.java
@@ -24,6 +24,7 @@
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
+import java.nio.file.Paths;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
@@ -122,6 +123,10 @@ public static void createOutput(String outputPath, String tempDirPath, List<Stri
 			standardFile.createNewFile();
 			java.io.File dbFile = new java.io.File(dbPath);
 			dbFile.delete();
+			if (dbFile.getParentFile() != null) {
+				dbFile.getParentFile().mkdirs();
+			}
+			
 			SleuthkitCase sk = SleuthkitCase.newCase(dbPath);
 
 			String timezone = "";
@@ -228,7 +233,7 @@ private static String stripExtension(String title) {
 	 * @return the path for an output file
 	 */
 	public static String buildPath(String path, String name, String type, String Ext) {
-		return path + java.io.File.separator + name + type + Ext;
+		return Paths.get(path, name + type + Ext).toString();
 	}
 
 	/**
diff --git a/tools/autotools/tsk_comparedir.cpp b/tools/autotools/tsk_comparedir.cpp
index 0ab30faf86af0add76f97ad1d4d858f7f2bbfbf1..db771d88e4c3cfc0aeda4641375fa1ca9bb35140 100644
--- a/tools/autotools/tsk_comparedir.cpp
+++ b/tools/autotools/tsk_comparedir.cpp
@@ -166,18 +166,18 @@ uint8_t
     struct stat status;
 
     strncpy(fullPath, m_lclDir, TSK_CD_BUFSIZE);
-    strncat(fullPath, a_dir, TSK_CD_BUFSIZE-strlen(fullPath));
+    strncat(fullPath, a_dir, TSK_CD_BUFSIZE-strlen(fullPath)-1);
     if ((dp = opendir(fullPath)) == NULL) {
         fprintf(stderr, "Error opening directory");
         return 1;
     }
     while ((dirp = readdir(dp)) != NULL) {
         strncpy(file, a_dir, TSK_CD_BUFSIZE);
-        strncat(file, "/", TSK_CD_BUFSIZE-strlen(file));
-        strncat(file, dirp->d_name, TSK_CD_BUFSIZE-strlen(file));
+        strncat(file, "/", TSK_CD_BUFSIZE-strlen(file)-1);
+        strncat(file, dirp->d_name, TSK_CD_BUFSIZE-strlen(file)-1);
 
         strncpy(fullPath, m_lclDir, TSK_CD_BUFSIZE);
-        strncat(fullPath, file, TSK_CD_BUFSIZE-strlen(fullPath));
+        strncat(fullPath, file, TSK_CD_BUFSIZE-strlen(fullPath)-1);
 
         stat(fullPath, &status);
         if (S_ISDIR(status.st_mode)) {
diff --git a/travis_install_libs.sh b/travis_install_libs.sh
index 05812f1f5b50470eaaeed3e878480fcfe006dcfc..0c659c11bd257dc9a387551585a0051df7c4542b 100755
--- a/travis_install_libs.sh
+++ b/travis_install_libs.sh
@@ -13,6 +13,6 @@ installLib() {
 	cd ..
 }
 
-installLib libvhdi 20201018
+installLib libvhdi 20201204
 installLib libvmdk 20200926
 
diff --git a/tsk/fs/yaffs.cpp b/tsk/fs/yaffs.cpp
index f5ec30a09dde02577111ed506242ec1c48b2fd2c..620f38e70410303c6a9d840b7e556e70322613fa 100755
--- a/tsk/fs/yaffs.cpp
+++ b/tsk/fs/yaffs.cpp
@@ -1762,7 +1762,6 @@ static uint8_t
 
     if (tsk_verbose)
         tsk_fprintf(stderr, "yaffs_make_deleted: Making virtual deleted node\n");
-
     if (yaffs_make_directory(yaffsfs, fs_file, YAFFS_OBJECT_DELETED, YAFFS_OBJECT_DELETED_NAME))
         return 1;
 
@@ -1931,7 +1930,6 @@ static uint8_t
 
     case YAFFS_TYPE_HARDLINK:
     case YAFFS_TYPE_UNKNOWN:
-    default:
         if (tsk_verbose)
             tsk_fprintf(stderr, "yaffs_inode_lookup: is *** UNHANDLED *** (type %d, header at 0x%x)\n", type, version->ycv_header_chunk->ycc_offset);
         // We can still set a few things
@@ -1955,6 +1953,10 @@ static uint8_t
         strncpy(a_fs_file->meta->name2->name, real_name,
             TSK_FS_META_NAME_LIST_NSIZE);
         break;
+    default:
+        if (tsk_verbose)
+            tsk_fprintf(stderr, "yaffs_inode_lookup: type is invalid (type %d, header at 0x%x)\n", type, version->ycv_header_chunk->ycc_offset);
+        return 1;
     }
 
     /* Who owns this? I'm following the way FATFS does it by freeing + NULLing