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;
58 =
new HashSet<Integer>(Arrays.asList(
79 CaseDbConnection connection = db.getConnection();
81 Statement statement = null;
82 ResultSet resultSet = null;
85 statement = connection.createStatement();
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");
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"));
158 }
catch (SQLException ex) {
159 LOGGER.log(Level.SEVERE,
"Failed to read account_types", ex);
161 closeResultSet(resultSet);
162 closeStatement(statement);
200 CaseDbConnection connection = db.getConnection();
205 connection.beginTransaction();
206 s = connection.createStatement();
207 rs = connection.executeQuery(s,
"SELECT * FROM account_types WHERE type_name = '" + accountTypeName +
"'");
211 s.execute(
"INSERT INTO account_types (type_name, display_name) VALUES ( '" + accountTypeName +
"', '" + displayName +
"')");
214 rs = connection.executeQuery(s,
"SELECT * FROM account_types WHERE type_name = '" + accountTypeName +
"'");
217 int typeID = rs.getInt(
"account_type_id");
218 accountType =
new Account.
Type(rs.getString(
"type_name"), rs.getString(
"display_name"));
223 connection.commitTransaction();
227 int typeID = rs.getInt(
"account_type_id");
229 accountType =
new Account.
Type(rs.getString(
"type_name"), rs.getString(
"display_name"));
234 }
catch (SQLException ex) {
235 connection.rollbackTransaction();
298 CaseDbConnection connection = db.getConnection();
303 s = connection.createStatement();
304 rs = connection.executeQuery(s,
"SELECT * FROM accounts WHERE account_type_id = " + getAccountTypeId(accountType)
305 +
" AND account_unique_identifier = '" +
normalizeAccountID(accountType, accountUniqueID) +
"'");
308 account =
new Account(rs.getInt(
"account_id"), accountType,
309 rs.getString(
"account_unique_identifier"));
311 }
catch (SQLException ex) {
372 if (relationshipType.isCreatableFrom(sourceArtifact) ==
false) {
373 throw new TskDataException(
"Can not make a " + relationshipType.getDisplayName()
374 +
" relationship from a" + sourceArtifact.getDisplayName());
383 List<Long> accountIDs =
new ArrayList<Long>();
385 if (null != sender) {
386 accountIDs.add(sender.getAccount().getAccountID());
387 if (sender.getDataSourceObjectID() != sourceArtifact.getDataSourceObjectID()) {
388 throw new TskDataException(
"Sender and relationship are from different data sources :"
389 +
"Sender source ID" + sender.getDataSourceObjectID() +
" != relationship source ID" + sourceArtifact.getDataSourceObjectID());
394 accountIDs.add(recipient.getAccount().getAccountID());
395 if (recipient.getDataSourceObjectID() != sourceArtifact.getDataSourceObjectID()) {
396 throw new TskDataException(
"Recipient and relationship are from different data sources :"
397 +
"Recipient source ID" + recipient.getDataSourceObjectID() +
" != relationship source ID" + sourceArtifact.getDataSourceObjectID());
402 Iterator<UnorderedAccountPair> iter = relationships.iterator();
404 while (iter.hasNext()) {
408 sourceArtifact, relationshipType, dateTime);
409 }
catch (TskCoreException ex) {
411 LOGGER.log(Level.WARNING,
"Error adding relationship", ex);
430 if (null == account) {
431 String query =
" INTO accounts (account_type_id, account_unique_identifier) "
432 +
"VALUES ( " + getAccountTypeId(accountType) +
", '"
436 query =
"INSERT " + query +
" ON CONFLICT DO NOTHING";
439 query =
"INSERT OR IGNORE " + query;
442 throw new TskCoreException(
"Unknown DB Type: " + db.
getDatabaseType().name());
445 CaseDbConnection connection = db.getConnection();
450 connection.beginTransaction();
451 s = connection.createStatement();
455 connection.commitTransaction();
456 account =
getAccount(accountType, accountUniqueID);
457 }
catch (SQLException ex) {
458 connection.rollbackTransaction();
459 throw new TskCoreException(
"Error adding an account", ex);
489 if (null != accountArtifact) {
490 return accountArtifact;
494 accountArtifact = db.
newBlackboardArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT, sourceFile.getId());
496 Collection<BlackboardAttribute> attributes =
new ArrayList<BlackboardAttribute>();
497 attributes.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, moduleName, accountType.getTypeName()));
498 attributes.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID, moduleName, accountUniqueID));
501 return accountArtifact;
519 CaseDbConnection connection = db.getConnection();
525 s = connection.createStatement();
526 String queryStr =
"SELECT artifacts.artifact_id AS artifact_id,"
527 +
" artifacts.obj_id AS obj_id,"
528 +
" artifacts.artifact_obj_id AS artifact_obj_id,"
529 +
" artifacts.data_source_obj_id AS data_source_obj_id,"
530 +
" artifacts.artifact_type_id AS artifact_type_id,"
531 +
" artifacts.review_status_id AS review_status_id"
532 +
" FROM blackboard_artifacts AS artifacts"
533 +
" JOIN blackboard_attributes AS attr_account_type"
534 +
" ON artifacts.artifact_id = attr_account_type.artifact_id"
535 +
" JOIN blackboard_attributes AS attr_account_id"
536 +
" ON artifacts.artifact_id = attr_account_id.artifact_id"
538 +
" AND attr_account_id.value_text = '" + accountUniqueID +
"'"
541 +
" AND attr_account_type.value_text = '" + accountType.getTypeName() +
"'"
542 +
" AND artifacts.obj_id = " + sourceFile.getId();
544 rs = connection.executeQuery(s, queryStr);
548 accountArtifact =
new BlackboardArtifact(db, rs.getLong(
"artifact_id"), rs.getLong(
"obj_id"), rs.getLong(
"artifact_obj_id"), rs.getLong(
"data_source_obj_id"),
549 bbartType.getTypeID(), bbartType.getTypeName(), bbartType.getDisplayName(),
552 }
catch (SQLException ex) {
553 throw new TskCoreException(
"Error getting account", ex);
561 return accountArtifact;
579 CaseDbConnection connection = db.getConnection();
585 s = connection.createStatement();
586 rs = connection.executeQuery(s,
"SELECT account_type_id, type_name, display_name, value_type FROM account_types WHERE type_name = '" + accountTypeName +
"'");
589 accountType =
new Account.
Type(accountTypeName, rs.getString(
"display_name"));
594 }
catch (SQLException ex) {
595 throw new TskCoreException(
"Error getting account type id", ex);
617 CaseDbConnection connection = db.getConnection();
623 s = connection.createStatement();
624 rs = connection.executeQuery(s,
"SELECT account_types.type_name as type_name,"
625 +
" account_types.display_name as display_name,"
626 +
" accounts.account_id as account_id,"
627 +
" accounts.account_unique_identifier as account_unique_identifier"
628 +
" FROM accounts as accounts"
629 +
" JOIN account_types as account_types"
630 +
" ON accounts.account_type_id = account_types.account_type_id"
631 +
" WHERE accounts.account_id = " + account_id);
635 account =
new Account(rs.getInt(
"account_id"), accountType, rs.getString(
"account_unique_identifier"));
637 }
catch (SQLException ex) {
638 throw new TskCoreException(
"Error getting account from account_id", ex);
663 CaseDbConnection connection = db.getConnection();
669 String dateTimeValStr = (dateTime > 0) ? Long.toString(dateTime) :
"NULL";
671 connection.beginTransaction();
672 s = connection.createStatement();
673 String query =
"INTO account_relationships (account1_id, account2_id, relationship_source_obj_id, date_time, relationship_type, data_source_obj_id ) "
674 +
"VALUES ( " + account1_id +
", " + account2_id +
", " + relationshipaArtifact.getId() +
", " + dateTimeValStr +
", " + relationshipType.getTypeID() +
", " + relationshipaArtifact.getDataSourceObjectID() +
")";
677 query =
"INSERT " + query +
" ON CONFLICT DO NOTHING";
680 query =
"INSERT OR IGNORE " + query;
683 throw new TskCoreException(
"Unknown DB Type: " + db.
getDatabaseType().name());
686 connection.commitTransaction();
687 }
catch (SQLException ex) {
688 connection.rollbackTransaction();
689 throw new TskCoreException(
"Error adding accounts relationship", ex);
713 CaseDbConnection connection = db.getConnection();
719 s = connection.createStatement();
722 Set<String> applicableInnerQueryFilters =
new HashSet<String>(Arrays.asList(
729 String innerQueryTemplate
730 =
" SELECT %1$1s as account_id,"
731 +
" data_source_obj_id"
732 +
" FROM account_relationships as relationships"
733 + (innerQueryfilterSQL.isEmpty() ?
"" :
" WHERE " + innerQueryfilterSQL);
735 String innerQuery1 = String.format(innerQueryTemplate,
"account1_id");
736 String innerQuery2 = String.format(innerQueryTemplate,
"account2_id");
739 String combinedInnerQuery
740 =
"SELECT count(*) as relationship_count, account_id, data_source_obj_id "
741 +
" FROM ( " + innerQuery1 +
" UNION " + innerQuery2 +
" ) AS inner_union"
742 +
" GROUP BY account_id, data_source_obj_id";
745 Set<String> applicableFilters =
new HashSet<String>(Arrays.asList(
753 " accounts.account_id AS account_id,"
754 +
" accounts.account_unique_identifier AS account_unique_identifier,"
756 +
" account_types.type_name AS type_name,"
758 +
" relationship_count,"
759 +
" data_source_info.device_id AS device_id"
760 +
" FROM ( " + combinedInnerQuery +
" ) AS account_device_instances"
761 +
" JOIN accounts AS accounts"
762 +
" ON accounts.account_id = account_device_instances.account_id"
763 +
" JOIN account_types AS account_types"
764 +
" ON accounts.account_type_id = account_types.account_type_id"
765 +
" JOIN data_source_info AS data_source_info"
766 +
" ON account_device_instances.data_source_obj_id = data_source_info.obj_id"
767 + (filterSQL.isEmpty() ?
"" :
" WHERE " + filterSQL);
771 queryStr =
"SELECT DISTINCT ON ( accounts.account_id, data_source_info.device_id) " + queryStr;
774 queryStr =
"SELECT " + queryStr +
" GROUP BY accounts.account_id, data_source_info.device_id";
777 throw new TskCoreException(
"Unknown DB Type: " + db.
getDatabaseType().name());
780 rs = connection.executeQuery(s, queryStr);
781 ArrayList<AccountDeviceInstance> accountDeviceInstances =
new ArrayList<AccountDeviceInstance>();
783 long account_id = rs.getLong(
"account_id");
784 String deviceID = rs.getString(
"device_id");
785 final String type_name = rs.getString(
"type_name");
786 final String account_unique_identifier = rs.getString(
"account_unique_identifier");
789 Account account =
new Account(account_id, accountType, account_unique_identifier);
793 return accountDeviceInstances;
794 }
catch (SQLException ex) {
795 throw new TskCoreException(
"Error getting account device instances. " + ex.getMessage(), ex);
820 long account_id = accountDeviceInstance.getAccount().getAccountID();
823 String datasourceObjIdsCSV = StringUtils.buildCSVString(
824 db.getDataSourceObjIds(accountDeviceInstance.getDeviceId()));
827 Set<String> applicableFilters =
new HashSet<String>(Arrays.asList(
833 CaseDbConnection connection = db.getConnection();
839 s = connection.createStatement();
842 =
"SELECT count(DISTINCT relationships.relationship_source_obj_id) as count "
843 +
" FROM account_relationships AS relationships"
844 +
" WHERE relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV +
" )"
845 +
" AND ( relationships.account1_id = " + account_id
846 +
" OR relationships.account2_id = " + account_id +
" )"
847 + (filterSQL.isEmpty() ?
"" :
" AND " + filterSQL);
849 rs = connection.executeQuery(s, queryStr);
851 return (rs.getLong(
"count"));
852 }
catch (SQLException ex) {
853 throw new TskCoreException(
"Error getting relationships count for account device instance. " + ex.getMessage(), ex);
878 if (accountDeviceInstanceList.isEmpty()) {
880 return Collections.emptySet();
883 Map<Long, Set<Long>> accountIdToDatasourceObjIdMap =
new HashMap<Long, Set<Long>>();
885 long accountID = accountDeviceInstance.getAccount().getAccountID();
886 List<Long> dataSourceObjIds = db.getDataSourceObjIds(accountDeviceInstance.getDeviceId());
888 if (accountIdToDatasourceObjIdMap.containsKey(accountID)) {
889 accountIdToDatasourceObjIdMap.get(accountID).addAll(dataSourceObjIds);
891 accountIdToDatasourceObjIdMap.put(accountID,
new HashSet<Long>(dataSourceObjIds));
895 List<String> adiSQLClauses =
new ArrayList<String>();
896 for (Map.Entry<Long, Set<Long>> entry : accountIdToDatasourceObjIdMap.entrySet()) {
897 final Long accountID = entry.getKey();
898 String datasourceObjIdsCSV = StringUtils.buildCSVString(entry.getValue());
901 "( ( relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV +
" ) )"
902 +
" AND ( relationships.account1_id = " + accountID
903 +
" OR relationships.account2_id = " + accountID +
" ) )"
906 String adiSQLClause = StringUtils.joinAsStrings(adiSQLClauses,
" OR ");
909 Set<String> applicableFilters =
new HashSet<String>(Arrays.asList(
915 CaseDbConnection connection = db.getConnection();
921 s = connection.createStatement();
923 =
"SELECT DISTINCT artifacts.artifact_id AS artifact_id,"
924 +
" artifacts.obj_id AS obj_id,"
925 +
" artifacts.artifact_obj_id AS artifact_obj_id,"
926 +
" artifacts.data_source_obj_id AS data_source_obj_id, "
927 +
" artifacts.artifact_type_id AS artifact_type_id, "
928 +
" artifacts.review_status_id AS review_status_id "
929 +
" FROM blackboard_artifacts as artifacts"
930 +
" JOIN account_relationships AS relationships"
931 +
" ON artifacts.artifact_obj_id = relationships.relationship_source_obj_id"
933 +
" WHERE (" + adiSQLClause +
" )"
935 + (filterSQL.isEmpty() ?
"" :
" AND (" + filterSQL +
" )");
937 rs = connection.executeQuery(s, queryStr);
938 Set<Content> relationshipSources =
new HashSet<Content>();
942 rs.getLong(
"obj_id"), rs.getLong(
"artifact_obj_id"),
943 rs.getLong(
"data_source_obj_id"), bbartType.getTypeID(),
944 bbartType.getTypeName(), bbartType.getDisplayName(),
948 return relationshipSources;
949 }
catch (SQLException ex) {
950 throw new TskCoreException(
"Error getting relationships for account. " + ex.getMessage(), ex);
982 Set<UnorderedAccountPair> relationships =
new HashSet<UnorderedAccountPair>();
984 for (
int i = 0; i < account_ids.size(); i++) {
985 for (
int j = i + 1; j < account_ids.size(); j++) {
990 return relationships;
994 String normailzeAccountID = accountUniqueID;
1002 return normailzeAccountID;
1006 String normailzedPhoneNum = phoneNum.replaceAll(
"\\D",
"");
1008 if (phoneNum.startsWith(
"+")) {
1009 normailzedPhoneNum =
"+" + normailzedPhoneNum;
1012 return normailzedPhoneNum;
1016 String normailzedEmailAddr = emailAddress.toLowerCase();
1018 return normailzedEmailAddr;
1034 if (null == commFilter || commFilter.getAndFilters().isEmpty()) {
1039 StringBuilder sqlSB =
new StringBuilder();
1040 boolean first =
true;
1044 if (applicableFilters.contains(subFilter.getClass().getName())) {
1045 String subfilterSQL = subFilter.getSQL(
this);
1046 if (!subfilterSQL.isEmpty()) {
1050 sqlSB.append(
" AND ");
1053 sqlSB.append(subfilterSQL);
1059 if (!sqlSB.toString().isEmpty()) {
1060 sqlStr =
"( " + sqlSB.toString() +
" )";
1081 return new Long(account1_id).hashCode() +
new Long(account2_id).hashCode();
1086 if (other ==
this) {
1093 UnorderedAccountPair otherPair = (UnorderedAccountPair) other;
BlackboardArtifact getAccountFileInstanceArtifact(Account.Type accountType, String accountUniqueID, Content sourceFile)
Set< Content > getRelationshipSources(Set< AccountDeviceInstance > accountDeviceInstanceList, CommunicationsFilter filter)
Account getAccount(Account.Type accountType, String accountUniqueID)
void addAttributes(Collection< BlackboardAttribute > attributes)
Account.Type addAccountType(String accountTypeName, String displayName)
String getCommunicationsFilterSQL(CommunicationsFilter commFilter, Set< String > applicableFilters)
boolean equals(Object other)
Set< UnorderedAccountPair > listToUnorderedPairs(List< Long > account_ids)
Account getOrCreateAccount(Account.Type accountType, String accountUniqueID)
Account getAccount(long account_id)
String normalizePhoneNum(String phoneNum)
void addRelationships(AccountFileInstance sender, List< AccountFileInstance > recipients, BlackboardArtifact sourceArtifact, Relationship.Type relationshipType, long dateTime)
final Map< String, Account.Type > typeNameToAccountTypeMap
static final Account.Type PHONE
AccountFileInstance createAccountFileInstance(Account.Type accountType, String accountUniqueID, String moduleName, Content sourceFile)
String normalizeAccountID(Account.Type accountType, String accountUniqueID)
static final Set< Integer > RELATIONSHIP_ARTIFACT_TYPE_IDS
String normalizeEmailAddress(String emailAddress)
void releaseSingleUserCaseReadLock()
final Map< Account.Type, Integer > accountTypeToTypeIdMap
Account.Type getAccountType(String accountTypeName)
BlackboardArtifact newBlackboardArtifact(int artifactTypeID, long obj_id)
static final Logger LOGGER
static final List< Account.Type > PREDEFINED_ACCOUNT_TYPES
void acquireSingleUserCaseWriteLock()
BlackboardArtifact.Type getArtifactType(String artTypeName)
void releaseSingleUserCaseWriteLock()
static final String RELATIONSHIP_ARTIFACT_TYPE_IDS_CSV_STR
void addAccountsRelationship(long account1_id, long account2_id, BlackboardArtifact relationshipaArtifact, Relationship.Type relationshipType, long dateTime)
long getRelationshipSourcesCount(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter)
void acquireSingleUserCaseReadLock()
List< AccountDeviceInstance > getAccountDeviceInstancesWithRelationships(CommunicationsFilter filter)
static ReviewStatus withID(int id)
static final Account.Type EMAIL