19 package org.sleuthkit.datamodel;
21 import java.sql.ResultSet;
22 import java.sql.SQLException;
23 import java.sql.Statement;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.List;
34 import java.util.concurrent.ConcurrentHashMap;
35 import java.util.logging.Level;
36 import java.util.logging.Logger;
51 private final Map<
Account.
Type, Integer> accountTypeToTypeIdMap
53 private final Map<String,
Account.
Type> typeNameToAccountTypeMap
57 private final static Set<Integer> RELATIONSHIP_ARTIFACT_TYPE_IDS
58 =
new HashSet<Integer>(Arrays.asList(
64 private static final String RELATIONSHIP_ARTIFACT_TYPE_IDS_CSV_STR
65 = StringUtils.buildCSVString(RELATIONSHIP_ARTIFACT_TYPE_IDS);
79 CaseDbConnection connection = db.getConnection();
81 Statement statement = null;
82 ResultSet resultSet = null;
85 statement = connection.createStatement();
87 int count = readAccountTypes();
92 statement.execute(
"INSERT INTO account_types (type_name, display_name) VALUES ( '" + type.getTypeName() +
"', '" + type.getDisplayName() +
"')");
93 }
catch (SQLException ex) {
94 resultSet = connection.executeQuery(statement,
"SELECT COUNT(*) AS count FROM account_types WHERE type_name = '" + type.getTypeName() +
"'");
96 if (resultSet.getLong(
"count") == 0) {
103 ResultSet rs2 = connection.executeQuery(statement,
"SELECT account_type_id FROM account_types WHERE type_name = '" + type.getTypeName() +
"'");
105 int typeID = rs2.getInt(
"account_type_id");
109 this.accountTypeToTypeIdMap.put(accountType, typeID);
110 this.typeNameToAccountTypeMap.put(type.getTypeName(), accountType);
113 }
catch (SQLException ex) {
114 LOGGER.log(Level.SEVERE,
"Failed to add row to account_types", ex);
116 closeResultSet(resultSet);
117 closeStatement(statement);
134 CaseDbConnection connection = db.getConnection();
136 Statement statement = null;
137 ResultSet resultSet = null;
141 statement = connection.createStatement();
144 resultSet = connection.executeQuery(statement,
"SELECT COUNT(*) AS count FROM account_types");
146 if (resultSet.getLong(
"count") > 0) {
149 resultSet = connection.executeQuery(statement,
"SELECT * FROM account_types");
150 while (resultSet.next()) {
151 Account.
Type accountType =
new Account.
Type(resultSet.getString(
"type_name"), resultSet.getString(
"display_name"));
152 this.accountTypeToTypeIdMap.put(accountType, resultSet.getInt(
"account_type_id"));
153 this.typeNameToAccountTypeMap.put(accountType.getTypeName(), accountType);
155 count = this.typeNameToAccountTypeMap.size();
158 }
catch (SQLException ex) {
159 LOGGER.log(Level.SEVERE,
"Failed to read account_types", ex);
161 closeResultSet(resultSet);
162 closeStatement(statement);
197 if (this.accountTypeToTypeIdMap.containsKey(accountType)) {
201 CaseDbConnection connection = db.getConnection();
206 connection.beginTransaction();
207 s = connection.createStatement();
208 rs = connection.executeQuery(s,
"SELECT * FROM account_types WHERE type_name = '" + accountTypeName +
"'");
212 s.execute(
"INSERT INTO account_types (type_name, display_name) VALUES ( '" + accountTypeName +
"', '" + displayName +
"')");
215 rs = connection.executeQuery(s,
"SELECT * FROM account_types WHERE type_name = '" + accountTypeName +
"'");
218 int typeID = rs.getInt(
"account_type_id");
219 accountType =
new Account.
Type(rs.getString(
"type_name"), rs.getString(
"display_name"));
221 this.accountTypeToTypeIdMap.put(accountType, typeID);
222 this.typeNameToAccountTypeMap.put(accountTypeName, accountType);
224 connection.commitTransaction();
228 int typeID = rs.getInt(
"account_type_id");
230 accountType =
new Account.
Type(rs.getString(
"type_name"), rs.getString(
"display_name"));
231 this.accountTypeToTypeIdMap.put(accountType, typeID);
235 }
catch (SQLException ex) {
236 connection.rollbackTransaction();
267 Account account = getOrCreateAccount(accountType, normalizeAccountID(accountType, accountUniqueID));
275 BlackboardArtifact accountArtifact = getOrCreateAccountFileInstanceArtifact(accountType, normalizeAccountID(accountType, accountUniqueID), moduleName, sourceFile);
301 CaseDbConnection connection = db.getConnection();
306 s = connection.createStatement();
307 rs = connection.executeQuery(s,
"SELECT * FROM accounts WHERE account_type_id = " + getAccountTypeId(accountType)
308 +
" AND account_unique_identifier = '" + normalizeAccountID(accountType, accountUniqueID) +
"'");
311 account =
new Account(rs.getInt(
"account_id"), accountType,
312 rs.getString(
"account_unique_identifier"));
314 }
catch (SQLException ex) {
367 if (relationshipType.isCreatableFrom(sourceArtifact) ==
false) {
368 throw new TskDataException(
"Can not make a " + relationshipType.getDisplayName()
369 +
" relationship from a" + sourceArtifact.getDisplayName());
378 List<Long> accountIDs =
new ArrayList<Long>();
380 if (null != sender) {
381 accountIDs.add(sender.getAccount().getAccountID());
382 if (sender.getDataSourceObjectID() != sourceArtifact.getDataSourceObjectID()) {
383 throw new TskDataException(
"Sender and relationship are from different data sources :"
384 +
"Sender source ID" + sender.getDataSourceObjectID() +
" != relationship source ID" + sourceArtifact.getDataSourceObjectID());
389 accountIDs.add(recipient.getAccount().getAccountID());
390 if (recipient.getDataSourceObjectID() != sourceArtifact.getDataSourceObjectID()) {
391 throw new TskDataException(
"Recipient and relationship are from different data sources :"
392 +
"Recipient source ID" + recipient.getDataSourceObjectID() +
" != relationship source ID" + sourceArtifact.getDataSourceObjectID());
396 Set<UnorderedAccountPair> relationships = listToUnorderedPairs(accountIDs);
397 Iterator<UnorderedAccountPair> iter = relationships.iterator();
399 while (iter.hasNext()) {
401 UnorderedAccountPair accountPair = iter.next();
402 addAccountsRelationship(accountPair.getFirst(), accountPair.getSecond(),
403 sourceArtifact, relationshipType, dateTime);
404 }
catch (TskCoreException ex) {
406 LOGGER.log(Level.WARNING,
"Error adding relationship", ex);
423 private Account getOrCreateAccount(
Account.
Type accountType, String accountUniqueID)
throws TskCoreException {
425 if (null == account) {
426 String query =
" INTO accounts (account_type_id, account_unique_identifier) "
427 +
"VALUES ( " + getAccountTypeId(accountType) +
", '"
428 + normalizeAccountID(accountType, accountUniqueID) +
"'" +
")";
431 query =
"INSERT " + query +
" ON CONFLICT DO NOTHING";
434 query =
"INSERT OR IGNORE " + query;
437 throw new TskCoreException(
"Unknown DB Type: " + db.
getDatabaseType().name());
440 CaseDbConnection connection = db.getConnection();
445 connection.beginTransaction();
446 s = connection.createStatement();
450 connection.commitTransaction();
451 account =
getAccount(accountType, accountUniqueID);
452 }
catch (SQLException ex) {
453 connection.rollbackTransaction();
454 throw new TskCoreException(
"Error adding an account", ex);
480 BlackboardArtifact getOrCreateAccountFileInstanceArtifact(Account.Type accountType, String accountUniqueID, String moduleName, Content sourceFile)
throws TskCoreException {
483 BlackboardArtifact accountArtifact = getAccountFileInstanceArtifact(accountType, accountUniqueID, sourceFile);
484 if (null != accountArtifact) {
485 return accountArtifact;
489 accountArtifact = db.
newBlackboardArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT, sourceFile.getId());
491 Collection<BlackboardAttribute> attributes =
new ArrayList<BlackboardAttribute>();
492 attributes.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, moduleName, accountType.getTypeName()));
493 attributes.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID, moduleName, accountUniqueID));
496 return accountArtifact;
512 private BlackboardArtifact getAccountFileInstanceArtifact(Account.Type accountType, String accountUniqueID, Content sourceFile)
throws TskCoreException {
513 BlackboardArtifact accountArtifact = null;
514 CaseDbConnection connection = db.getConnection();
520 s = connection.createStatement();
521 String queryStr =
"SELECT artifacts.artifact_id AS artifact_id,"
522 +
" artifacts.obj_id AS obj_id,"
523 +
" artifacts.artifact_obj_id AS artifact_obj_id,"
524 +
" artifacts.data_source_obj_id AS data_source_obj_id,"
525 +
" artifacts.artifact_type_id AS artifact_type_id,"
526 +
" artifacts.review_status_id AS review_status_id"
527 +
" FROM blackboard_artifacts AS artifacts"
528 +
" JOIN blackboard_attributes AS attr_account_type"
529 +
" ON artifacts.artifact_id = attr_account_type.artifact_id"
530 +
" JOIN blackboard_attributes AS attr_account_id"
531 +
" ON artifacts.artifact_id = attr_account_id.artifact_id"
532 +
" AND attr_account_id.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID.getTypeID()
533 +
" AND attr_account_id.value_text = '" + accountUniqueID +
"'"
534 +
" WHERE artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()
535 +
" AND attr_account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()
536 +
" AND attr_account_type.value_text = '" + accountType.getTypeName() +
"'"
537 +
" AND artifacts.obj_id = " + sourceFile.getId();
539 rs = connection.executeQuery(s, queryStr);
541 BlackboardArtifact.Type bbartType = db.
getArtifactType(rs.getInt(
"artifact_type_id"));
543 accountArtifact =
new BlackboardArtifact(db, rs.getLong(
"artifact_id"), rs.getLong(
"obj_id"), rs.getLong(
"artifact_obj_id"), rs.getLong(
"data_source_obj_id"),
544 bbartType.getTypeID(), bbartType.getTypeName(), bbartType.getDisplayName(),
545 BlackboardArtifact.ReviewStatus.withID(rs.getInt(
"review_status_id")));
547 }
catch (SQLException ex) {
548 throw new TskCoreException(
"Error getting account", ex);
556 return accountArtifact;
571 if (this.typeNameToAccountTypeMap.containsKey(accountTypeName)) {
572 return this.typeNameToAccountTypeMap.get(accountTypeName);
575 CaseDbConnection connection = db.getConnection();
581 s = connection.createStatement();
582 rs = connection.executeQuery(s,
"SELECT account_type_id, type_name, display_name, value_type FROM account_types WHERE type_name = '" + accountTypeName +
"'");
585 accountType =
new Account.
Type(accountTypeName, rs.getString(
"display_name"));
586 this.accountTypeToTypeIdMap.put(accountType, rs.getInt(
"account_type_id"));
587 this.typeNameToAccountTypeMap.put(accountTypeName, accountType);
590 }
catch (SQLException ex) {
591 throw new TskCoreException(
"Error getting account type id", ex);
613 CaseDbConnection connection = db.getConnection();
619 s = connection.createStatement();
620 rs = connection.executeQuery(s,
"SELECT account_types.type_name as type_name,"
621 +
" account_types.display_name as display_name,"
622 +
" accounts.account_id as account_id,"
623 +
" accounts.account_unique_identifier as account_unique_identifier"
624 +
" FROM accounts as accounts"
625 +
" JOIN account_types as account_types"
626 +
" ON accounts.account_type_id = account_types.account_type_id"
627 +
" WHERE accounts.account_id = " + account_id);
631 account =
new Account(rs.getInt(
"account_id"), accountType, rs.getString(
"account_unique_identifier"));
633 }
catch (SQLException ex) {
634 throw new TskCoreException(
"Error getting account from account_id", ex);
658 private void addAccountsRelationship(
long account1_id,
long account2_id, BlackboardArtifact relationshipaArtifact, Relationship.Type relationshipType,
long dateTime)
throws TskCoreException {
659 CaseDbConnection connection = db.getConnection();
665 String dateTimeValStr = (dateTime > 0) ? Long.toString(dateTime) :
"NULL";
667 connection.beginTransaction();
668 s = connection.createStatement();
669 String query =
"INTO account_relationships (account1_id, account2_id, relationship_source_obj_id, date_time, relationship_type, data_source_obj_id ) "
670 +
"VALUES ( " + account1_id +
", " + account2_id +
", " + relationshipaArtifact.getId() +
", " + dateTimeValStr +
", " + relationshipType.getTypeID() +
", " + relationshipaArtifact.getDataSourceObjectID() +
")";
673 query =
"INSERT " + query +
" ON CONFLICT DO NOTHING";
676 query =
"INSERT OR IGNORE " + query;
679 throw new TskCoreException(
"Unknown DB Type: " + db.
getDatabaseType().name());
682 connection.commitTransaction();
683 }
catch (SQLException ex) {
684 connection.rollbackTransaction();
685 throw new TskCoreException(
"Error adding accounts relationship", ex);
709 CaseDbConnection connection = db.getConnection();
715 s = connection.createStatement();
718 Set<String> applicableInnerQueryFilters =
new HashSet<String>(Arrays.asList(
723 String innerQueryfilterSQL = getCommunicationsFilterSQL(filter, applicableInnerQueryFilters);
725 String innerQueryTemplate
726 =
" SELECT %1$1s as account_id,"
727 +
" data_source_obj_id"
728 +
" FROM account_relationships as relationships"
729 + (innerQueryfilterSQL.isEmpty() ?
"" :
" WHERE " + innerQueryfilterSQL);
731 String innerQuery1 = String.format(innerQueryTemplate,
"account1_id");
732 String innerQuery2 = String.format(innerQueryTemplate,
"account2_id");
735 String combinedInnerQuery
736 =
"SELECT count(*) as relationship_count, account_id, data_source_obj_id "
737 +
" FROM ( " + innerQuery1 +
" UNION " + innerQuery2 +
" ) AS inner_union"
738 +
" GROUP BY account_id, data_source_obj_id";
741 Set<String> applicableFilters =
new HashSet<String>(Arrays.asList(
745 String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
749 " accounts.account_id AS account_id,"
750 +
" accounts.account_unique_identifier AS account_unique_identifier,"
752 +
" account_types.type_name AS type_name,"
754 +
" relationship_count,"
755 +
" data_source_info.device_id AS device_id"
756 +
" FROM ( " + combinedInnerQuery +
" ) AS account_device_instances"
757 +
" JOIN accounts AS accounts"
758 +
" ON accounts.account_id = account_device_instances.account_id"
759 +
" JOIN account_types AS account_types"
760 +
" ON accounts.account_type_id = account_types.account_type_id"
761 +
" JOIN data_source_info AS data_source_info"
762 +
" ON account_device_instances.data_source_obj_id = data_source_info.obj_id"
763 + (filterSQL.isEmpty() ?
"" :
" WHERE " + filterSQL);
767 queryStr =
"SELECT DISTINCT ON ( accounts.account_id, data_source_info.device_id) " + queryStr;
770 queryStr =
"SELECT " + queryStr +
" GROUP BY accounts.account_id, data_source_info.device_id";
773 throw new TskCoreException(
"Unknown DB Type: " + db.
getDatabaseType().name());
776 rs = connection.executeQuery(s, queryStr);
777 ArrayList<AccountDeviceInstance> accountDeviceInstances =
new ArrayList<AccountDeviceInstance>();
779 long account_id = rs.getLong(
"account_id");
780 String deviceID = rs.getString(
"device_id");
781 final String type_name = rs.getString(
"type_name");
782 final String account_unique_identifier = rs.getString(
"account_unique_identifier");
784 Account.
Type accountType = typeNameToAccountTypeMap.get(type_name);
785 Account account =
new Account(account_id, accountType, account_unique_identifier);
789 return accountDeviceInstances;
790 }
catch (SQLException ex) {
791 throw new TskCoreException(
"Error getting account device instances. " + ex.getMessage(), ex);
816 long account_id = accountDeviceInstance.getAccount().getAccountID();
819 String datasourceObjIdsCSV = StringUtils.buildCSVString(
820 db.getDataSourceObjIds(accountDeviceInstance.getDeviceId()));
823 Set<String> applicableFilters =
new HashSet<String>(Arrays.asList(
827 String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
829 CaseDbConnection connection = db.getConnection();
835 s = connection.createStatement();
838 =
"SELECT count(DISTINCT relationships.relationship_source_obj_id) as count "
839 +
" FROM account_relationships AS relationships"
840 +
" WHERE relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV +
" )"
841 +
" AND ( relationships.account1_id = " + account_id
842 +
" OR relationships.account2_id = " + account_id +
" )"
843 + (filterSQL.isEmpty() ?
"" :
" AND " + filterSQL);
845 rs = connection.executeQuery(s, queryStr);
847 return (rs.getLong(
"count"));
848 }
catch (SQLException ex) {
849 throw new TskCoreException(
"Error getting relationships count for account device instance. " + ex.getMessage(), ex);
874 if (accountDeviceInstanceList.isEmpty()) {
876 return Collections.emptySet();
879 Map<Long, Set<Long>> accountIdToDatasourceObjIdMap =
new HashMap<Long, Set<Long>>();
881 long accountID = accountDeviceInstance.getAccount().getAccountID();
882 List<Long> dataSourceObjIds = db.getDataSourceObjIds(accountDeviceInstance.getDeviceId());
884 if (accountIdToDatasourceObjIdMap.containsKey(accountID)) {
885 accountIdToDatasourceObjIdMap.get(accountID).addAll(dataSourceObjIds);
887 accountIdToDatasourceObjIdMap.put(accountID,
new HashSet<Long>(dataSourceObjIds));
891 List<String> adiSQLClauses =
new ArrayList<String>();
892 for (Map.Entry<Long, Set<Long>> entry : accountIdToDatasourceObjIdMap.entrySet()) {
893 final Long accountID = entry.getKey();
894 String datasourceObjIdsCSV = StringUtils.buildCSVString(entry.getValue());
897 "( ( relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV +
" ) )"
898 +
" AND ( relationships.account1_id = " + accountID
899 +
" OR relationships.account2_id = " + accountID +
" ) )"
902 String adiSQLClause = StringUtils.joinAsStrings(adiSQLClauses,
" OR ");
905 Set<String> applicableFilters =
new HashSet<String>(Arrays.asList(
909 String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
911 CaseDbConnection connection = db.getConnection();
917 s = connection.createStatement();
919 =
"SELECT DISTINCT artifacts.artifact_id AS artifact_id,"
920 +
" artifacts.obj_id AS obj_id,"
921 +
" artifacts.artifact_obj_id AS artifact_obj_id,"
922 +
" artifacts.data_source_obj_id AS data_source_obj_id, "
923 +
" artifacts.artifact_type_id AS artifact_type_id, "
924 +
" artifacts.review_status_id AS review_status_id "
925 +
" FROM blackboard_artifacts as artifacts"
926 +
" JOIN account_relationships AS relationships"
927 +
" ON artifacts.artifact_obj_id = relationships.relationship_source_obj_id"
929 +
" WHERE (" + adiSQLClause +
" )"
931 + (filterSQL.isEmpty() ?
"" :
" AND (" + filterSQL +
" )");
933 rs = connection.executeQuery(s, queryStr);
934 Set<Content> relationshipSources =
new HashSet<Content>();
938 rs.getLong(
"obj_id"), rs.getLong(
"artifact_obj_id"),
939 rs.getLong(
"data_source_obj_id"), bbartType.getTypeID(),
940 bbartType.getTypeName(), bbartType.getDisplayName(),
944 return relationshipSources;
945 }
catch (SQLException ex) {
946 throw new TskCoreException(
"Error getting relationships for account. " + ex.getMessage(), ex);
963 if (accountTypeToTypeIdMap.containsKey(accountType)) {
964 return accountTypeToTypeIdMap.get(accountType);
977 private Set<UnorderedAccountPair> listToUnorderedPairs(List<Long> account_ids) {
978 Set<UnorderedAccountPair> relationships =
new HashSet<UnorderedAccountPair>();
980 for (
int i = 0; i < account_ids.size(); i++) {
981 for (
int j = i + 1; j < account_ids.size(); j++) {
982 relationships.add(
new UnorderedAccountPair(account_ids.get(i), account_ids.get(j)));
986 return relationships;
989 private String normalizeAccountID(Account.Type accountType, String accountUniqueID) {
990 String normailzeAccountID = accountUniqueID;
992 if (accountType.equals(Account.Type.PHONE)) {
993 normailzeAccountID = normalizePhoneNum(accountUniqueID);
994 }
else if (accountType.equals(Account.Type.EMAIL)) {
995 normailzeAccountID = normalizeEmailAddress(accountUniqueID);
998 return normailzeAccountID;
1001 private String normalizePhoneNum(String phoneNum) {
1002 String normailzedPhoneNum = phoneNum.replaceAll(
"\\D",
"");
1004 if (phoneNum.startsWith(
"+")) {
1005 normailzedPhoneNum =
"+" + normailzedPhoneNum;
1008 return normailzedPhoneNum;
1011 private String normalizeEmailAddress(String emailAddress) {
1012 String normailzedEmailAddr = emailAddress.toLowerCase();
1014 return normailzedEmailAddr;
1029 private String getCommunicationsFilterSQL(CommunicationsFilter commFilter, Set<String> applicableFilters) {
1030 if (null == commFilter || commFilter.getAndFilters().isEmpty()) {
1035 StringBuilder sqlSB =
new StringBuilder();
1036 boolean first =
true;
1037 for (CommunicationsFilter.SubFilter subFilter : commFilter.getAndFilters()) {
1040 if (applicableFilters.contains(subFilter.getClass().getName())) {
1041 String subfilterSQL = subFilter.getSQL(
this);
1042 if (!subfilterSQL.isEmpty()) {
1046 sqlSB.append(
" AND ");
1049 sqlSB.append(subfilterSQL);
1055 if (!sqlSB.toString().isEmpty()) {
1056 sqlStr =
"( " + sqlSB.toString() +
" )";
1065 private final class UnorderedAccountPair {
1067 private final long account1_id;
1068 private final long account2_id;
1070 UnorderedAccountPair(
long account1_id,
long account2_id) {
1071 this.account1_id = account1_id;
1072 this.account2_id = account2_id;
1076 public int hashCode() {
1077 return new Long(account1_id).hashCode() +
new Long(account2_id).hashCode();
1081 public boolean equals(Object other) {
1082 if (other ==
this) {
1085 if (!(other instanceof UnorderedAccountPair)) {
1089 UnorderedAccountPair otherPair = (UnorderedAccountPair) other;
1090 return ((account1_id == otherPair.account1_id && account2_id == otherPair.account2_id)
1091 || (account1_id == otherPair.account2_id && account2_id == otherPair.account1_id));
1094 public long getFirst() {
1098 public long getSecond() {
Set< Content > getRelationshipSources(Set< AccountDeviceInstance > accountDeviceInstanceList, CommunicationsFilter filter)
void addAttributes(Collection< BlackboardAttribute > attributes)
org.sleuthkit.datamodel.Account.Type getAccountType(String accountTypeName)
AccountFileInstance createAccountFileInstance(org.sleuthkit.datamodel.Account.Type accountType, String accountUniqueID, String moduleName, Content sourceFile)
org.sleuthkit.datamodel.Account.Type addAccountType(String accountTypeName, String displayName)
void releaseSingleUserCaseReadLock()
BlackboardArtifact newBlackboardArtifact(int artifactTypeID, long obj_id)
static final List< Account.Type > PREDEFINED_ACCOUNT_TYPES
void acquireSingleUserCaseWriteLock()
BlackboardArtifact.Type getArtifactType(String artTypeName)
void releaseSingleUserCaseWriteLock()
long getRelationshipSourcesCount(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter)
void acquireSingleUserCaseReadLock()
List< AccountDeviceInstance > getAccountDeviceInstancesWithRelationships(CommunicationsFilter filter)
static ReviewStatus withID(int id)
void addRelationships(AccountFileInstance sender, List< AccountFileInstance > recipients, BlackboardArtifact sourceArtifact, org.sleuthkit.datamodel.Relationship.Type relationshipType, long dateTime)
Account getAccount(org.sleuthkit.datamodel.Account.Type accountType, String accountUniqueID)