Sleuth Kit Java Bindings (JNI)  4.6
Java bindings for using The Sleuth Kit
CommunicationsManager.java
Go to the documentation of this file.
1 /*
2  * Sleuth Kit Data Model
3  *
4  * Copyright 2017-18 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.datamodel;
20 
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.List;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.concurrent.ConcurrentHashMap;
34 import java.util.logging.Level;
35 import java.util.logging.Logger;
37 import static org.sleuthkit.datamodel.SleuthkitCase.closeResultSet;
38 import static org.sleuthkit.datamodel.SleuthkitCase.closeStatement;
39 
44 public final class CommunicationsManager {
45 
46  private static final Logger LOGGER = Logger.getLogger(CommunicationsManager.class.getName());
47 
48  private final SleuthkitCase db;
49 
50  private final Map<Account.Type, Integer> accountTypeToTypeIdMap
51  = new ConcurrentHashMap<Account.Type, Integer>();
52  private final Map<String, Account.Type> typeNameToAccountTypeMap
53  = new ConcurrentHashMap<String, Account.Type>();
54 
55  // Artifact types that represent a relationship between accounts
56  private final static Set<Integer> RELATIONSHIP_ARTIFACT_TYPE_IDS
57  = new HashSet<Integer>(Arrays.asList(
62  ));
63  private static final String RELATIONSHIP_ARTIFACT_TYPE_IDS_CSV_STR
64  = StringUtils.buildCSVString(RELATIONSHIP_ARTIFACT_TYPE_IDS);
65 
75  this.db = skCase;
76  initAccountTypes();
77  }
78 
85  private void initAccountTypes() throws TskCoreException {
86  CaseDbConnection connection = db.getConnection();
88  Statement statement = null;
89  ResultSet resultSet = null;
90 
91  try {
92  statement = connection.createStatement();
93  // Read the table
94  int count = readAccountTypes();
95  if (0 == count) {
96  // Table is empty, populate it with predefined types
98  try {
99  statement.execute("INSERT INTO account_types (type_name, display_name) VALUES ( '" + type.getTypeName() + "', '" + type.getDisplayName() + "')"); //NON-NLS
100  } catch (SQLException ex) {
101  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM account_types WHERE type_name = '" + type.getTypeName() + "'"); //NON-NLS
102  resultSet.next();
103  if (resultSet.getLong("count") == 0) {
104  throw ex;
105  }
106  resultSet.close();
107  }
108 
109  ResultSet rs2 = connection.executeQuery(statement, "SELECT account_type_id FROM account_types WHERE type_name = '" + type.getTypeName() + "'"); //NON-NLS
110  rs2.next();
111  int typeID = rs2.getInt("account_type_id");
112  rs2.close();
113 
114  Account.Type accountType = new Account.Type(type.getTypeName(), type.getDisplayName());
115  this.accountTypeToTypeIdMap.put(accountType, typeID);
116  this.typeNameToAccountTypeMap.put(type.getTypeName(), accountType);
117  }
118  }
119  } catch (SQLException ex) {
120  LOGGER.log(Level.SEVERE, "Failed to add row to account_types", ex);
121  } finally {
122  closeResultSet(resultSet);
123  closeStatement(statement);
124  connection.close();
126  }
127  }
128 
137  private int readAccountTypes() throws TskCoreException {
138  CaseDbConnection connection = db.getConnection();
140  Statement statement = null;
141  ResultSet resultSet = null;
142  int count = 0;
143 
144  try {
145  statement = connection.createStatement();
146 
147  // If the account_types table is already populated, say when opening a case, then load it
148  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM account_types"); //NON-NLS
149  resultSet.next();
150  if (resultSet.getLong("count") > 0) {
151 
152  resultSet.close();
153  resultSet = connection.executeQuery(statement, "SELECT * FROM account_types");
154  while (resultSet.next()) {
155  Account.Type accountType = new Account.Type(resultSet.getString("type_name"), resultSet.getString("display_name"));
156  this.accountTypeToTypeIdMap.put(accountType, resultSet.getInt("account_type_id"));
157  this.typeNameToAccountTypeMap.put(accountType.getTypeName(), accountType);
158  }
159  count = this.typeNameToAccountTypeMap.size();
160  }
161 
162  } catch (SQLException ex) {
163  throw new TskCoreException("Failed to read account_types", ex);
164  } finally {
165  closeResultSet(resultSet);
166  closeStatement(statement);
167  connection.close();
169  }
170 
171  return count;
172  }
173 
179  SleuthkitCase getSleuthkitCase() {
180  return this.db;
181  }
182 
195  // NOTE: Full name given for Type for doxygen linking
196  public org.sleuthkit.datamodel.Account.Type addAccountType(String accountTypeName, String displayName) throws TskCoreException {
197  Account.Type accountType = new Account.Type(accountTypeName, displayName);
198 
199  // check if already in map
200  if (this.accountTypeToTypeIdMap.containsKey(accountType)) {
201  return accountType;
202  }
203 
204  CaseDbConnection connection = db.getConnection();
206  Statement s = null;
207  ResultSet rs = null;
208  try {
209  connection.beginTransaction();
210  s = connection.createStatement();
211  rs = connection.executeQuery(s, "SELECT * FROM account_types WHERE type_name = '" + accountTypeName + "'"); //NON-NLS
212  if (!rs.next()) {
213  rs.close();
214 
215  s.execute("INSERT INTO account_types (type_name, display_name) VALUES ( '" + accountTypeName + "', '" + displayName + "')"); //NON-NLS
216 
217  // Read back the typeID
218  rs = connection.executeQuery(s, "SELECT * FROM account_types WHERE type_name = '" + accountTypeName + "'"); //NON-NLS
219  rs.next();
220 
221  int typeID = rs.getInt("account_type_id");
222  accountType = new Account.Type(rs.getString("type_name"), rs.getString("display_name"));
223 
224  this.accountTypeToTypeIdMap.put(accountType, typeID);
225  this.typeNameToAccountTypeMap.put(accountTypeName, accountType);
226 
227  connection.commitTransaction();
228 
229  return accountType;
230  } else {
231  int typeID = rs.getInt("account_type_id");
232 
233  accountType = new Account.Type(rs.getString("type_name"), rs.getString("display_name"));
234  this.accountTypeToTypeIdMap.put(accountType, typeID);
235 
236  return accountType;
237  }
238  } catch (SQLException ex) {
239  connection.rollbackTransaction();
240  throw new TskCoreException("Error adding account type", ex);
241  } finally {
242  closeResultSet(rs);
243  closeStatement(s);
244  connection.close();
246  }
247  }
248 
266  // NOTE: Full name given for Type for doxygen linking
267  public AccountFileInstance createAccountFileInstance(org.sleuthkit.datamodel.Account.Type accountType, String accountUniqueID, String moduleName, Content sourceFile) throws TskCoreException {
268 
269  // make or get the Account (unique at the case-level)
270  Account account = getOrCreateAccount(accountType, normalizeAccountID(accountType, accountUniqueID));
271 
272  /*
273  * make or get the artifact. Will not create one if it already exists
274  * for the sourceFile. Such as an email PST that has the same email
275  * address multiple times. Only one artifact is created for each email
276  * message in that PST.
277  */
278  BlackboardArtifact accountArtifact = getOrCreateAccountFileInstanceArtifact(accountType, normalizeAccountID(accountType, accountUniqueID), moduleName, sourceFile);
279 
280  // The account instance map was unused so we have removed it from the database,
281  // but we expect we may need it so I am preserving this method comment and usage here.
282  // add a row to Accounts to Instances mapping table
283  // @@@ BC: Seems like we should only do this if we had to create the artifact.
284  // But, it will probably fail to create a new one based on unique constraints.
285  // addAccountFileInstanceMapping(account.getAccountID(), accountArtifact.getArtifactID());
286  return new AccountFileInstance(accountArtifact, account);
287  }
288 
301  // NOTE: Full name given for Type for doxygen linking
302  public Account getAccount(org.sleuthkit.datamodel.Account.Type accountType, String accountUniqueID) throws TskCoreException {
303  Account account = null;
304  CaseDbConnection connection = db.getConnection();
306  Statement s = null;
307  ResultSet rs = null;
308  try {
309  s = connection.createStatement();
310  rs = connection.executeQuery(s, "SELECT * FROM accounts WHERE account_type_id = " + getAccountTypeId(accountType)
311  + " AND account_unique_identifier = '" + normalizeAccountID(accountType, accountUniqueID) + "'"); //NON-NLS
312 
313  if (rs.next()) {
314  account = new Account(rs.getInt("account_id"), accountType,
315  rs.getString("account_unique_identifier"));
316  }
317  } catch (SQLException ex) {
318  throw new TskCoreException("Error getting account type id", ex);
319  } finally {
320  closeResultSet(rs);
321  closeStatement(s);
322  connection.close();
324  }
325 
326  return account;
327  }
328 
351  // NOTE: Full name given for Type for doxygen linking
352  public void addRelationships(AccountFileInstance sender, List<AccountFileInstance> recipients,
353  BlackboardArtifact sourceArtifact, org.sleuthkit.datamodel.Relationship.Type relationshipType, long dateTime) throws TskCoreException, TskDataException {
354 
355  if (relationshipType.isCreatableFrom(sourceArtifact) == false) {
356  throw new TskDataException("Can not make a " + relationshipType.getDisplayName()
357  + " relationship from a" + sourceArtifact.getDisplayName());
358  }
359 
360  /*
361  * Enforce that all accounts and the relationship between them are from
362  * the same 'source'. This is required for the queries to work
363  * correctly.
364  */
365  // Currently we do not save the direction of communication
366  List<Long> accountIDs = new ArrayList<Long>();
367 
368  if (null != sender) {
369  accountIDs.add(sender.getAccount().getAccountID());
370  if (sender.getDataSourceObjectID() != sourceArtifact.getDataSourceObjectID()) {
371  throw new TskDataException("Sender and relationship are from different data sources :"
372  + "Sender source ID" + sender.getDataSourceObjectID() + " != relationship source ID" + sourceArtifact.getDataSourceObjectID());
373  }
374  }
375 
376  for (AccountFileInstance recipient : recipients) {
377  accountIDs.add(recipient.getAccount().getAccountID());
378  if (recipient.getDataSourceObjectID() != sourceArtifact.getDataSourceObjectID()) {
379  throw new TskDataException("Recipient and relationship are from different data sources :"
380  + "Recipient source ID" + recipient.getDataSourceObjectID() + " != relationship source ID" + sourceArtifact.getDataSourceObjectID());
381  }
382  }
383 
384  for (int i = 0; i < accountIDs.size(); i++) {
385  for (int j = i + 1; j < accountIDs.size(); j++) {
386  try {
387  addAccountsRelationship(accountIDs.get(i), accountIDs.get(j),
388  sourceArtifact, relationshipType, dateTime);
389  } catch (TskCoreException ex) {
390  // @@@ This should probably not be caught and instead we stop adding
391  LOGGER.log(Level.WARNING, "Error adding relationship", ex); //NON-NLS
392  }
393  }
394  }
395  }
396 
409  private Account getOrCreateAccount(Account.Type accountType, String accountUniqueID) throws TskCoreException {
410  Account account = getAccount(accountType, accountUniqueID);
411  if (null == account) {
412  String query = " INTO accounts (account_type_id, account_unique_identifier) "
413  + "VALUES ( " + getAccountTypeId(accountType) + ", '"
414  + normalizeAccountID(accountType, accountUniqueID) + "'" + ")";
415  switch (db.getDatabaseType()) {
416  case POSTGRESQL:
417  query = "INSERT " + query + " ON CONFLICT DO NOTHING"; //NON-NLS
418  break;
419  case SQLITE:
420  query = "INSERT OR IGNORE " + query;
421  break;
422  default:
423  throw new TskCoreException("Unknown DB Type: " + db.getDatabaseType().name());
424  }
425 
426  CaseDbConnection connection = db.getConnection();
428  Statement s = null;
429  ResultSet rs = null;
430  try {
431  connection.beginTransaction();
432  s = connection.createStatement();
433 
434  s.execute(query);
435 
436  connection.commitTransaction();
437  account = getAccount(accountType, accountUniqueID);
438  } catch (SQLException ex) {
439  connection.rollbackTransaction();
440  throw new TskCoreException("Error adding an account", ex);
441  } finally {
442  closeResultSet(rs);
443  closeStatement(s);
444  connection.close();
446  }
447  }
448 
449  return account;
450  }
451 
467  BlackboardArtifact getOrCreateAccountFileInstanceArtifact(Account.Type accountType, String accountUniqueID, String moduleName, Content sourceFile) throws TskCoreException {
468 
469  // see if it already exists
470  BlackboardArtifact accountArtifact = getAccountFileInstanceArtifact(accountType, accountUniqueID, sourceFile);
471  if (null != accountArtifact) {
472  return accountArtifact;
473  }
474 
475  // Create a new artifact.
476  accountArtifact = db.newBlackboardArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT, sourceFile.getId());
477 
478  Collection<BlackboardAttribute> attributes = new ArrayList<BlackboardAttribute>();
479  attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, moduleName, accountType.getTypeName()));
480  attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID, moduleName, accountUniqueID));
481  accountArtifact.addAttributes(attributes);
482 
483  return accountArtifact;
484  }
485 
499  private BlackboardArtifact getAccountFileInstanceArtifact(Account.Type accountType, String accountUniqueID, Content sourceFile) throws TskCoreException {
500  BlackboardArtifact accountArtifact = null;
501  CaseDbConnection connection = db.getConnection();
503  Statement s = null;
504  ResultSet rs = null;
505 
506  try {
507  s = connection.createStatement();
508  String queryStr = "SELECT artifacts.artifact_id AS artifact_id,"
509  + " artifacts.obj_id AS obj_id,"
510  + " artifacts.artifact_obj_id AS artifact_obj_id,"
511  + " artifacts.data_source_obj_id AS data_source_obj_id,"
512  + " artifacts.artifact_type_id AS artifact_type_id,"
513  + " artifacts.review_status_id AS review_status_id"
514  + " FROM blackboard_artifacts AS artifacts"
515  + " JOIN blackboard_attributes AS attr_account_type"
516  + " ON artifacts.artifact_id = attr_account_type.artifact_id"
517  + " JOIN blackboard_attributes AS attr_account_id"
518  + " ON artifacts.artifact_id = attr_account_id.artifact_id"
519  + " AND attr_account_id.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID.getTypeID()
520  + " AND attr_account_id.value_text = '" + accountUniqueID + "'"
521  + " WHERE artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()
522  + " AND attr_account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()
523  + " AND attr_account_type.value_text = '" + accountType.getTypeName() + "'"
524  + " AND artifacts.obj_id = " + sourceFile.getId(); //NON-NLS
525 
526  rs = connection.executeQuery(s, queryStr); //NON-NLS
527  if (rs.next()) {
528  BlackboardArtifact.Type bbartType = db.getArtifactType(rs.getInt("artifact_type_id"));
529 
530  accountArtifact = new BlackboardArtifact(db, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"),
531  bbartType.getTypeID(), bbartType.getTypeName(), bbartType.getDisplayName(),
532  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")));
533  }
534  } catch (SQLException ex) {
535  throw new TskCoreException("Error getting account", ex);
536  } finally {
537  closeResultSet(rs);
538  closeStatement(s);
539  connection.close();
541  }
542 
543  return accountArtifact;
544  }
545 
555  // NOTE: Full name given for Type for doxygen linking
556  public org.sleuthkit.datamodel.Account.Type getAccountType(String accountTypeName) throws TskCoreException {
557  if (this.typeNameToAccountTypeMap.containsKey(accountTypeName)) {
558  return this.typeNameToAccountTypeMap.get(accountTypeName);
559  }
560 
561  CaseDbConnection connection = db.getConnection();
563  Statement s = null;
564  ResultSet rs = null;
565 
566  try {
567  s = connection.createStatement();
568  rs = connection.executeQuery(s, "SELECT account_type_id, type_name, display_name FROM account_types WHERE type_name = '" + accountTypeName + "'"); //NON-NLS
569  Account.Type accountType = null;
570  if (rs.next()) {
571  accountType = new Account.Type(accountTypeName, rs.getString("display_name"));
572  this.accountTypeToTypeIdMap.put(accountType, rs.getInt("account_type_id"));
573  this.typeNameToAccountTypeMap.put(accountTypeName, accountType);
574  }
575  return accountType;
576  } catch (SQLException ex) {
577  throw new TskCoreException("Error getting account type id", ex);
578  } finally {
579  closeResultSet(rs);
580  closeStatement(s);
581  connection.close();
583  }
584  }
585 
599  private void addAccountsRelationship(long account1_id, long account2_id, BlackboardArtifact relationshipaArtifact, Relationship.Type relationshipType, long dateTime) throws TskCoreException {
600  CaseDbConnection connection = db.getConnection();
602  Statement s = null;
603  ResultSet rs = null;
604 
605  try {
606  String dateTimeValStr = (dateTime > 0) ? Long.toString(dateTime) : "NULL";
607 
608  connection.beginTransaction();
609  s = connection.createStatement();
610  String query = "INTO account_relationships (account1_id, account2_id, relationship_source_obj_id, date_time, relationship_type, data_source_obj_id ) "
611  + "VALUES ( " + account1_id + ", " + account2_id + ", " + relationshipaArtifact.getId() + ", " + dateTimeValStr + ", " + relationshipType.getTypeID() + ", " + relationshipaArtifact.getDataSourceObjectID() + ")";
612  switch (db.getDatabaseType()) {
613  case POSTGRESQL:
614  query = "INSERT " + query + " ON CONFLICT DO NOTHING";
615  break;
616  case SQLITE:
617  query = "INSERT OR IGNORE " + query;
618  break;
619  default:
620  throw new TskCoreException("Unknown DB Type: " + db.getDatabaseType().name());
621  }
622  s.execute(query); //NON-NLS
623  connection.commitTransaction();
624  } catch (SQLException ex) {
625  connection.rollbackTransaction();
626  throw new TskCoreException("Error adding accounts relationship", ex);
627  } finally {
628  closeResultSet(rs);
629  closeStatement(s);
630  connection.close();
632  }
633  }
634 
649  public List<AccountDeviceInstance> getAccountDeviceInstancesWithRelationships(CommunicationsFilter filter) throws TskCoreException {
650  CaseDbConnection connection = db.getConnection();
652  Statement s = null;
653  ResultSet rs = null;
654 
655  try {
656  s = connection.createStatement();
657 
658  //set up applicable filters
659  Set<String> applicableInnerQueryFilters = new HashSet<String>(Arrays.asList(
660  CommunicationsFilter.DateRangeFilter.class.getName(),
661  CommunicationsFilter.DeviceFilter.class.getName(),
663  ));
664  String relationshipFilterSQL = getCommunicationsFilterSQL(filter, applicableInnerQueryFilters);
665 
666  String relationshipLimitSQL = getMostRecentFilterLimitSQL(filter);
667 
668  String relTblfilterQuery =
669  "SELECT * "
670  + "FROM account_relationships as relationships"
671  + (relationshipFilterSQL.isEmpty() ? "" : " WHERE " + relationshipFilterSQL)
672  + (relationshipLimitSQL.isEmpty() ? "" : relationshipLimitSQL);
673 
674  String uniqueAccountQueryTemplate
675  = " SELECT %1$1s as account_id,"
676  + " data_source_obj_id"
677  + " FROM ( " + relTblfilterQuery + ")AS %2$s";
678 
679  String relationshipTableFilterQuery1 = String.format(uniqueAccountQueryTemplate, "account1_id", "union_query_1");
680  String relationshipTableFilterQuery2 = String.format(uniqueAccountQueryTemplate, "account2_id", "union_query_2");
681 
682  //this query groups by account_id and data_source_obj_id across both innerQueries
683  String uniqueAccountQuery
684  = "SELECT DISTINCT account_id, data_source_obj_id"
685  + " FROM ( " + relationshipTableFilterQuery1 + " UNION " + relationshipTableFilterQuery2 + " ) AS inner_union"
686  + " GROUP BY account_id, data_source_obj_id";
687 
688  // set up applicable filters
689  Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
691  ));
692 
693  String accountTypeFilterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
694 
695  String queryStr
696  = //account info
697  " accounts.account_id AS account_id,"
698  + " accounts.account_unique_identifier AS account_unique_identifier,"
699  //account type info
700  + " account_types.type_name AS type_name,"
701  //Account device instance info
702  + " data_source_info.device_id AS device_id"
703  + " FROM ( " + uniqueAccountQuery + " ) AS account_device_instances"
704  + " JOIN accounts AS accounts"
705  + " ON accounts.account_id = account_device_instances.account_id"
706  + " JOIN account_types AS account_types"
707  + " ON accounts.account_type_id = account_types.account_type_id"
708  + " JOIN data_source_info AS data_source_info"
709  + " ON account_device_instances.data_source_obj_id = data_source_info.obj_id"
710  + (accountTypeFilterSQL.isEmpty() ? "" : " WHERE " + accountTypeFilterSQL);
711 
712  switch (db.getDatabaseType()) {
713  case POSTGRESQL:
714  queryStr = "SELECT DISTINCT ON ( accounts.account_id, data_source_info.device_id) " + queryStr;
715  break;
716  case SQLITE:
717  queryStr = "SELECT " + queryStr + " GROUP BY accounts.account_id, data_source_info.device_id";
718  break;
719  default:
720  throw new TskCoreException("Unknown DB Type: " + db.getDatabaseType().name());
721  }
722 
723  rs = connection.executeQuery(s, queryStr); //NON-NLS
724  ArrayList<AccountDeviceInstance> accountDeviceInstances = new ArrayList<AccountDeviceInstance>();
725  while (rs.next()) {
726  long account_id = rs.getLong("account_id");
727  String deviceID = rs.getString("device_id");
728  final String type_name = rs.getString("type_name");
729  final String account_unique_identifier = rs.getString("account_unique_identifier");
730 
731  Account.Type accountType = typeNameToAccountTypeMap.get(type_name);
732  Account account = new Account(account_id, accountType, account_unique_identifier);
733  accountDeviceInstances.add(new AccountDeviceInstance(account, deviceID));
734  }
735 
736  return accountDeviceInstances;
737  } catch (SQLException ex) {
738  throw new TskCoreException("Error getting account device instances. " + ex.getMessage(), ex);
739  } finally {
740  closeResultSet(rs);
741  closeStatement(s);
742  connection.close();
744  }
745  }
746 
766  public Map<AccountPair, Long> getRelationshipCountsPairwise(Set<AccountDeviceInstance> accounts, CommunicationsFilter filter) throws TskCoreException {
767 
768  Set<Long> accountIDs = new HashSet<Long>();
769  Set<String> accountDeviceIDs = new HashSet<String>();
770  for (AccountDeviceInstance adi : accounts) {
771  accountIDs.add(adi.getAccount().getAccountID());
772  accountDeviceIDs.add("'" + adi.getDeviceId() + "'");
773  }
774  //set up applicable filters
775  Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
776  CommunicationsFilter.DateRangeFilter.class.getName(),
777  CommunicationsFilter.DeviceFilter.class.getName(),
779  ));
780 
781  String accountIDsCSL = StringUtils.buildCSVString(accountIDs);
782  String accountDeviceIDsCSL = StringUtils.buildCSVString(accountDeviceIDs);
783  String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
784 
785  final String queryString
786  = " SELECT count(DISTINCT relationships.relationship_source_obj_id) AS count," //realtionship count
787  + " data_source_info.device_id AS device_id,"
788  //account 1 info
789  + " accounts1.account_id AS account1_id,"
790  + " accounts1.account_unique_identifier AS account1_unique_identifier,"
791  + " account_types1.type_name AS type_name1,"
792  + " account_types1.display_name AS display_name1,"
793  //account 2 info
794  + " accounts2.account_id AS account2_id,"
795  + " accounts2.account_unique_identifier AS account2_unique_identifier,"
796  + " account_types2.type_name AS type_name2,"
797  + " account_types2.display_name AS display_name2"
798  + " FROM account_relationships AS relationships"
799  + " JOIN data_source_info AS data_source_info"
800  + " ON relationships.data_source_obj_id = data_source_info.obj_id "
801  //account1 aliases
802  + " JOIN accounts AS accounts1 "
803  + " ON accounts1.account_id = relationships.account1_id"
804  + " JOIN account_types AS account_types1"
805  + " ON accounts1.account_type_id = account_types1.account_type_id"
806  //account2 aliases
807  + " JOIN accounts AS accounts2 "
808  + " ON accounts2.account_id = relationships.account2_id"
809  + " JOIN account_types AS account_types2"
810  + " ON accounts2.account_type_id = account_types2.account_type_id"
811  + " WHERE (( relationships.account1_id IN (" + accountIDsCSL + ")) "
812  + " AND ( relationships.account2_id IN ( " + accountIDsCSL + " ))"
813  + " AND ( data_source_info.device_id IN (" + accountDeviceIDsCSL + "))) "
814  + (filterSQL.isEmpty() ? "" : " AND " + filterSQL)
815  + " GROUP BY data_source_info.device_id, "
816  + " accounts1.account_id, "
817  + " account_types1.type_name, "
818  + " account_types1.display_name, "
819  + " accounts2.account_id, "
820  + " account_types2.type_name, "
821  + " account_types2.display_name";
822  CaseDbConnection connection = db.getConnection();
824  Statement s = null;
825  ResultSet rs = null;
826 
827  Map<AccountPair, Long> results = new HashMap<AccountPair, Long>();
828 
829  try {
830  s = connection.createStatement();
831  rs = connection.executeQuery(s, queryString); //NON-NLS
832 
833  while (rs.next()) {
834  //make account 1
835  Account.Type type1 = new Account.Type(rs.getString("type_name1"), rs.getString("display_name1"));
836  AccountDeviceInstance adi1 = new AccountDeviceInstance(new Account(rs.getLong("account1_id"), type1,
837  rs.getString("account1_unique_identifier")),
838  rs.getString("device_id"));
839 
840  //make account 2
841  Account.Type type2 = new Account.Type(rs.getString("type_name2"), rs.getString("display_name2"));
842  AccountDeviceInstance adi2 = new AccountDeviceInstance(new Account(rs.getLong("account2_id"), type2,
843  rs.getString("account2_unique_identifier")),
844  rs.getString("device_id"));
845 
846  AccountPair relationshipKey = new AccountPair(adi1, adi2);
847  long count = rs.getLong("count");
848 
849  //merge counts for relationships that have the accounts flipped.
850  Long oldCount = results.get(relationshipKey);
851  if (oldCount != null) {
852  count += oldCount;
853  }
854  results.put(relationshipKey, count);
855  }
856  return results;
857  } catch (SQLException ex) {
858  throw new TskCoreException("Error getting relationships between accounts. " + ex.getMessage(), ex);
859  } finally {
860  closeResultSet(rs);
861  closeStatement(s);
862  connection.close();
864  }
865  }
866 
882  public long getRelationshipSourcesCount(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter) throws TskCoreException {
883 
884  long account_id = accountDeviceInstance.getAccount().getAccountID();
885 
886  // Get the list of Data source objects IDs correpsonding to this DeviceID.
887  String datasourceObjIdsCSV = StringUtils.buildCSVString(
888  db.getDataSourceObjIds(accountDeviceInstance.getDeviceId()));
889 
890  // set up applicable filters
891  Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
893  CommunicationsFilter.DateRangeFilter.class.getName()
894  ));
895  String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
896 
897  CaseDbConnection connection = db.getConnection();
899  Statement s = null;
900  ResultSet rs = null;
901 
902  try {
903  s = connection.createStatement();
904 
905  String innerQuery = " account_relationships AS relationships";
906  String limitStr = getMostRecentFilterLimitSQL(filter);
907 
908  if(!limitStr.isEmpty()) {
909  innerQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships";
910  }
911 
912  String queryStr
913  = "SELECT count(DISTINCT relationships.relationship_source_obj_id) as count "
914  + " FROM" + innerQuery
915  + " WHERE relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV + " )"
916  + " AND ( relationships.account1_id = " + account_id
917  + " OR relationships.account2_id = " + account_id + " )"
918  + (filterSQL.isEmpty() ? "" : " AND " + filterSQL);
919 
920  rs = connection.executeQuery(s, queryStr); //NON-NLS
921  rs.next();
922  return (rs.getLong("count"));
923  } catch (SQLException ex) {
924  throw new TskCoreException("Error getting relationships count for account device instance. " + ex.getMessage(), ex);
925  } finally {
926  closeResultSet(rs);
927  closeStatement(s);
928  connection.close();
930  }
931  }
932 
948  public Set<Content> getRelationshipSources(Set<AccountDeviceInstance> accountDeviceInstanceList, CommunicationsFilter filter) throws TskCoreException {
949 
950  if (accountDeviceInstanceList.isEmpty()) {
951  //log this?
952  return Collections.emptySet();
953  }
954 
955  Map<Long, Set<Long>> accountIdToDatasourceObjIdMap = new HashMap<Long, Set<Long>>();
956  for (AccountDeviceInstance accountDeviceInstance : accountDeviceInstanceList) {
957  long accountID = accountDeviceInstance.getAccount().getAccountID();
958  List<Long> dataSourceObjIds = db.getDataSourceObjIds(accountDeviceInstance.getDeviceId());
959 
960  if (accountIdToDatasourceObjIdMap.containsKey(accountID)) {
961  accountIdToDatasourceObjIdMap.get(accountID).addAll(dataSourceObjIds);
962  } else {
963  accountIdToDatasourceObjIdMap.put(accountID, new HashSet<Long>(dataSourceObjIds));
964  }
965  }
966 
967  List<String> adiSQLClauses = new ArrayList<String>();
968  for (Map.Entry<Long, Set<Long>> entry : accountIdToDatasourceObjIdMap.entrySet()) {
969  final Long accountID = entry.getKey();
970  String datasourceObjIdsCSV = StringUtils.buildCSVString(entry.getValue());
971 
972  adiSQLClauses.add(
973  "( ( relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV + " ) )"
974  + " AND ( relationships.account1_id = " + accountID
975  + " OR relationships.account2_id = " + accountID + " ) )"
976  );
977  }
978  String adiSQLClause = StringUtils.joinAsStrings(adiSQLClauses, " OR ");
979 
980  // set up applicable filters
981  Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
983  .getName(),
985  .getName()
986  ));
987  String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
988 
989  String limitQuery = " account_relationships AS relationships";
990  String limitStr = getMostRecentFilterLimitSQL(filter);
991  if(!limitStr.isEmpty()) {
992  limitQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships";
993  }
994 
995  CaseDbConnection connection = db.getConnection();
997  Statement s = null;
998  ResultSet rs = null;
999 
1000  try {
1001  s = connection.createStatement();
1002  String queryStr
1003  = "SELECT DISTINCT artifacts.artifact_id AS artifact_id,"
1004  + " artifacts.obj_id AS obj_id,"
1005  + " artifacts.artifact_obj_id AS artifact_obj_id,"
1006  + " artifacts.data_source_obj_id AS data_source_obj_id, "
1007  + " artifacts.artifact_type_id AS artifact_type_id, "
1008  + " artifacts.review_status_id AS review_status_id "
1009  + " FROM blackboard_artifacts as artifacts"
1010  + " JOIN " + limitQuery
1011  + " ON artifacts.artifact_obj_id = relationships.relationship_source_obj_id"
1012  // append sql to restrict search to specified account device instances
1013  + " WHERE (" + adiSQLClause + " )"
1014  // plus other filters
1015  + (filterSQL.isEmpty() ? "" : " AND (" + filterSQL + " )");
1016 
1017  rs = connection.executeQuery(s, queryStr); //NON-NLS
1018  Set<Content> relationshipSources = new HashSet<Content>();
1019  while (rs.next()) {
1020  BlackboardArtifact.Type bbartType = db.getArtifactType(rs.getInt("artifact_type_id"));
1021  relationshipSources.add(new BlackboardArtifact(db, rs.getLong("artifact_id"),
1022  rs.getLong("obj_id"), rs.getLong("artifact_obj_id"),
1023  rs.getLong("data_source_obj_id"), bbartType.getTypeID(),
1024  bbartType.getTypeName(), bbartType.getDisplayName(),
1025  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))));
1026  }
1027 
1028  return relationshipSources;
1029  } catch (SQLException ex) {
1030  throw new TskCoreException("Error getting relationships for account. " + ex.getMessage(), ex);
1031  } finally {
1032  closeResultSet(rs);
1033  closeStatement(s);
1034  connection.close();
1036  }
1037  }
1038 
1054  public List<AccountDeviceInstance> getRelatedAccountDeviceInstances(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter) throws TskCoreException {
1055  final List<Long> dataSourceObjIds
1056  = getSleuthkitCase().getDataSourceObjIds(accountDeviceInstance.getDeviceId());
1057 
1058  //set up applicable filters
1059  Set<String> applicableInnerQueryFilters = new HashSet<String>(Arrays.asList(
1060  CommunicationsFilter.DateRangeFilter.class.getName(),
1061  CommunicationsFilter.DeviceFilter.class.getName(),
1063  ));
1064 
1065  String innerQueryfilterSQL = getCommunicationsFilterSQL(filter, applicableInnerQueryFilters);
1066 
1067  String innerQueryTemplate
1068  = " SELECT %1$1s as account_id,"
1069  + " data_source_obj_id"
1070  + " FROM account_relationships as relationships"
1071  + " WHERE %2$1s = " + accountDeviceInstance.getAccount().getAccountID() + ""
1072  + " AND data_source_obj_id IN (" + StringUtils.buildCSVString(dataSourceObjIds) + ")"
1073  + (innerQueryfilterSQL.isEmpty() ? "" : " AND " + innerQueryfilterSQL);
1074 
1075  String innerQuery1 = String.format(innerQueryTemplate, "account1_id", "account2_id");
1076  String innerQuery2 = String.format(innerQueryTemplate, "account2_id", "account1_id");
1077 
1078  //this query groups by account_id and data_source_obj_id across both innerQueries
1079  String combinedInnerQuery
1080  = "SELECT account_id, data_source_obj_id "
1081  + " FROM ( " + innerQuery1 + " UNION " + innerQuery2 + " ) AS inner_union"
1082  + " GROUP BY account_id, data_source_obj_id";
1083 
1084  // set up applicable filters
1085  Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
1086  CommunicationsFilter.AccountTypeFilter.class.getName()
1087  ));
1088 
1089  String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
1090 
1091  String queryStr
1092  = //account info
1093  " accounts.account_id AS account_id,"
1094  + " accounts.account_unique_identifier AS account_unique_identifier,"
1095  //account type info
1096  + " account_types.type_name AS type_name,"
1097  //Account device instance info
1098  + " data_source_info.device_id AS device_id"
1099  + " FROM ( " + combinedInnerQuery + " ) AS account_device_instances"
1100  + " JOIN accounts AS accounts"
1101  + " ON accounts.account_id = account_device_instances.account_id"
1102  + " JOIN account_types AS account_types"
1103  + " ON accounts.account_type_id = account_types.account_type_id"
1104  + " JOIN data_source_info AS data_source_info"
1105  + " ON account_device_instances.data_source_obj_id = data_source_info.obj_id"
1106  + (filterSQL.isEmpty() ? "" : " WHERE " + filterSQL);
1107 
1108  switch (db.getDatabaseType()) {
1109  case POSTGRESQL:
1110  queryStr = "SELECT DISTINCT ON ( accounts.account_id, data_source_info.device_id) " + queryStr;
1111  break;
1112  case SQLITE:
1113  queryStr = "SELECT " + queryStr + " GROUP BY accounts.account_id, data_source_info.device_id";
1114  break;
1115  default:
1116  throw new TskCoreException("Unknown DB Type: " + db.getDatabaseType().name());
1117  }
1118 
1119  CaseDbConnection connection = db.getConnection();
1121  Statement s = null;
1122  ResultSet rs = null;
1123 
1124  try {
1125  s = connection.createStatement();
1126 
1127  rs = connection.executeQuery(s, queryStr); //NON-NLS
1128  ArrayList<AccountDeviceInstance> accountDeviceInstances = new ArrayList<AccountDeviceInstance>();
1129  while (rs.next()) {
1130  long account_id = rs.getLong("account_id");
1131  String deviceID = rs.getString("device_id");
1132  final String type_name = rs.getString("type_name");
1133  final String account_unique_identifier = rs.getString("account_unique_identifier");
1134 
1135  Account.Type accountType = typeNameToAccountTypeMap.get(type_name);
1136  Account account = new Account(account_id, accountType, account_unique_identifier);
1137  accountDeviceInstances.add(new AccountDeviceInstance(account, deviceID));
1138  }
1139 
1140  return accountDeviceInstances;
1141  } catch (SQLException ex) {
1142  throw new TskCoreException("Error getting account device instances. " + ex.getMessage(), ex);
1143  } finally {
1144  closeResultSet(rs);
1145  closeStatement(s);
1146  connection.close();
1148  }
1149  }
1150 
1167  public List<Content> getRelationshipSources(AccountDeviceInstance account1, AccountDeviceInstance account2, CommunicationsFilter filter) throws TskCoreException {
1168 
1169  //set up applicable filters
1170  Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
1171  CommunicationsFilter.DateRangeFilter.class.getName(),
1172  CommunicationsFilter.DeviceFilter.class.getName(),
1174  ));
1175 
1176  String limitQuery = " account_relationships AS relationships";
1177  String limitStr = getMostRecentFilterLimitSQL(filter);
1178  if(!limitStr.isEmpty()) {
1179  limitQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships";
1180  }
1181 
1182  String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
1183  final String queryString = "SELECT artifacts.artifact_id AS artifact_id,"
1184  + " artifacts.obj_id AS obj_id,"
1185  + " artifacts.artifact_obj_id AS artifact_obj_id,"
1186  + " artifacts.data_source_obj_id AS data_source_obj_id,"
1187  + " artifacts.artifact_type_id AS artifact_type_id,"
1188  + " artifacts.review_status_id AS review_status_id"
1189  + " FROM blackboard_artifacts AS artifacts"
1190  + " JOIN " + limitQuery
1191  + " ON artifacts.artifact_obj_id = relationships.relationship_source_obj_id"
1192  + " WHERE (( relationships.account1_id = " + account1.getAccount().getAccountID()
1193  + " AND relationships.account2_id = " + account2.getAccount().getAccountID()
1194  + " ) OR ( relationships.account2_id = " + account1.getAccount().getAccountID()
1195  + " AND relationships.account1_id =" + account2.getAccount().getAccountID() + " ))"
1196  + (filterSQL.isEmpty() ? "" : " AND " + filterSQL);
1197  CaseDbConnection connection = db.getConnection();
1199  Statement s = null;
1200  ResultSet rs = null;
1201  try {
1202  s = connection.createStatement();
1203  rs = connection.executeQuery(s, queryString); //NON-NLS
1204 
1205  ArrayList<Content> artifacts = new ArrayList<Content>();
1206  while (rs.next()) {
1207  BlackboardArtifact.Type bbartType = db.getArtifactType(rs.getInt("artifact_type_id"));
1208  artifacts.add(new BlackboardArtifact(db, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"),
1209  bbartType.getTypeID(), bbartType.getTypeName(), bbartType.getDisplayName(),
1210  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))));
1211  }
1212 
1213  return artifacts;
1214  } catch (SQLException ex) {
1215  throw new TskCoreException("Error getting relationships between accounts. " + ex.getMessage(), ex);
1216  } finally {
1217  closeResultSet(rs);
1218  closeStatement(s);
1219  connection.close();
1221  }
1222  }
1223 
1234  public List<AccountFileInstance> getAccountFileInstances(Account account) throws TskCoreException {
1235  List<AccountFileInstance> accountFileInstanceList = new ArrayList<>();
1236 
1237  List<BlackboardArtifact> artifactList = getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID, account.getTypeSpecificID());
1238 
1239  if(artifactList != null && !artifactList.isEmpty()) {
1240  for(BlackboardArtifact artifact : artifactList) {
1241  accountFileInstanceList.add(new AccountFileInstance(artifact, account));
1242  }
1243  }
1244 
1245  if(!accountFileInstanceList.isEmpty()) {
1246  return accountFileInstanceList;
1247  } else {
1248  return null;
1249  }
1250  }
1251 
1260  public List<Account.Type> getAccountTypesInUse() throws TskCoreException{
1261  CaseDbConnection connection = db.getConnection();
1263  Statement s = null;
1264  ResultSet rs = null;
1265  List<Account.Type> inUseAccounts = new ArrayList<>();
1266 
1267  try {
1268  String query = "SELECT DISTINCT accounts.account_type_id, type_name, display_name FROM accounts JOIN account_types ON accounts.account_type_id = account_types.account_type_id";
1269  s = connection.createStatement();
1270  rs = connection.executeQuery(s, query); //NON-NLS
1271  Account.Type accountType = null;
1272  while (rs.next()) {
1273  String accountTypeName = rs.getString("type_name");
1274  accountType = this.typeNameToAccountTypeMap.get(accountTypeName);
1275 
1276  if(accountType == null) {
1277  accountType = new Account.Type(accountTypeName, rs.getString("display_name"));
1278  this.accountTypeToTypeIdMap.put(accountType, rs.getInt("account_type_id"));
1279  }
1280 
1281  inUseAccounts.add(accountType);
1282  }
1283  return inUseAccounts;
1284  } catch (SQLException ex) {
1285  throw new TskCoreException("Error getting account type id", ex);
1286  } finally {
1287  closeResultSet(rs);
1288  closeStatement(s);
1289  connection.close();
1291  }
1292  }
1293 
1301  int getAccountTypeId(Account.Type accountType) {
1302  if (accountTypeToTypeIdMap.containsKey(accountType)) {
1303  return accountTypeToTypeIdMap.get(accountType);
1304  }
1305 
1306  return 0;
1307  }
1308 
1318  private String normalizeAccountID(Account.Type accountType, String accountUniqueID) {
1319  String normailzeAccountID = accountUniqueID;
1320 
1321  if (accountType.equals(Account.Type.PHONE)) {
1322  normailzeAccountID = normalizePhoneNum(accountUniqueID);
1323  } else if (accountType.equals(Account.Type.EMAIL)) {
1324  normailzeAccountID = normalizeEmailAddress(accountUniqueID);
1325  }
1326 
1327  return normailzeAccountID;
1328  }
1329 
1338  private String normalizePhoneNum(String phoneNum) {
1339  String normailzedPhoneNum = phoneNum.replaceAll("\\D", "");
1340 
1341  if (phoneNum.startsWith("+")) {
1342  normailzedPhoneNum = "+" + normailzedPhoneNum;
1343  }
1344 
1345  if(normailzedPhoneNum.isEmpty()) {
1346  normailzedPhoneNum = phoneNum;
1347  }
1348 
1349  return normailzedPhoneNum;
1350  }
1351 
1359  private String normalizeEmailAddress(String emailAddress) {
1360  String normailzedEmailAddr = emailAddress.toLowerCase();
1361 
1362  return normailzedEmailAddr;
1363  }
1364 
1377  private String getCommunicationsFilterSQL(CommunicationsFilter commFilter, Set<String> applicableFilters) {
1378  if (null == commFilter || commFilter.getAndFilters().isEmpty()) {
1379  return "";
1380  }
1381 
1382  String sqlStr = "";
1383  StringBuilder sqlSB = new StringBuilder();
1384  boolean first = true;
1385  for (CommunicationsFilter.SubFilter subFilter : commFilter.getAndFilters()) {
1386 
1387  // If the filter is applicable
1388  if (applicableFilters.contains(subFilter.getClass().getName())) {
1389  String subfilterSQL = subFilter.getSQL(this);
1390  if (!subfilterSQL.isEmpty()) {
1391  if (first) {
1392  first = false;
1393  } else {
1394  sqlSB.append(" AND ");
1395  }
1396  sqlSB.append("( ");
1397  sqlSB.append(subfilterSQL);
1398  sqlSB.append(" )");
1399  }
1400  }
1401  }
1402 
1403  if (!sqlSB.toString().isEmpty()) {
1404  sqlStr = "( " + sqlSB.toString() + " )";
1405  }
1406  return sqlStr;
1407  }
1408 
1416  private String getMostRecentFilterLimitSQL(CommunicationsFilter filter) {
1417  String limitStr = "";
1418 
1419  if (filter != null && !filter.getAndFilters().isEmpty()) {
1420 
1421  for (CommunicationsFilter.SubFilter subFilter : filter.getAndFilters()) {
1422  if(subFilter.getClass().getName().equals(CommunicationsFilter.MostRecentFilter.class.getName())) {
1423  limitStr = subFilter.getSQL(this);
1424  break;
1425  }
1426  }
1427  }
1428 
1429  return limitStr;
1430  }
1431 }
Set< Content > getRelationshipSources(Set< AccountDeviceInstance > accountDeviceInstanceList, CommunicationsFilter filter)
ArrayList< BlackboardArtifact > getBlackboardArtifacts(int artifactTypeID)
void addAttributes(Collection< BlackboardAttribute > attributes)
List< AccountDeviceInstance > getRelatedAccountDeviceInstances(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter)
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)
Map< AccountPair, Long > getRelationshipCountsPairwise(Set< AccountDeviceInstance > accounts, CommunicationsFilter filter)
BlackboardArtifact newBlackboardArtifact(int artifactTypeID, long obj_id)
static final List< Account.Type > PREDEFINED_ACCOUNT_TYPES
Definition: Account.java:70
BlackboardArtifact.Type getArtifactType(String artTypeName)
List< Content > getRelationshipSources(AccountDeviceInstance account1, AccountDeviceInstance account2, CommunicationsFilter filter)
long getRelationshipSourcesCount(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter)
List< AccountDeviceInstance > getAccountDeviceInstancesWithRelationships(CommunicationsFilter filter)
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)
List< AccountFileInstance > getAccountFileInstances(Account account)

Copyright © 2011-2018 Brian Carrier. (carrier -at- sleuthkit -dot- org)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.