diff --git a/Core/build.xml b/Core/build.xml
index f32b5a28c3..a1290ef2e8 100644
--- a/Core/build.xml
+++ b/Core/build.xml
@@ -98,6 +98,8 @@
tofile="${ext.dir}/sleuthkit-${TSK_VERSION}.jar"/>
+
sleuthkit 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.autopsy.centralrepository.datamodel;
+
+/**
+ * Encapsulates the concept of an examiner.
+ */
+final public class CentralRepoExaminer {
+
+ private final long id; // Row id in the examiners table in central repo database.
+ private final String loginName;
+
+ public CentralRepoExaminer(long id, String loginName) {
+ this.id = id;
+ this.loginName = loginName;
+ }
+
+ /**
+ * Returns the id.
+ *
+ * @return id
+ */
+ public long getId() {
+ return id;
+ }
+
+ /**
+ * Returns the login name of examiner.
+ *
+ * @return login name
+ */
+ public String getLoginName() {
+ return this.loginName;
+ }
+
+}
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepository.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepository.java
index 21fd832c00..b71606594f 100755
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepository.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepository.java
@@ -26,7 +26,6 @@ import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
-import org.sleuthkit.datamodel.Examiner;
/**
* Main interface for interacting with the database
@@ -163,21 +162,15 @@ public interface CentralRepository {
void updateCase(CorrelationCase eamCase) throws CentralRepoException;
/**
- * Updates the examiners table, by adding current logged in user to
- * examiners table, if not already in there.
- *
- * @throws CentralRepoException If there is an error.
+ * Queries the examiner table for the given user name.
+ * Adds a row if the user is not found in the examiner table.
*
+ * @param examinerLoginName user name to look for.
+ * @return CentralRepoExaminer for the given user name.
+ * @throws CentralRepoException If there is an error in looking up or
+ * inserting the user in the examiners table.
*/
- void updateExaminers() throws CentralRepoException;
-
- /**
- * Get the Examiner object for the current logged in user.
- *
- * @return Examiner Current examiner.
- * @throws CentralRepoException
- */
- Examiner getCurrentCentralRepoExaminer() throws CentralRepoException;
+ CentralRepoExaminer getOrInsertExaminer(String examinerLoginName) throws CentralRepoException;
/**
* Retrieves Central Repo case based on an Autopsy Case
@@ -872,10 +865,5 @@ public interface CentralRepository {
* @throws CentralRepoException
*/
CentralRepoAccount getOrCreateAccount(CentralRepoAccount.CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException;
-
-
- /**
- * Clears all caches.
- */
- void clearCaches();
+
}
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepositoryDbQueryCallback.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepositoryDbQueryCallback.java
index cedf5d0880..ffc02b4a9f 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepositoryDbQueryCallback.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepositoryDbQueryCallback.java
@@ -1,7 +1,7 @@
/*
* Central Repository
*
- * Copyright 2015-2020 Basis Technology Corp.
+ * Copyright 2020 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,7 +27,7 @@ import java.sql.SQLException;
* the results themselves.
*
*/
-public interface CentralRepositoryDbQueryCallback {
+interface CentralRepositoryDbQueryCallback {
/**
* Process the resultset from a query.
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Persona.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Persona.java
index a05fee9b16..fd4e26eca2 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Persona.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Persona.java
@@ -27,7 +27,6 @@ import java.util.Collections;
import java.util.Objects;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
-import org.sleuthkit.datamodel.Examiner;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
@@ -116,7 +115,7 @@ public class Persona {
}
// Persona name to use if no name is specified.
- private static final String DEFAULT_PERSONA_NAME = "NoName";
+ private static final String DEFAULT_PERSONA_NAME = "Unknown";
// primary key in the Personas table in CR database
private final long id;
@@ -126,7 +125,7 @@ public class Persona {
private final long createdDate;
private final long modifiedDate;
private final PersonaStatus status;
- private final Examiner examiner;
+ private final CentralRepoExaminer examiner;
public long getId() {
return id;
@@ -156,11 +155,11 @@ public class Persona {
return status;
}
- public Examiner getExaminer() {
+ public CentralRepoExaminer getExaminer() {
return examiner;
}
- Persona(long id, String uuidStr, String name, String comment, long created_date, long modified_date, PersonaStatus status, Examiner examiner) {
+ Persona(long id, String uuidStr, String name, String comment, long created_date, long modified_date, PersonaStatus status, CentralRepoExaminer examiner) {
this.id = id;
this.uuidStr = uuidStr;
this.name = name;
@@ -207,7 +206,7 @@ public class Persona {
private static Persona createPersona(String name, String comment, PersonaStatus status) throws CentralRepoException {
// generate a UUID for the persona
String uuidStr = UUID.randomUUID().toString();
- Examiner examiner = CentralRepository.getInstance().getCurrentCentralRepoExaminer();
+ CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli();
@@ -240,7 +239,7 @@ public class Persona {
*/
public PersonaAccount addAccountToPersona(CentralRepoAccount account, String justification, Persona.Confidence confidence) throws CentralRepoException {
- Examiner currentExaminer = CentralRepository.getInstance().getCurrentCentralRepoExaminer();
+ CentralRepoExaminer currentExaminer = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli();
@@ -263,19 +262,18 @@ public class Persona {
*/
private static class PersonaQueryCallback implements CentralRepositoryDbQueryCallback {
- private Persona persona = null;
+ private final Collection personaList = new ArrayList<>();
@Override
public void process(ResultSet rs) throws SQLException {
while (rs.next()) {
- Examiner examiner = new Examiner(
+ CentralRepoExaminer examiner = new CentralRepoExaminer(
rs.getInt("examiner_id"),
- rs.getString("login_name"),
- rs.getString("display_name"));
+ rs.getString("login_name"));
PersonaStatus status = PersonaStatus.fromId(rs.getInt("status_id"));
- persona = new Persona(
+ Persona persona = new Persona(
rs.getInt("id"),
rs.getString("uuid"),
rs.getString("name"),
@@ -285,14 +283,24 @@ public class Persona {
status,
examiner
);
+
+ personaList.add(persona);
}
}
- Persona getPersona() {
- return persona;
+ Collection getPersonas() {
+ return Collections.unmodifiableCollection(personaList);
}
};
+ // Partial query string to select from personas table,
+ // just supply the where clause.
+ private static final String PERSONA_QUERY =
+ "SELECT p.id, p.uuid, p.name, p.comment, p.created_date, p.modified_date, p.status_id, p.examiner_id, e.login_name, e.display_name "
+ + "FROM personas as p "
+ + "INNER JOIN examiners as e ON e.id = p.examiner_id ";
+
+
/**
* Gets the row from the Personas table with the given UUID, creates and
* returns the Persona from that data.
@@ -306,41 +314,39 @@ public class Persona {
*/
private static Persona getPersonaByUUID(String uuid) throws CentralRepoException {
- String queryClause = "SELECT p.id, p.uuid, p.name, p.comment, p.created_date, p.modified_date, p.status_id, p.examiner_id, e.login_name, e.display_name "
- + "FROM personas as p "
- + "INNER JOIN examiners as e ON e.id = p.examiner_id "
+ String queryClause =
+ PERSONA_QUERY
+ "WHERE p.uuid = '" + uuid + "'";
PersonaQueryCallback queryCallback = new PersonaQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
- return queryCallback.getPersona();
+ Collection personas = queryCallback.getPersonas();
+
+ return personas.isEmpty() ? null : personas.iterator().next();
}
/**
- * Gets the row from the Personas table with the given id, creates and
- * returns the Persona from that data.
+ * Gets the rows from the Personas table with matching name.
*
- * @param id Persona id to match.
- * @return Persona matching the given UUID, may be null if no match is
- * found.
+ * @param partialName Name substring to match.
+ * @return Collection of personas matching the given name substring, may be
+ * empty if no match is found.
*
* @throws CentralRepoException If there is an error in querying the
* Personas table.
*/
- public static Persona getPersonaById(long id) throws CentralRepoException {
+ public static Collection getPersonaByName(String partialName) throws CentralRepoException {
- String queryClause = "SELECT p.id, p.uuid, p.name, p.comment, p.created_date, p.modified_date, p.status_id, p.examiner_id, e.login_name, e.display_name "
- + "FROM personas as p "
- + "INNER JOIN examiners as e ON e.id = p.examiner_id "
- + "WHERE p.id = " + id;
+ String queryClause = PERSONA_QUERY
+ + "WHERE LOWER(p.name) LIKE " + "LOWER('%" + partialName + "%')" ;
PersonaQueryCallback queryCallback = new PersonaQueryCallback();
CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
- return queryCallback.getPersona();
+ return queryCallback.getPersonas();
}
-
+
/**
* Creates an alias for the Persona.
*
@@ -403,32 +409,7 @@ public class Persona {
public Collection getPersonaAccounts() throws CentralRepoException {
return PersonaAccount.getPersonaAccountsForPersona(this.getId());
}
-
- /**
- * Gets all the Persona for the specified Account.
- *
- * @param accountId Id of account for which to get the Personas for.
- * @return Collection of PersonaAccounts. may be empty.
- *
- * @throws CentralRepoException If there is an error in getting the
- * persona_account.
- */
- public static Collection getPersonaAccountsForAccount(long accountId) throws CentralRepoException {
- return PersonaAccount.getPersonaAccountsForAccount(accountId);
- }
-
- /**
- * Get all accounts associated with the persona.
- *
- * @return Collection of all accounts associated with the given persona, may
- * be empty.
- * @throws CentralRepoException If there is an error in getting the
- * accounts.
- */
- public Collection getAccounts() throws CentralRepoException {
- return PersonaAccount.getAccountsForPersona(this.getId());
- }
-
+
/**
* Callback to process a query that gets cases for account instances of an
* account
@@ -464,7 +445,7 @@ public class Persona {
Collection casesForPersona = new ArrayList<>();
// get all accounts for this persona
- Collection accounts = this.getAccounts();
+ Collection accounts = PersonaAccount.getAccountsForPersona(this.getId());
for (CentralRepoAccount account : accounts) {
int corrTypeId = account.getAccountType().getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
@@ -527,7 +508,7 @@ public class Persona {
public Collection getDataSources() throws CentralRepoException {
Collection correlationDataSources = new ArrayList<>();
- Collection accounts = this.getAccounts();
+ Collection accounts = PersonaAccount.getAccountsForPersona(this.getId());
for (CentralRepoAccount account : accounts) {
int corrTypeId = account.getAccountType().getCorrelationTypeId();
CorrelationAttributeInstance.Type correlationType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
@@ -563,10 +544,9 @@ public class Persona {
while (resultSet.next()) {
// examiner that created the persona
- Examiner personaExaminer = new Examiner(
+ CentralRepoExaminer personaExaminer = new CentralRepoExaminer(
resultSet.getInt("persona_examiner_id"),
- resultSet.getString("persona_examiner_login_name"),
- resultSet.getString("persona_examiner_display_name"));
+ resultSet.getString("persona_examiner_login_name"));
// create persona
PersonaStatus status = PersonaStatus.fromId(resultSet.getInt("status_id"));
@@ -653,7 +633,7 @@ public class Persona {
* @return Collection of personas, may be empty.
* @throws CentralRepoException
*/
- public static Collection getPersonaForDataSource(CorrelationDataSource dataSource) throws CentralRepoException {
+ public static Collection getPersonasForDataSource(CorrelationDataSource dataSource) throws CentralRepoException {
Collection personaList = new ArrayList<>();
Collection accountTypes = CentralRepository.getInstance().getAllAccountTypes();
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java
index 4e11cf8202..3b74b82aea 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java
@@ -23,7 +23,6 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import org.sleuthkit.datamodel.Examiner;
/**
* This class represents an association between a Persona and an Account.
@@ -39,9 +38,9 @@ public class PersonaAccount {
private final String justification;
private final Persona.Confidence confidence;
private final long dateAdded;
- private final Examiner examiner;
+ private final CentralRepoExaminer examiner;
- public PersonaAccount(Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence, long dateAdded, Examiner examiner) {
+ public PersonaAccount(Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) {
this.persona = persona;
this.account = account;
this.justification = justification;
@@ -70,7 +69,7 @@ public class PersonaAccount {
return dateAdded;
}
- public Examiner getExaminer() {
+ public CentralRepoExaminer getExaminer() {
return examiner;
}
@@ -86,16 +85,14 @@ public class PersonaAccount {
while (rs.next()) {
// examiner that created the persona/account association
- Examiner paExaminer = new Examiner(
+ CentralRepoExaminer paExaminer = new CentralRepoExaminer(
rs.getInt("pa_examiner_id"),
- rs.getString("pa_examiner_login_name"),
- rs.getString("pa_examiner_display_name"));
+ rs.getString("pa_examiner_login_name"));
// examiner that created the persona
- Examiner personaExaminer = new Examiner(
+ CentralRepoExaminer personaExaminer = new CentralRepoExaminer(
rs.getInt("persona_examiner_id"),
- rs.getString("persona_examiner_login_name"),
- rs.getString("persona_examiner_display_name"));
+ rs.getString("persona_examiner_login_name"));
// create persona
Persona.PersonaStatus status = Persona.PersonaStatus.fromId(rs.getInt("status_id"));
@@ -175,7 +172,7 @@ public class PersonaAccount {
* @throws CentralRepoException If there is an error in getting the
* persona_account.
*/
- static Collection getPersonaAccountsForAccount(long accountId) throws CentralRepoException {
+ public static Collection getPersonaAccountsForAccount(long accountId) throws CentralRepoException {
String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE
+ " WHERE persona_accounts.account_id = " + accountId;
@@ -184,6 +181,27 @@ public class PersonaAccount {
return queryCallback.getPersonaAccountsList();
}
+
+ /**
+ * Gets all the Persona associated with all the accounts matching the given
+ * account identifier substring.
+ *
+ * @param accountIdentifierSubstring Account identifier substring to search
+ * for.
+ * @return Collection of PersonaAccounts. may be empty.
+ *
+ * @throws CentralRepoException If there is an error in getting the
+ * persona_account.
+ */
+ public static Collection getPersonaAccountsForAccountIdentifier(String accountIdentifierSubstring) throws CentralRepoException {
+ String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE
+ + " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')";
+
+ PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback();
+ CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback);
+
+ return queryCallback.getPersonaAccountsList();
+ }
/**
* Callback to process a query that gets all accounts belonging to a
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAlias.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAlias.java
index cff33619dd..cdebd7097d 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAlias.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAlias.java
@@ -25,7 +25,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.apache.commons.lang3.StringUtils;
-import org.sleuthkit.datamodel.Examiner;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
@@ -40,7 +39,7 @@ public class PersonaAlias {
private final String justification;
private final Persona.Confidence confidence;
private final long dateAdded;
- private final Examiner examiner;
+ private final CentralRepoExaminer examiner;
public long getPersonaId() {
return personaId;
@@ -62,11 +61,11 @@ public class PersonaAlias {
return dateAdded;
}
- public Examiner getExaminer() {
+ public CentralRepoExaminer getExaminer() {
return examiner;
}
- public PersonaAlias(long personaId, String alias, String justification, Persona.Confidence confidence, long dateAdded, Examiner examiner) {
+ public PersonaAlias(long personaId, String alias, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) {
this.personaId = personaId;
this.alias = alias;
this.justification = justification;
@@ -88,7 +87,7 @@ public class PersonaAlias {
*/
static PersonaAlias addPersonaAlias(Persona persona, String alias, String justification, Persona.Confidence confidence) throws CentralRepoException {
- Examiner examiner = CentralRepository.getInstance().getCurrentCentralRepoExaminer();
+ CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli();
@@ -118,10 +117,9 @@ public class PersonaAlias {
public void process(ResultSet rs) throws SQLException {
while (rs.next()) {
- Examiner examiner = new Examiner(
+ CentralRepoExaminer examiner = new CentralRepoExaminer(
rs.getInt("examiner_id"),
- rs.getString("login_name"),
- rs.getString("display_name"));
+ rs.getString("login_name"));
PersonaAlias alias = new PersonaAlias(
rs.getLong("persona_id"),
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaMetadata.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaMetadata.java
index 691772d264..713f00a981 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaMetadata.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaMetadata.java
@@ -25,7 +25,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.apache.commons.lang3.StringUtils;
-import org.sleuthkit.datamodel.Examiner;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
@@ -43,7 +42,7 @@ public class PersonaMetadata {
private final String justification;
private final Persona.Confidence confidence;
private final long dateAdded;
- private final Examiner examiner;
+ private final CentralRepoExaminer examiner;
public long getPersonaId() {
return personaId;
@@ -69,11 +68,11 @@ public class PersonaMetadata {
return dateAdded;
}
- public Examiner getExaminer() {
+ public CentralRepoExaminer getExaminer() {
return examiner;
}
- public PersonaMetadata(long personaId, String name, String value, String justification, Persona.Confidence confidence, long dateAdded, Examiner examiner) {
+ public PersonaMetadata(long personaId, String name, String value, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) {
this.personaId = personaId;
this.name = name;
this.value = value;
@@ -97,7 +96,7 @@ public class PersonaMetadata {
*/
static PersonaMetadata addPersonaMetadata(long personaId, String name, String value, String justification, Persona.Confidence confidence) throws CentralRepoException {
- Examiner examiner = CentralRepository.getInstance().getCurrentCentralRepoExaminer();
+ CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(System.getProperty("user.name"));
Instant instant = Instant.now();
Long timeStampMillis = instant.toEpochMilli();
@@ -128,10 +127,9 @@ public class PersonaMetadata {
public void process(ResultSet rs) throws SQLException {
while (rs.next()) {
- Examiner examiner = new Examiner(
+ CentralRepoExaminer examiner = new CentralRepoExaminer(
rs.getInt("examiner_id"),
- rs.getString("login_name"),
- rs.getString("display_name"));
+ rs.getString("login_name"));
PersonaMetadata metaData = new PersonaMetadata(
rs.getLong("persona_id"),
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresCentralRepo.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresCentralRepo.java
index 6dd8b7efea..f1afb941bc 100755
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresCentralRepo.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresCentralRepo.java
@@ -71,8 +71,6 @@ final class PostgresCentralRepo extends RdbmsCentralRepo {
private PostgresCentralRepo() throws CentralRepoException {
dbSettings = new PostgresCentralRepoSettings();
bulkArtifactsThreshold = dbSettings.getBulkThreshold();
-
- this.updateExaminers();
}
@Override
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/RdbmsCentralRepo.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/RdbmsCentralRepo.java
index 39f48b0e66..de88396de7 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/RdbmsCentralRepo.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/RdbmsCentralRepo.java
@@ -51,7 +51,7 @@ import org.sleuthkit.autopsy.healthmonitor.HealthMonitor;
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
-import org.sleuthkit.datamodel.Examiner;
+import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskData;
/**
@@ -102,8 +102,6 @@ abstract class RdbmsCentralRepo implements CentralRepository {
// Update Test code if this changes. It's hard coded there.
static final int DEFAULT_BULK_THRESHHOLD = 1000;
- private Examiner cachedCurrentExaminer = null;
-
private static final int QUERY_STR_MAX_LEN = 1000;
@@ -222,7 +220,6 @@ abstract class RdbmsCentralRepo implements CentralRepository {
/**
* Reset the contents of the caches associated with EamDb results.
*/
- @Override
public final void clearCaches() {
synchronized(typeCache) {
typeCache.invalidateAll();
@@ -233,7 +230,6 @@ abstract class RdbmsCentralRepo implements CentralRepository {
dataSourceCacheByDsObjectId.invalidateAll();
dataSourceCacheById.invalidateAll();
accountsCache.invalidateAll();
- cachedCurrentExaminer = null;
}
/**
@@ -2520,72 +2516,6 @@ abstract class RdbmsCentralRepo implements CentralRepository {
}
}
- /**
- * Updates the examiners table, by adding current logged in user to
- * examiners table, if not already in there.
- *
- * @throws CentralRepoException If there is an error.
- *
- */
- @Override
- public void updateExaminers() throws CentralRepoException {
-
- String loginName = System.getProperty("user.name");
- if (loginName.isEmpty()) {
- logger.log(Level.SEVERE, "Cannot determine logged in user name");
- return;
- }
-
- try (Connection connection = connect();
- Statement statement = connection.createStatement();) {
-
- String insertSQL;
- switch (CentralRepoDbManager.getSavedDbChoice().getDbPlatform()) {
- case POSTGRESQL:
- insertSQL = "INSERT INTO examiners (login_name) VALUES ('" + loginName + "')" + getConflictClause(); //NON-NLS
- break;
- case SQLITE:
- insertSQL = "INSERT OR IGNORE INTO examiners (login_name) VALUES ('" + loginName + "')";
- break;
- default:
- throw new CentralRepoException(String.format("Cannot add examiner to currently selected CR database platform %s", CentralRepoDbManager.getSavedDbChoice().getDbPlatform()));
- }
- statement.execute(insertSQL); //NON-NLS
- } catch (SQLException ex) {
- throw new CentralRepoException("Error inserting row in examiners", ex);
- }
- }
-
- @Override
- public Examiner getCurrentCentralRepoExaminer() throws CentralRepoException {
-
- // return cached value if there's one
- if (cachedCurrentExaminer != null) {
- return cachedCurrentExaminer;
- }
- String loginName = System.getProperty("user.name");
- if (loginName == null || loginName.isEmpty()) {
- throw new CentralRepoException("Failed to determine logged in user name.");
- }
-
- String querySQL = "SELECT * FROM examiners WHERE login_name = '" + loginName + "'";
-
- try (Connection connection = connect();
- Statement statement = connection.createStatement();
- ResultSet resultSet = statement.executeQuery(querySQL);) {
-
- if (resultSet.next()) {
- cachedCurrentExaminer = new Examiner(resultSet.getLong("id"), resultSet.getString("login_name"), resultSet.getString("display_name"));
- return cachedCurrentExaminer;
- } else {
- throw new CentralRepoException("Error getting examiner for name = " + loginName);
- }
-
- } catch (SQLException ex) {
- throw new CentralRepoException("Error getting examiner for name = " + loginName, ex);
- }
- }
-
@Override
public void executeInsertSQL(String insertClause) throws CentralRepoException {
@@ -2766,6 +2696,61 @@ abstract class RdbmsCentralRepo implements CentralRepository {
}
}
+ /**
+ * Queries the examiner table for the given user name.
+ * Adds a row if the user is not found in the examiner table.
+ *
+ * @param examinerLoginName user name to look for.
+ * @return CentralRepoExaminer for the given user name.
+ * @throws CentralRepoException If there is an error in looking up or
+ * inserting the user in the examiners table.
+ */
+
+ @Override
+ public CentralRepoExaminer getOrInsertExaminer(String examinerLoginName) throws CentralRepoException {
+
+ String querySQL = "SELECT * FROM examiners WHERE login_name = '" + SleuthkitCase.escapeSingleQuotes(examinerLoginName) + "'";
+ try (Connection connection = connect();
+ Statement statement = connection.createStatement();
+ ResultSet resultSet = statement.executeQuery(querySQL);) {
+
+ if (resultSet.next()) {
+ return new CentralRepoExaminer(resultSet.getLong("id"), resultSet.getString("login_name"));
+ } else {
+ // Could not find this user in the Examiner table, add a row for it.
+ try {
+ String insertSQL;
+ switch (CentralRepoDbManager.getSavedDbChoice().getDbPlatform()) {
+ case POSTGRESQL:
+ insertSQL = "INSERT INTO examiners (login_name) VALUES ('" + SleuthkitCase.escapeSingleQuotes(examinerLoginName) + "')" + getConflictClause(); //NON-NLS
+ break;
+ case SQLITE:
+ insertSQL = "INSERT OR IGNORE INTO examiners (login_name) VALUES ('" + SleuthkitCase.escapeSingleQuotes(examinerLoginName) + "')"; //NON-NLS
+ break;
+ default:
+ throw new CentralRepoException(String.format("Cannot add examiner to currently selected CR database platform %s", CentralRepoDbManager.getSavedDbChoice().getDbPlatform())); //NON-NLS
+ }
+ statement.execute(insertSQL);
+
+ // Query the table again to get the row for the user
+ try (ResultSet resultSet2 = statement.executeQuery(querySQL)) {
+ if (resultSet2.next()) {
+ return new CentralRepoExaminer(resultSet2.getLong("id"), resultSet2.getString("login_name"));
+ } else {
+ throw new CentralRepoException("Error getting examiner for name = " + examinerLoginName);
+ }
+ }
+
+ } catch (SQLException ex) {
+ throw new CentralRepoException("Error inserting row in examiners", ex);
+ }
+ }
+
+ } catch (SQLException ex) {
+ throw new CentralRepoException("Error getting examiner for name = " + examinerLoginName, ex);
+ }
+ }
+
/**
* Update an existing organization.
*
diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteCentralRepo.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteCentralRepo.java
index aac89adf91..80f1df746c 100644
--- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteCentralRepo.java
+++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteCentralRepo.java
@@ -78,8 +78,6 @@ final class SqliteCentralRepo extends RdbmsCentralRepo {
private SqliteCentralRepo() throws CentralRepoException {
dbSettings = new SqliteCentralRepoSettings();
bulkArtifactsThreshold = dbSettings.getBulkThreshold();
-
- this.updateExaminers();
}
@Override
@@ -855,15 +853,6 @@ final class SqliteCentralRepo extends RdbmsCentralRepo {
}
}
- @Override
- public void updateExaminers() throws CentralRepoException {
- try {
- acquireSharedLock();
- super.updateExaminers();
- } finally {
- releaseSharedLock();
- }
- }
/**
* Check whether a reference set with the given name/version is in the
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties-MERGED
index c8cebe69fc..4d4d3cf10f 100755
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties-MERGED
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties-MERGED
@@ -17,7 +17,10 @@ ContextViewer.messageFrom=From
ContextViewer.messageOn=On
ContextViewer.messageTo=To
ContextViewer.on=Opened at
+ContextViewer.programExecution=Program Execution:
ContextViewer.recentDocs=Recent Documents:
+ContextViewer.runOn=Program Run On
+ContextViewer.runUnknown=\ Program Run at unknown time
ContextViewer.title=Context
ContextViewer.toolTip=Displays context for selected file.
ContextViewer.unknown=Opened at unknown time
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.java
index 7200f6747f..f7f1f6559e 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/contextviewer/ContextViewer.java
@@ -334,7 +334,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
@NbBundle.Messages({
"ContextViewer.attachmentSource=Attached to: ",
"ContextViewer.downloadSource=Downloaded from: ",
- "ContextViewer.recentDocs=Recent Documents: "
+ "ContextViewer.recentDocs=Recent Documents: ",
+ "ContextViewer.programExecution=Program Execution: "
})
private void setSourceFields(BlackboardArtifact associatedArtifact) throws TskCoreException {
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID()
@@ -357,6 +358,11 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
javax.swing.JPanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact);
contextUsagePanels.add(usagePanel);
+ } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() == associatedArtifact.getArtifactTypeID()) {
+ String sourceName = Bundle.ContextViewer_programExecution();
+ String sourceText = programExecArtifactToString(associatedArtifact);
+ javax.swing.JPanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact);
+ contextUsagePanels.add(usagePanel);
}
}
@@ -416,6 +422,36 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
return sb.toString();
}
+ /**
+ * Returns a display string with Program Execution
+ * artifact.
+ *
+ * @param artifact artifact to get doc from.
+ *
+ * @return Display string with download URL and date/time.
+ *
+ * @throws TskCoreException
+ */
+ @NbBundle.Messages({
+ "ContextViewer.runOn=Program Run On",
+ "ContextViewer.runUnknown= Program Run at unknown time"
+ })
+ private String programExecArtifactToString(BlackboardArtifact artifact) throws TskCoreException {
+ StringBuilder sb = new StringBuilder(ARTIFACT_STR_MAX_LEN);
+ Map attributesMap = getAttributesMap(artifact);
+
+ BlackboardAttribute attribute = attributesMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
+
+ if (BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() == artifact.getArtifactTypeID()) {
+ if (attribute != null && attribute.getValueLong() > 0) {
+ appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, attributesMap, Bundle.ContextViewer_runOn());
+ } else {
+ sb.append(Bundle.ContextViewer_runUnknown());
+ }
+ }
+ return sb.toString();
+ }
+
/**
* Returns a abbreviated display string for a message artifact.
*
diff --git a/Core/src/org/sleuthkit/autopsy/events/AutopsyEventPublisher.java b/Core/src/org/sleuthkit/autopsy/events/AutopsyEventPublisher.java
index 02a5d2e2ab..f3fb273bcf 100644
--- a/Core/src/org/sleuthkit/autopsy/events/AutopsyEventPublisher.java
+++ b/Core/src/org/sleuthkit/autopsy/events/AutopsyEventPublisher.java
@@ -42,9 +42,9 @@ public final class AutopsyEventPublisher {
private static final Logger logger = Logger.getLogger(AutopsyEventPublisher.class.getName());
private static final int MAX_REMOTE_EVENT_PUBLISH_TRIES = 1;
private final LocalEventPublisher localPublisher; // LocalEventPublisher is thread-safe
- @GuardedBy("this)")
+ @GuardedBy("this")
private RemoteEventPublisher remotePublisher;
- @GuardedBy("this)")
+ @GuardedBy("this")
private String currentChannelName;
/**
diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoPersonasTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoPersonasTest.java
index 141a23e66d..2b1d72534e 100644
--- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoPersonasTest.java
+++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoPersonasTest.java
@@ -165,13 +165,8 @@ public class CentralRepoPersonasTest extends TestCase {
Assert.fail("Failed to create central repo database, should be located at + " + crDbFilePath);
}
- // clear caches to match the clean slate database.
- CentralRepository.getInstance().clearCaches();
- // Add current logged in user to examiners table - since we delete the DB after every test.
- CentralRepository.getInstance().updateExaminers();
-
- // Set up some default objects to be used by the tests
+ // Set up some default objects to be used by the tests
try {
case1 = new CorrelationCase(CASE_1_UUID, "case1");
case1 = CentralRepository.getInstance().newCase(case1);
@@ -304,6 +299,9 @@ public class CentralRepoPersonasTest extends TestCase {
Assert.assertEquals(DOG_PERSONA_NAME, pa1.getPersona().getName());
Assert.assertEquals(status.name(), dogPersona.getStatus().name());
Assert.assertTrue(dogPersona.getExaminer().getLoginName().equalsIgnoreCase(pa1.getExaminer().getLoginName()));
+
+ // Assert that the persona was created by the currently logged in user
+ Assert.assertTrue(dogPersona.getExaminer().getLoginName().equalsIgnoreCase(System.getProperty("user.name")));
// Assert that Persona was created within the last 10 mins
Assert.assertTrue(Instant.now().toEpochMilli() - pa1.getDateAdded() < 600 * 1000);
@@ -371,7 +369,7 @@ public class CentralRepoPersonasTest extends TestCase {
// Get ALL personas for an account
- Collection personaAccounts2 = Persona.getPersonaAccountsForAccount(catdogFBAccount.getAccountId());
+ Collection personaAccounts2 = PersonaAccount.getPersonaAccountsForAccount(catdogFBAccount.getAccountId());
Assert.assertEquals(2, personaAccounts2.size());
for (PersonaAccount pa: personaAccounts2) {
@@ -525,21 +523,21 @@ public class CentralRepoPersonasTest extends TestCase {
- Collection dogAccounts = dogPersona.getAccounts();
- Assert.assertEquals(2, dogAccounts.size()); // Dog has 2 accounts.
- for (CentralRepoAccount acct : dogAccounts) {
- Assert.assertTrue(acct.getTypeSpecificId().equalsIgnoreCase(FACEBOOK_ID_CATDOG)
- || acct.getTypeSpecificId().equalsIgnoreCase(DOG_EMAIL_ID));
+ Collection dogPersonaAccounts = dogPersona.getPersonaAccounts();
+ Assert.assertEquals(2, dogPersonaAccounts.size()); // Dog has 2 accounts.
+ for (PersonaAccount pa : dogPersonaAccounts) {
+ Assert.assertTrue(pa.getAccount().getTypeSpecificId().equalsIgnoreCase(FACEBOOK_ID_CATDOG)
+ || pa.getAccount().getTypeSpecificId().equalsIgnoreCase(DOG_EMAIL_ID));
// System.out.println("Dog Account id : " + acct.getTypeSpecificId());
}
- Collection catAccounts = catPersona.getAccounts();
- Assert.assertEquals(2, catAccounts.size()); // cat has 2 accounts.
- for (CentralRepoAccount acct:catAccounts) {
+ Collection catPersonaAccounts = catPersona.getPersonaAccounts();
+ Assert.assertEquals(2, catPersonaAccounts.size()); // cat has 2 accounts.
+ for (PersonaAccount pa:catPersonaAccounts) {
//System.out.println("Cat Account id : " + acct.getTypeSpecificId());
- Assert.assertTrue(acct.getTypeSpecificId().equalsIgnoreCase(FACEBOOK_ID_CATDOG)
- || acct.getTypeSpecificId().equalsIgnoreCase(CAT_WHATSAPP_ID));
+ Assert.assertTrue(pa.getAccount().getTypeSpecificId().equalsIgnoreCase(FACEBOOK_ID_CATDOG)
+ || pa.getAccount().getTypeSpecificId().equalsIgnoreCase(CAT_WHATSAPP_ID));
}
// create account and Persona for Sherlock Holmes.
@@ -644,22 +642,22 @@ public class CentralRepoPersonasTest extends TestCase {
// Test getting peronas by data source.
// Test that getting all Personas for DS 1
- Collection ds1Persona = Persona.getPersonaForDataSource(dataSource1fromCase1);
+ Collection ds1Persona = Persona.getPersonasForDataSource(dataSource1fromCase1);
Assert.assertEquals(2, ds1Persona.size()); //
- Collection ds2Persona = Persona.getPersonaForDataSource(dataSource2fromCase1);
+ Collection ds2Persona = Persona.getPersonasForDataSource(dataSource2fromCase1);
Assert.assertEquals(2, ds2Persona.size()); //
- Collection ds3Persona = Persona.getPersonaForDataSource(dataSource1fromCase2);
+ Collection ds3Persona = Persona.getPersonasForDataSource(dataSource1fromCase2);
Assert.assertEquals(2, ds3Persona.size()); //
- Collection ds4Persona = Persona.getPersonaForDataSource(dataSource1fromCase3);
+ Collection ds4Persona = Persona.getPersonasForDataSource(dataSource1fromCase3);
Assert.assertEquals(1, ds4Persona.size()); //
- Collection ds5Persona = Persona.getPersonaForDataSource(dataSource2fromCase3);
+ Collection ds5Persona = Persona.getPersonasForDataSource(dataSource2fromCase3);
Assert.assertEquals(0, ds5Persona.size()); //
- Collection ds6Persona = Persona.getPersonaForDataSource(dataSource1fromCase4);
+ Collection ds6Persona = Persona.getPersonasForDataSource(dataSource1fromCase4);
Assert.assertEquals(0, ds6Persona.size()); //
@@ -687,7 +685,7 @@ public class CentralRepoPersonasTest extends TestCase {
Persona.PersonaStatus.ACTIVE, emailAccount1, "The person lost his name", Persona.Confidence.LOW);
// Verify Persona has a default name
- Assert.assertEquals("NoName", pa1.getPersona().getName());
+ Assert.assertEquals("Unknown", pa1.getPersona().getName());
} catch (CentralRepoException ex) {
Assert.fail("No name persona test failed. Exception: " + ex);
@@ -695,5 +693,212 @@ public class CentralRepoPersonasTest extends TestCase {
}
+ }
+
+ /**
+ * Tests searching of Persona by persona name.
+ */
+ public void testPersonaSearchByName() {
+
+ // Test1: create Personas with similar names.
+ {
+ try {
+ // Create an email account
+ CentralRepoAccount emailAccount1 = CentralRepository.getInstance()
+ .getOrCreateAccount(emailAccountType, EMAIL_ID_1);
+
+ // Create all personas with same comment.
+ final String personaComment = "Creator of Jungle Book.";
+
+ // Create a Persona with name "Rudyard Kipling"
+ Persona.createPersonaForAccount("Rudyard Kipling", personaComment,
+ Persona.PersonaStatus.ACTIVE, emailAccount1, "", Persona.Confidence.LOW);
+
+ // Create a Persona with name "Rudy"
+ Persona.createPersonaForAccount("Rudy", personaComment,
+ Persona.PersonaStatus.ACTIVE, emailAccount1, "", Persona.Confidence.LOW);
+
+
+ // Create a Persona with name "Kipling Senior"
+ Persona.createPersonaForAccount("Kipling Senior", personaComment,
+ Persona.PersonaStatus.ACTIVE, emailAccount1, "", Persona.Confidence.LOW);
+
+ // Create a Persona with name "Senor Kipling"
+ Persona.createPersonaForAccount("Senor Kipling", personaComment,
+ Persona.PersonaStatus.ACTIVE, emailAccount1, "", Persona.Confidence.LOW);
+
+
+ // Test 1 Search "kipling" - expect 3 matches
+ Collection personaSearchResult = Persona.getPersonaByName("kipling");
+ Assert.assertEquals(3, personaSearchResult.size());
+ for (Persona p: personaSearchResult) {
+ Assert.assertTrue(p.getComment().equalsIgnoreCase(personaComment));
+ }
+
+ // Search 'Rudy' - expect 2 matches
+ personaSearchResult = Persona.getPersonaByName("Rudy");
+ Assert.assertEquals(2, personaSearchResult.size());
+
+
+ // Search 'Sen' - expect 2 matches
+ personaSearchResult = Persona.getPersonaByName("Sen");
+ Assert.assertEquals(2, personaSearchResult.size());
+
+
+ // Search 'IPL' - expect 3 matches
+ personaSearchResult = Persona.getPersonaByName("IPL");
+ Assert.assertEquals(3, personaSearchResult.size());
+
+
+ // Serach "Rudyard Kipling" - expect 1 match
+ personaSearchResult = Persona.getPersonaByName("Rudyard Kipling");
+ Assert.assertEquals(1, personaSearchResult.size());
+ Assert.assertTrue(personaSearchResult.iterator().next().getName().equalsIgnoreCase("Rudyard Kipling"));
+
+ // Search '' - expect ALL (4) to match
+ personaSearchResult = Persona.getPersonaByName("");
+ Assert.assertEquals(4, personaSearchResult.size());
+
+
+ } catch (CentralRepoException ex) {
+ Assert.fail("No name persona test failed. Exception: " + ex);
+ }
+ }
+
+
+ }
+
+
+ /**
+ * Tests searching of Persona by account identifier substrings.
+ */
+ public void testPersonaSearchByAccountIdentifier() {
+
+ // Test1: create Personas with similar names.
+ {
+ try {
+ // Create an email account1
+ CentralRepoAccount emailAccount1 = CentralRepository.getInstance()
+ .getOrCreateAccount(emailAccountType, "joeexotic555@yahoo.com");
+
+ // Create all personas with same comment.
+ final String personaComment = "Comment used to create a persona";
+
+ // Create a Persona with name "Joe Exotic" associated with the email address
+ Persona.createPersonaForAccount("Joe Exotic", personaComment,
+ Persona.PersonaStatus.ACTIVE, emailAccount1, "", Persona.Confidence.LOW);
+
+ // Create a Persona with name "Tiger King" associated with the email address
+ Persona.createPersonaForAccount("Tiger King", personaComment,
+ Persona.PersonaStatus.ACTIVE, emailAccount1, "", Persona.Confidence.LOW);
+
+
+
+ // Create an phone account with number "+1 999 555 3366"
+ CentralRepoAccount phoneAccount1 = CentralRepository.getInstance()
+ .getOrCreateAccount(phoneAccountType, "+1 999 555 3366");
+
+
+ // Create a Persona with name "Carol Baskin" associated
+ Persona.createPersonaForAccount("Carol Baskin", personaComment,
+ Persona.PersonaStatus.ACTIVE, phoneAccount1, "", Persona.Confidence.LOW);
+
+ // Create a Persona with no name assoctaed with
+ Persona.createPersonaForAccount(null, personaComment,
+ Persona.PersonaStatus.ACTIVE, phoneAccount1, "", Persona.Confidence.LOW);
+
+
+
+ // Create another email account1
+ CentralRepoAccount emailAccount2 = CentralRepository.getInstance()
+ .getOrCreateAccount(emailAccountType, "jodoe@mail.com");
+
+
+
+ // Create a Persona with name "John Doe" associated with the email address
+ Persona.createPersonaForAccount("John Doe", personaComment,
+ Persona.PersonaStatus.ACTIVE, emailAccount2, "", Persona.Confidence.LOW);
+
+ Persona.createPersonaForAccount("Joanne Doe", personaComment,
+ Persona.PersonaStatus.ACTIVE, emailAccount2, "", Persona.Confidence.LOW);
+
+
+
+ // Test1 Search on 'joe' - should get 2
+ Collection personaSearchResult = PersonaAccount.getPersonaAccountsForAccountIdentifier("joe");
+ Assert.assertEquals(2, personaSearchResult.size());
+ for (PersonaAccount pa: personaSearchResult) {
+ Assert.assertTrue(pa.getAccount().getTypeSpecificId().contains("joe"));
+ }
+
+ // Search on 'exotic' - should get 2
+ personaSearchResult = PersonaAccount.getPersonaAccountsForAccountIdentifier("exotic");
+ Assert.assertEquals(2, personaSearchResult.size());
+ for (PersonaAccount pa: personaSearchResult) {
+ Assert.assertTrue(pa.getAccount().getTypeSpecificId().contains("exotic"));
+ }
+
+ // Test1 Search on '999' - should get 2
+ personaSearchResult = PersonaAccount.getPersonaAccountsForAccountIdentifier("999");
+ Assert.assertEquals(2, personaSearchResult.size());
+ for (PersonaAccount pa: personaSearchResult) {
+ Assert.assertTrue(pa.getAccount().getTypeSpecificId().contains("999"));
+ }
+
+ // Test1 Search on '555' - should get 4
+ personaSearchResult = PersonaAccount.getPersonaAccountsForAccountIdentifier("555");
+ Assert.assertEquals(4, personaSearchResult.size());
+ for (PersonaAccount pa: personaSearchResult) {
+ Assert.assertTrue(pa.getAccount().getTypeSpecificId().contains("555"));
+ }
+
+ // Test1 Search on 'doe' - should get 2
+ personaSearchResult = PersonaAccount.getPersonaAccountsForAccountIdentifier("doe");
+ Assert.assertEquals(2, personaSearchResult.size());
+ for (PersonaAccount pa: personaSearchResult) {
+ Assert.assertTrue(pa.getAccount().getTypeSpecificId().contains("doe"));
+ }
+
+ // Test1 Search on '@' - should get 4
+ personaSearchResult = PersonaAccount.getPersonaAccountsForAccountIdentifier("@");
+ Assert.assertEquals(4, personaSearchResult.size());
+ for (PersonaAccount pa: personaSearchResult) {
+ Assert.assertTrue(pa.getAccount().getTypeSpecificId().contains("@"));
+ }
+
+ // Test1 Search on '' - should get ALL (6)
+ personaSearchResult = PersonaAccount.getPersonaAccountsForAccountIdentifier("");
+ Assert.assertEquals(6, personaSearchResult.size());
+
+
+ } catch (CentralRepoException ex) {
+ Assert.fail("No name persona test failed. Exception: " + ex);
+ }
+ }
+
+ }
+
+ /**
+ * Tests the getOrInsertExaminer() api.
+ */
+ public void testExaminers() {
+
+ try {
+ String examinerName = "abcdefg";
+ CentralRepoExaminer examiner = CentralRepository.getInstance().getOrInsertExaminer(examinerName);
+ Assert.assertTrue(examiner.getLoginName().equalsIgnoreCase(examinerName));
+
+ examinerName = "";
+ examiner = CentralRepository.getInstance().getOrInsertExaminer(examinerName);
+ Assert.assertTrue(examiner.getLoginName().equalsIgnoreCase(examinerName));
+
+ examinerName = "D'Aboville";
+ examiner = CentralRepository.getInstance().getOrInsertExaminer(examinerName);
+ Assert.assertTrue(examiner.getLoginName().equalsIgnoreCase(examinerName));
+
+ } catch (CentralRepoException ex) {
+ Assert.fail("Examiner tests failed. Exception: " + ex);
+ }
+
}
}
diff --git a/RecentActivity/build.xml b/RecentActivity/build.xml
index d298fa519a..4b3d8e3347 100644
--- a/RecentActivity/build.xml
+++ b/RecentActivity/build.xml
@@ -22,6 +22,9 @@
+
+
+
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED
index 35666d6973..310602b0f9 100755
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED
@@ -55,6 +55,13 @@ ExtractSafari_Error_Getting_History=An error occurred while processing Safari hi
ExtractSafari_Error_Parsing_Bookmark=An error occured while processing Safari Bookmark files
ExtractSafari_Error_Parsing_Cookies=An error occured while processing Safari Cookies files
ExtractSafari_Module_Name=Safari
+ExtractSru_error_finding_export_srudb_program=Error finding export_srudb program
+ExtractSru_module_name=System Resource Usage Extractor
+ExtractSru_process_error_executing_export_srudb_program=Error running export_srudb program
+ExtractSru_process_errormsg_find_software_hive=Unable to find SOFTWARE HIVE file
+ExtractSru_process_errormsg_find_srudb_dat=Unable to find srudb.dat file
+ExtractSru_process_errormsg_write_software_hive=Unable to write SOFTWARE HIVE file
+ExtractSru_process_errormsg_write_srudb_dat=Unable to write srudb.dat file
ExtractZone_Internet=Internet Zone
ExtractZone_Local_Intranet=Local Intranet Zone
ExtractZone_Local_Machine=Local Machine Zone
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSru.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSru.java
new file mode 100644
index 0000000000..08782097bf
--- /dev/null
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSru.java
@@ -0,0 +1,484 @@
+ /*
+ *
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2020 Basis Technology Corp.
+ *
+ * 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.autopsy.recentactivity;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.Map;
+import org.apache.commons.io.FilenameUtils;
+import org.openide.modules.InstalledFileLocator;
+import org.openide.util.NbBundle.Messages;
+import org.sleuthkit.autopsy.casemodule.Case;
+import org.sleuthkit.autopsy.casemodule.services.FileManager;
+import org.sleuthkit.autopsy.coreutils.ExecUtil;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.coreutils.PlatformUtil;
+import org.sleuthkit.autopsy.coreutils.SQLiteDBConnect;
+import org.sleuthkit.autopsy.datamodel.ContentUtils;
+import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
+import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
+import org.sleuthkit.autopsy.ingest.IngestJobContext;
+import org.sleuthkit.datamodel.AbstractFile;
+import org.sleuthkit.datamodel.Blackboard;
+import org.sleuthkit.datamodel.BlackboardArtifact;
+import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
+import org.sleuthkit.datamodel.BlackboardAttribute;
+import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
+import org.sleuthkit.datamodel.Content;
+import org.sleuthkit.datamodel.TskCoreException;
+
+
+/**
+ * Extract the System Resource Usage database to a temp directory so it can be parsed into a SQLite db
+ * and then brought into extracted content
+ */
+final class ExtractSru extends Extract {
+
+ private static final Logger logger = Logger.getLogger(ExtractSru.class.getName());
+
+ private IngestJobContext context;
+
+ private static final String APPLICATION_USAGE_SOURCE_NAME = "System Resource Usage - Application Usage"; //NON-NLS
+ private static final String NETWORK_USAGE_SOURCE_NAME = "System Resource Usage - Network Usage";
+
+// private static final String ARTIFACT_ATTRIBUTE_NAME = "TSK_ARTIFACT_NAME"; //NON-NLS
+
+ private static final String MODULE_NAME = "extractSRU"; //NON-NLS
+
+ private static final String SRU_TOOL_FOLDER = "markmckinnon"; //NON-NLS
+ private static final String SRU_TOOL_NAME_WINDOWS_32 = "Export_Srudb_32.exe"; //NON-NLS
+ private static final String SRU_TOOL_NAME_WINDOWS_64 = "Export_Srudb_64.exe"; //NON-NLS
+ private static final String SRU_TOOL_NAME_LINUX = "Export_Srudb_Linux.exe"; //NON-NLS
+ private static final String SRU_TOOL_NAME_MAC = "Export_srudb_macos"; //NON-NLS
+ private static final String SRU_OUTPUT_FILE_NAME = "Output.txt"; //NON-NLS
+ private static final String SRU_ERROR_FILE_NAME = "Error.txt"; //NON-NLS
+
+ private static final Map applicationFilesFound = new HashMap<>();
+
+ @Messages({
+ "ExtractSru_module_name=System Resource Usage Extractor"
+ })
+ ExtractSru() {
+ this.moduleName = Bundle.ExtractSru_module_name();
+ }
+
+ @Messages({
+ "ExtractSru_error_finding_export_srudb_program=Error finding export_srudb program",
+ "ExtractSru_process_error_executing_export_srudb_program=Error running export_srudb program"
+ })
+
+ @Override
+ void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
+
+ this.context = context;
+
+ String modOutPath = Case.getCurrentCase().getModuleDirectory() + File.separator + "sru";
+ File dir = new File(modOutPath);
+ if (dir.exists() == false) {
+ dir.mkdirs();
+ }
+
+ String tempDirPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), "sru"); //NON-NLS
+ String softwareHiveFileName = getSoftwareHiveFile(dataSource, tempDirPath);
+
+ if (softwareHiveFileName == null) {
+ return;
+ }
+
+ AbstractFile sruAbstractFile = getSruFile(dataSource, tempDirPath);
+
+ String sruFileName = tempDirPath + File.separator + sruAbstractFile.getId() + "_" + sruAbstractFile.getName();
+
+ if (sruFileName == null) {
+ this.addErrorMessage(Bundle.ExtractSru_process_errormsg_find_srudb_dat());
+ logger.log(Level.SEVERE, "SRUDB.dat file not found"); //NON-NLS
+ return; //If we cannot find the srudb.dat file we cannot proceed
+ }
+
+ final String sruDumper = getPathForSruDumper();
+ if (sruDumper == null) {
+ this.addErrorMessage(Bundle.ExtractSru_error_finding_export_srudb_program());
+ logger.log(Level.SEVERE, "Error finding export_srudb program"); //NON-NLS
+ return; //If we cannot find the export_srudb program we cannot proceed
+ }
+
+ if (context.dataSourceIngestIsCancelled()) {
+ return;
+ }
+
+ try {
+ String modOutFile = modOutPath + File.separator + sruAbstractFile.getId() + "_srudb.db3";
+
+ extractSruFiles(sruDumper, sruFileName, modOutFile, tempDirPath, softwareHiveFileName);
+
+ findSruExecutedFiles(modOutFile, dataSource);
+
+ createNetUsageArtifacts(modOutFile, sruAbstractFile);
+ createAppUsageArtifacts(modOutFile, sruAbstractFile);
+ } catch (IOException ex) {
+ this.addErrorMessage(Bundle.ExtractSru_process_error_executing_export_srudb_program());
+ logger.log(Level.SEVERE, "SRUDB.dat file not found"); //NON-NLS
+ }
+ }
+
+ @Messages({
+ "ExtractSru_process_errormsg_find_software_hive=Unable to find SOFTWARE HIVE file",
+ "ExtractSru_process_errormsg_write_software_hive=Unable to write SOFTWARE HIVE file"
+ })
+
+ /**
+ * Extract the SOFTWARE hive file to the temp directory
+ *
+ * @param dataSource datasource where software hiive is
+ * @param tempDirPath temp directory to write file to
+ *
+ * @return Software hive file location
+ */
+ String getSoftwareHiveFile(Content dataSource, String tempDirPath) {
+ FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
+
+ List softwareHiveFiles;
+
+ try {
+ softwareHiveFiles = fileManager.findFiles(dataSource, "SOFTWARE"); //NON-NLS
+ } catch (TskCoreException ex) {
+ this.addErrorMessage(Bundle.ExtractSru_process_errormsg_find_software_hive());
+ logger.log(Level.WARNING, "Unable to find SOFTWARE HIVE file.", ex); //NON-NLS
+ return null; // No need to continue
+ }
+
+ String softwareHiveFileName = null;
+
+ for (AbstractFile softwareFile : softwareHiveFiles) {
+
+ if (softwareFile.getParentPath().endsWith("/config/")) {
+ softwareHiveFileName = tempDirPath + File.separator + softwareFile.getId() + "_" + softwareFile.getName();
+
+ try {
+ ContentUtils.writeToFile(softwareFile, new File(softwareHiveFileName));
+ } catch (IOException ex) {
+ this.addErrorMessage(Bundle.ExtractSru_process_errormsg_find_software_hive());
+ logger.log(Level.WARNING, String.format("Unable to write %s to temp directory. File name: %s", softwareFile.getName(), softwareFile), ex); //NON-NLS
+ return null;
+ }
+ }
+ }
+ return softwareHiveFileName;
+ }
+
+ @Messages({
+ "ExtractSru_process_errormsg_find_srudb_dat=Unable to find srudb.dat file",
+ "ExtractSru_process_errormsg_write_srudb_dat=Unable to write srudb.dat file"
+ })
+ /**
+ * Extract the SOFTWARE hive file to the temp directory
+ *
+ * @param dataSource datasource where software hiive is
+ * @param tempDirPath temp directory to write file to
+ *
+ * @return Software hive file location
+ */
+ AbstractFile getSruFile(Content dataSource, String tempDirPath) {
+ FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
+
+ List sruFiles;
+
+ try {
+ sruFiles = fileManager.findFiles(dataSource, "SRUDB.DAT"); //NON-NLS
+ } catch (TskCoreException ex) {
+ this.addErrorMessage(Bundle.ExtractSru_process_errormsg_find_srudb_dat());
+ logger.log(Level.WARNING, "Unable to find SRUDB.DAT file.", ex); //NON-NLS
+ return null; // No need to continue
+ }
+
+ AbstractFile sruAbstractFile = null;
+
+ for (AbstractFile sruFile : sruFiles) {
+
+ String sruFileName = tempDirPath + File.separator + sruFile.getId() + "_" + sruFile.getName();
+ sruAbstractFile = sruFile;
+
+ try {
+ ContentUtils.writeToFile(sruFile, new File(sruFileName));
+ } catch (IOException ex) {
+ this.addErrorMessage(Bundle.ExtractSru_process_errormsg_write_srudb_dat());
+ logger.log(Level.WARNING, String.format("Unable to write %s to temp directory. File name: %s", sruFile.getName(), sruFile), ex); //NON-NLS
+ return null;
+ }
+
+ }
+ return sruAbstractFile;
+ }
+
+ /**
+ * Run the export srudb program against the srudb.dat file
+ *
+ * @param sruExePath
+ * @param tempDirPath
+ * @param tempOutPath
+ *
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ void extractSruFiles(String sruExePath, String sruFile, String tempOutFile, String tempOutPath, String softwareHiveFile) throws IOException {
+ final Path outputFilePath = Paths.get(tempOutPath, SRU_OUTPUT_FILE_NAME);
+ final Path errFilePath = Paths.get(tempOutPath, SRU_ERROR_FILE_NAME);
+
+ List commandLine = new ArrayList<>();
+ commandLine.add(sruExePath);
+ commandLine.add(sruFile); //NON-NLS
+ commandLine.add(softwareHiveFile);
+ commandLine.add(tempOutFile);
+
+ ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
+ processBuilder.redirectOutput(outputFilePath.toFile());
+ processBuilder.redirectError(errFilePath.toFile());
+
+ ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context));
+ }
+
+ private String getPathForSruDumper() {
+ Path path = null;
+ if (PlatformUtil.isWindowsOS()) {
+ if (PlatformUtil.is64BitOS()) {
+ path = Paths.get(SRU_TOOL_FOLDER, SRU_TOOL_NAME_WINDOWS_64);
+ } else {
+ path = Paths.get(SRU_TOOL_FOLDER, SRU_TOOL_NAME_WINDOWS_32);
+ }
+ } else {
+ if ("Linux".equals(PlatformUtil.getOSName())) {
+ path = Paths.get(SRU_TOOL_FOLDER, SRU_TOOL_NAME_LINUX);
+ } else {
+ path = Paths.get(SRU_TOOL_FOLDER, SRU_TOOL_NAME_MAC);
+ }
+ }
+ File sruToolFile = InstalledFileLocator.getDefault().locate(path.toString(),
+ ExtractSru.class.getPackage().getName(), false);
+ if (sruToolFile != null) {
+ return sruToolFile.getAbsolutePath();
+ }
+
+ return null;
+ }
+
+ private void findSruExecutedFiles(String sruDb, Content dataSource) {
+
+ org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager();
+
+ String sqlStatement = "SELECT DISTINCT SUBSTR(LTRIM(IdBlob, '\\Device\\HarddiskVolume'), INSTR(LTRIM(IdBlob, '\\Device\\HarddiskVolume'), '\\')) "
+ + " application_name, idBlob source_name FROM SruDbIdMapTable WHERE idType = 0 AND idBlob NOT LIKE '!!%'"; //NON-NLS
+
+ try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + sruDb); //NON-NLS
+ ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
+
+ while (resultSet.next()) {
+
+ if (context.dataSourceIngestIsCancelled()) {
+ logger.log(Level.INFO, "Cancelled SRU Artifact Creation."); //NON-NLS
+ return;
+ }
+
+ String applicationName = resultSet.getString("application_name"); //NON-NLS
+ String sourceName = resultSet.getString("source_name"); //NON-NLS
+
+ String normalizePathName = FilenameUtils.normalize(applicationName, true);
+ String fileName = FilenameUtils.getName(normalizePathName);
+ String filePath = FilenameUtils.getPath(normalizePathName);
+ if (fileName.contains(" [")) {
+ fileName = fileName.substring(0, fileName.indexOf(" ["));
+ }
+ List sourceFiles;
+ try {
+ sourceFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS
+ for (AbstractFile sourceFile : sourceFiles) {
+ if (sourceFile.getParentPath().endsWith(filePath)) {
+ applicationFilesFound.put(sourceName.toLowerCase(), sourceFile);
+ }
+ }
+
+ } catch (TskCoreException ex) {
+ logger.log(Level.WARNING, String.format("Error finding actual file %s. file may not exist", normalizePathName)); //NON-NLS
+ }
+ }
+ } catch (SQLException ex) {
+ logger.log(Level.WARNING, "Error while trying to read into a sqlite db.", ex);//NON-NLS
+ }
+
+ }
+
+ private void createNetUsageArtifacts(String sruDb, AbstractFile sruAbstractFile) {
+ List bba = new ArrayList<>();
+
+ String sqlStatement = "SELECT STRFTIME('%s', timestamp) ExecutionTime, Application_Name, User_Name, "
+ + " bytesSent, BytesRecvd FROM network_Usage , SruDbIdMapTable "
+ + " where appId = IdIndex and IdType = 0 order by ExecutionTime;"; //NON-NLS
+
+ try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + sruDb); //NON-NLS
+ ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
+
+ while (resultSet.next()) {
+
+ if (context.dataSourceIngestIsCancelled()) {
+ logger.log(Level.INFO, "Cancelled SRU Net Usage Artifact Creation."); //NON-NLS
+ return;
+ }
+
+ String applicationName = resultSet.getString("Application_Name"); //NON-NLS
+ Long executionTime = Long.valueOf(resultSet.getInt("ExecutionTime")); //NON-NLS
+ Long bytesSent = Long.valueOf(resultSet.getInt("bytesSent")); //NON-NLS
+ Long bytesRecvd = Long.valueOf(resultSet.getInt("BytesRecvd")); //NON-NLS
+ String userName = resultSet.getString("User_Name"); //NON-NLS
+
+ Collection bbattributes = Arrays.asList(
+ new BlackboardAttribute(
+ BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(),
+ applicationName),//NON-NLS
+ new BlackboardAttribute(
+ BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME, getName(),
+ userName),
+ new BlackboardAttribute(
+ BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, getName(),
+ executionTime),
+ new BlackboardAttribute(
+ BlackboardAttribute.ATTRIBUTE_TYPE.TSK_BYTES_SENT, getName(), bytesSent),
+ new BlackboardAttribute(
+ BlackboardAttribute.ATTRIBUTE_TYPE.TSK_BYTES_RECEIVED, getName(), bytesRecvd),
+ new BlackboardAttribute(
+ BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, getName(), NETWORK_USAGE_SOURCE_NAME));
+
+ try {
+ BlackboardArtifact bbart = sruAbstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN);
+ bbart.addAttributes(bbattributes);
+ bba.add(bbart);
+ BlackboardArtifact associateBbArtifact = createAssociatedArtifact(applicationName.toLowerCase(), bbart);
+ if (associateBbArtifact != null) {
+ bba.add(associateBbArtifact);
+ }
+ } catch (TskCoreException ex) {
+ logger.log(Level.SEVERE, "Exception Adding Artifact.", ex);//NON-NLS
+ }
+ }
+
+ } catch (SQLException ex) {
+ logger.log(Level.SEVERE, "Error while trying to read into a sqlite db.", ex);//NON-NLS
+ }
+
+ try {
+ blackboard.postArtifacts(bba, MODULE_NAME);
+ } catch (Blackboard.BlackboardException ex) {
+ logger.log(Level.SEVERE, "Error Posting Artifact.", ex);//NON-NLS
+ }
+ }
+
+ private void createAppUsageArtifacts(String sruDb, AbstractFile sruAbstractFile) {
+ List bba = new ArrayList<>();
+
+ String sqlStatement = "SELECT STRFTIME('%s', timestamp) ExecutionTime, Application_Name, User_Name "
+ + " FROM Application_Resource_Usage, SruDbIdMapTable WHERE "
+ + " idType = 0 and idIndex = appId order by ExecutionTime;"; //NON-NLS
+
+ try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + sruDb); //NON-NLS
+ ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
+
+ while (resultSet.next()) {
+
+ if (context.dataSourceIngestIsCancelled()) {
+ logger.log(Level.INFO, "Cancelled SRU Net Usage Artifact Creation."); //NON-NLS
+ return;
+ }
+
+ String applicationName = resultSet.getString("Application_Name"); //NON-NLS
+ Long executionTime = Long.valueOf(resultSet.getInt("ExecutionTime")); //NON-NLS
+ String userName = resultSet.getString("User_Name");
+
+ Collection bbattributes = Arrays.asList(
+ new BlackboardAttribute(
+ BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(),
+ applicationName),//NON-NLS
+ new BlackboardAttribute(
+ BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME, getName(),
+ userName),
+ new BlackboardAttribute(
+ BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, getName(),
+ executionTime),
+ new BlackboardAttribute(
+ BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, getName(), APPLICATION_USAGE_SOURCE_NAME));
+
+ try {
+ BlackboardArtifact bbart = sruAbstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN);
+ bbart.addAttributes(bbattributes);
+ bba.add(bbart);
+ BlackboardArtifact associateBbArtifact = createAssociatedArtifact(applicationName.toLowerCase(), bbart);
+ if (associateBbArtifact != null) {
+ bba.add(associateBbArtifact);
+ }
+ } catch (TskCoreException ex) {
+ logger.log(Level.SEVERE, "Exception Adding Artifact.", ex);//NON-NLS
+ }
+ }
+
+ } catch (SQLException ex) {
+ logger.log(Level.SEVERE, "Error while trying to read into a sqlite db.", ex);//NON-NLS
+ }
+
+ try {
+ blackboard.postArtifacts(bba, MODULE_NAME);
+ } catch (Blackboard.BlackboardException ex) {
+ logger.log(Level.SEVERE, "Error Posting Artifact.", ex);//NON-NLS
+ }
+ }
+
+ /**
+ * Create associated artifacts using file path name and the artifact it associates with
+ *
+ * @param filePathName file and path of object being associated with
+ *
+ * @param bba blackboard artifact to associate with
+ *
+ * @returnv BlackboardArtifact or a null value
+ */
+ private BlackboardArtifact createAssociatedArtifact(String filePathName, BlackboardArtifact bba) {
+ if (applicationFilesFound.containsKey(filePathName)) {
+ AbstractFile sourceFile = applicationFilesFound.get(filePathName);
+ Collection bbattributes2 = new ArrayList<>();
+ bbattributes2.addAll(Arrays.asList(
+ new BlackboardAttribute(TSK_ASSOCIATED_ARTIFACT, this.getName(),
+ bba.getArtifactID())));
+
+ BlackboardArtifact associatedObjectBba = createArtifactWithAttributes(TSK_ASSOCIATED_OBJECT, sourceFile, bbattributes2);
+ if (associatedObjectBba != null) {
+ return associatedObjectBba;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java
index 444a6d638d..c5cb62a779 100644
--- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java
+++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java
@@ -78,6 +78,7 @@ public final class RAImageIngestModule implements DataSourceIngestModule {
Extract safari = new ExtractSafari();
Extract zoneInfo = new ExtractZoneIdentifier();
Extract recycleBin = new ExtractRecycleBin();
+ Extract sru = new ExtractSru();
extractors.add(chrome);
extractors.add(firefox);
@@ -91,7 +92,8 @@ public final class RAImageIngestModule implements DataSourceIngestModule {
extractors.add(dataSourceAnalyzer); //this needs to run after ExtractRegistry and ExtractOs
extractors.add(zoneInfo); // this needs to run after the web browser modules
extractors.add(recycleBin); // this needs to run after ExtractRegistry and ExtractOS
-
+ extractors.add(sru);
+
browserExtractors.add(chrome);
browserExtractors.add(firefox);
browserExtractors.add(iexplore);
diff --git a/appveyor.yml b/appveyor.yml
index d7cba33584..b3cbb4032c 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -19,7 +19,6 @@ environment:
LIBVHDI_HOME: "C:\\libvhdi_64bit"
LIBVMDK_HOME: "C:\\libvmdk_64bit\\libvmdk"
LIBEWF_HOME: "C:\\libewf_64bit"
- POSTGRESQL_HOME_64: "C:\\Program Files\\PostgreSQL\\9.5"
JDK_HOME: C:\Program Files\Java\jdk1.8.0
PYTHON: "C:\\Python36-x64"
@@ -41,7 +40,7 @@ build_script:
- python win32\updateAndBuildAll.py -m
- ps: pushd bindings/java
- ps: ant -version
- - cmd: ant dist-PostgreSQL
+ - cmd: ant dist
- ps: popd
- cd %APPVEYOR_BUILD_FOLDER%
- cmd: ant -q build
diff --git a/thirdparty/markmckinnon/Export_Srudb_Linux b/thirdparty/markmckinnon/Export_Srudb_Linux
new file mode 100644
index 0000000000..0af32da85b
Binary files /dev/null and b/thirdparty/markmckinnon/Export_Srudb_Linux differ
diff --git a/thirdparty/markmckinnon/Export_srudb_macos b/thirdparty/markmckinnon/Export_srudb_macos
new file mode 100644
index 0000000000..eeff65b668
Binary files /dev/null and b/thirdparty/markmckinnon/Export_srudb_macos differ
diff --git a/thirdparty/markmckinnon/LICENSE-2.0.txt b/thirdparty/markmckinnon/LICENSE-2.0.txt
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/thirdparty/markmckinnon/LICENSE-2.0.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/thirdparty/markmckinnon/export_srudb_32.exe b/thirdparty/markmckinnon/export_srudb_32.exe
new file mode 100644
index 0000000000..e642c1d8df
Binary files /dev/null and b/thirdparty/markmckinnon/export_srudb_32.exe differ
diff --git a/thirdparty/markmckinnon/export_srudb_64.exe b/thirdparty/markmckinnon/export_srudb_64.exe
new file mode 100644
index 0000000000..7b07367739
Binary files /dev/null and b/thirdparty/markmckinnon/export_srudb_64.exe differ
diff --git a/travis_build.sh b/travis_build.sh
index f7b7cfb72d..5f574d5726 100755
--- a/travis_build.sh
+++ b/travis_build.sh
@@ -4,7 +4,7 @@ set -e
echo "Building TSK..."
cd sleuthkit/sleuthkit
./bootstrap && ./configure --prefix=/usr && make
-pushd bindings/java && ant -q dist-PostgreSQL && popd
+pushd bindings/java && ant -q dist && popd
echo "Building Autopsy..." && echo -en 'travis_fold:start:script.build\\r'
cd $TRAVIS_BUILD_DIR/
diff --git a/unix_setup.sh b/unix_setup.sh
index 2a75ab72c7..edd6c36632 100644
--- a/unix_setup.sh
+++ b/unix_setup.sh
@@ -57,13 +57,13 @@ fi
ext_jar_filepath=$PWD/autopsy/modules/ext/sleuthkit-$TSK_VERSION.jar;
echo -n "Copying sleuthkit-$TSK_VERSION.jar into the Autopsy directory..."
-rm -f $ext_jar_filepath;
+rm -f "$ext_jar_filepath";
if [ "$?" -gt 0 ]; then #checking if remove operation failed
echo "ERROR: Deleting $ext_jar_filepath failed."
echo "Please check your permissions."
exit 1
else
- cp $sleuthkit_jar_filepath $ext_jar_filepath
+ cp $sleuthkit_jar_filepath "$ext_jar_filepath"
if [ "$?" -ne 0 ]; then # checking copy operation was successful
echo "ERROR: Copying $sleuthkit_jar_filepath to $ext_jar_filepath failed."
echo "Please check your permissions."