Sleuth Kit Java Bindings (JNI)  4.10.0
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-2020 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;
38 import static org.sleuthkit.datamodel.SleuthkitCase.closeResultSet;
39 import static org.sleuthkit.datamodel.SleuthkitCase.closeStatement;
40 
45 public final class CommunicationsManager {
46 
47  private static final Logger LOGGER = Logger.getLogger(CommunicationsManager.class.getName());
48 
49  private final SleuthkitCase db;
50 
51  private final Map<Account.Type, Integer> accountTypeToTypeIdMap
52  = new ConcurrentHashMap<>();
53  private final Map<String, Account.Type> typeNameToAccountTypeMap
54  = new ConcurrentHashMap<>();
55 
56  // Artifact types that can represent a relationship between accounts.
57  private static final Set<Integer> RELATIONSHIP_ARTIFACT_TYPE_IDS = new HashSet<Integer>(Arrays.asList(
62  ));
63  private static final String RELATIONSHIP_ARTIFACT_TYPE_IDS_CSV_STR = StringUtils.buildCSVString(RELATIONSHIP_ARTIFACT_TYPE_IDS);
64 
74  this.db = skCase;
75  initAccountTypes();
76  }
77 
84  private void initAccountTypes() throws TskCoreException {
85  CaseDbConnection connection = db.getConnection();
87  Statement statement = null;
88  ResultSet resultSet = null;
89 
90  try {
91  statement = connection.createStatement();
92  // Read the table
93  int count = readAccountTypes();
94  if (0 == count) {
95  // Table is empty, populate it with predefined types
97  try {
98  statement.execute("INSERT INTO account_types (type_name, display_name) VALUES ( '" + type.getTypeName() + "', '" + type.getDisplayName() + "')"); //NON-NLS
99  } catch (SQLException ex) {
100  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM account_types WHERE type_name = '" + type.getTypeName() + "'"); //NON-NLS
101  resultSet.next();
102  if (resultSet.getLong("count") == 0) {
103  throw ex;
104  }
105  resultSet.close();
106  }
107 
108  ResultSet rs2 = connection.executeQuery(statement, "SELECT account_type_id FROM account_types WHERE type_name = '" + type.getTypeName() + "'"); //NON-NLS
109  rs2.next();
110  int typeID = rs2.getInt("account_type_id");
111  rs2.close();
112 
113  Account.Type accountType = new Account.Type(type.getTypeName(), type.getDisplayName());
114  this.accountTypeToTypeIdMap.put(accountType, typeID);
115  this.typeNameToAccountTypeMap.put(type.getTypeName(), accountType);
116  }
117  }
118  } catch (SQLException ex) {
119  LOGGER.log(Level.SEVERE, "Failed to add row to account_types", ex);
120  } finally {
121  closeResultSet(resultSet);
122  closeStatement(statement);
123  connection.close();
125  }
126  }
127 
136  private int readAccountTypes() throws TskCoreException {
137  CaseDbConnection connection = db.getConnection();
139  Statement statement = null;
140  ResultSet resultSet = null;
141  int count = 0;
142 
143  try {
144  statement = connection.createStatement();
145 
146  // If the account_types table is already populated, say when opening a case, then load it
147  resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM account_types"); //NON-NLS
148  resultSet.next();
149  if (resultSet.getLong("count") > 0) {
150 
151  resultSet.close();
152  resultSet = connection.executeQuery(statement, "SELECT * FROM account_types");
153  while (resultSet.next()) {
154  Account.Type accountType = new Account.Type(resultSet.getString("type_name"), resultSet.getString("display_name"));
155  this.accountTypeToTypeIdMap.put(accountType, resultSet.getInt("account_type_id"));
156  this.typeNameToAccountTypeMap.put(accountType.getTypeName(), accountType);
157  }
158  count = this.typeNameToAccountTypeMap.size();
159  }
160 
161  } catch (SQLException ex) {
162  throw new TskCoreException("Failed to read account_types", ex);
163  } finally {
164  closeResultSet(resultSet);
165  closeStatement(statement);
166  connection.close();
168  }
169 
170  return count;
171  }
172 
178  SleuthkitCase getSleuthkitCase() {
179  return this.db;
180  }
181 
194  // NOTE: Full name given for Type for doxygen linking
195  public org.sleuthkit.datamodel.Account.Type addAccountType(String accountTypeName, String displayName) throws TskCoreException {
196  Account.Type accountType = new Account.Type(accountTypeName, displayName);
197 
198  // check if already in map
199  if (this.accountTypeToTypeIdMap.containsKey(accountType)) {
200  return accountType;
201  }
202 
203  CaseDbConnection connection = db.getConnection();
205  Statement s = null;
206  ResultSet rs = null;
207  try {
208  connection.beginTransaction();
209  s = connection.createStatement();
210  rs = connection.executeQuery(s, "SELECT * FROM account_types WHERE type_name = '" + accountTypeName + "'"); //NON-NLS
211  if (!rs.next()) {
212  rs.close();
213 
214  s.execute("INSERT INTO account_types (type_name, display_name) VALUES ( '" + accountTypeName + "', '" + displayName + "')"); //NON-NLS
215 
216  // Read back the typeID
217  rs = connection.executeQuery(s, "SELECT * FROM account_types WHERE type_name = '" + accountTypeName + "'"); //NON-NLS
218  rs.next();
219 
220  int typeID = rs.getInt("account_type_id");
221  accountType = new Account.Type(rs.getString("type_name"), rs.getString("display_name"));
222 
223  this.accountTypeToTypeIdMap.put(accountType, typeID);
224  this.typeNameToAccountTypeMap.put(accountTypeName, accountType);
225 
226  connection.commitTransaction();
227 
228  return accountType;
229  } else {
230  int typeID = rs.getInt("account_type_id");
231 
232  accountType = new Account.Type(rs.getString("type_name"), rs.getString("display_name"));
233  this.accountTypeToTypeIdMap.put(accountType, typeID);
234 
235  return accountType;
236  }
237  } catch (SQLException ex) {
238  connection.rollbackTransaction();
239  throw new TskCoreException("Error adding account type", ex);
240  } finally {
241  closeResultSet(rs);
242  closeStatement(s);
243  connection.close();
245  }
246  }
247 
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, InvalidAccountIDException {
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 
302  // NOTE: Full name given for Type for doxygen linking
303  public Account getAccount(org.sleuthkit.datamodel.Account.Type accountType, String accountUniqueID) throws TskCoreException, InvalidAccountIDException {
304  Account account = null;
305  CaseDbConnection connection = db.getConnection();
307  Statement s = null;
308  ResultSet rs = null;
309  try {
310  s = connection.createStatement();
311  rs = connection.executeQuery(s, "SELECT * FROM accounts WHERE account_type_id = " + getAccountTypeId(accountType)
312  + " AND account_unique_identifier = '" + normalizeAccountID(accountType, accountUniqueID) + "'"); //NON-NLS
313 
314  if (rs.next()) {
315  account = new Account(rs.getInt("account_id"), accountType,
316  rs.getString("account_unique_identifier"));
317  }
318  } catch (SQLException ex) {
319  throw new TskCoreException("Error getting account type id", ex);
320  } finally {
321  closeResultSet(rs);
322  closeStatement(s);
323  connection.close();
325  }
326 
327  return account;
328  }
329 
352  // NOTE: Full name given for Type for doxygen linking
353  public void addRelationships(AccountFileInstance sender, List<AccountFileInstance> recipients,
354  BlackboardArtifact sourceArtifact, org.sleuthkit.datamodel.Relationship.Type relationshipType, long dateTime) throws TskCoreException, TskDataException {
355 
356  if (relationshipType.isCreatableFrom(sourceArtifact) == false) {
357  throw new TskDataException("Can not make a " + relationshipType.getDisplayName()
358  + " relationship from a" + sourceArtifact.getDisplayName());
359  }
360 
361  /*
362  * Enforce that all accounts and the relationship between them are from
363  * the same 'source'. This is required for the queries to work
364  * correctly.
365  */
366  // Currently we do not save the direction of communication
367  List<Long> accountIDs = new ArrayList<Long>();
368 
369  if (null != sender) {
370  accountIDs.add(sender.getAccount().getAccountID());
371  if (sender.getDataSourceObjectID() != sourceArtifact.getDataSourceObjectID()) {
372  throw new TskDataException("Sender and relationship are from different data sources :"
373  + "Sender source ID" + sender.getDataSourceObjectID() + " != relationship source ID" + sourceArtifact.getDataSourceObjectID());
374  }
375  }
376 
377  for (AccountFileInstance recipient : recipients) {
378  accountIDs.add(recipient.getAccount().getAccountID());
379  if (recipient.getDataSourceObjectID() != sourceArtifact.getDataSourceObjectID()) {
380  throw new TskDataException("Recipient and relationship are from different data sources :"
381  + "Recipient source ID" + recipient.getDataSourceObjectID() + " != relationship source ID" + sourceArtifact.getDataSourceObjectID());
382  }
383  }
384 
385  for (int i = 0; i < accountIDs.size(); i++) {
386  for (int j = i + 1; j < accountIDs.size(); j++) {
387  try {
388  addAccountsRelationship(accountIDs.get(i), accountIDs.get(j),
389  sourceArtifact, relationshipType, dateTime);
390  } catch (TskCoreException ex) {
391  // @@@ This should probably not be caught and instead we stop adding
392  LOGGER.log(Level.WARNING, "Error adding relationship", ex); //NON-NLS
393  }
394  }
395  }
396  }
397 
412  private Account getOrCreateAccount(Account.Type accountType, String accountUniqueID) throws TskCoreException, InvalidAccountIDException {
413  Account account = getAccount(accountType, accountUniqueID);
414  if (null == account) {
415  String query = " INTO accounts (account_type_id, account_unique_identifier) "
416  + "VALUES ( " + getAccountTypeId(accountType) + ", '"
417  + normalizeAccountID(accountType, accountUniqueID) + "'" + ")";
418  switch (db.getDatabaseType()) {
419  case POSTGRESQL:
420  query = "INSERT " + query + " ON CONFLICT DO NOTHING"; //NON-NLS
421  break;
422  case SQLITE:
423  query = "INSERT OR IGNORE " + query;
424  break;
425  default:
426  throw new TskCoreException("Unknown DB Type: " + db.getDatabaseType().name());
427  }
428 
429  CaseDbConnection connection = db.getConnection();
431  Statement s = null;
432  ResultSet rs = null;
433  try {
434  connection.beginTransaction();
435  s = connection.createStatement();
436 
437  s.execute(query);
438 
439  connection.commitTransaction();
440  account = getAccount(accountType, accountUniqueID);
441  } catch (SQLException ex) {
442  connection.rollbackTransaction();
443  throw new TskCoreException("Error adding an account", ex);
444  } finally {
445  closeResultSet(rs);
446  closeStatement(s);
447  connection.close();
449  }
450  }
451 
452  return account;
453  }
454 
472  private BlackboardArtifact getOrCreateAccountFileInstanceArtifact(Account.Type accountType, String accountUniqueID, String moduleName, Content sourceFile) throws TskCoreException {
473  BlackboardArtifact accountArtifact = getAccountFileInstanceArtifact(accountType, accountUniqueID, sourceFile);
474  if (accountArtifact == null) {
475  accountArtifact = db.newBlackboardArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT, sourceFile.getId());
476  Collection<BlackboardAttribute> attributes = new ArrayList<>();
477  attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, moduleName, accountType.getTypeName()));
478  attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID, moduleName, accountUniqueID));
479  accountArtifact.addAttributes(attributes);
480  try {
481  db.getBlackboard().postArtifact(accountArtifact, moduleName);
482  } catch (BlackboardException ex) {
483  LOGGER.log(Level.SEVERE, String.format("Error posting new account artifact to the blackboard (object ID = %d)", accountArtifact.getId()), ex);
484  }
485  }
486  return accountArtifact;
487  }
488 
502  private BlackboardArtifact getAccountFileInstanceArtifact(Account.Type accountType, String accountUniqueID, Content sourceFile) throws TskCoreException {
503  BlackboardArtifact accountArtifact = null;
504  CaseDbConnection connection = db.getConnection();
506  Statement s = null;
507  ResultSet rs = null;
508 
509  try {
510  s = connection.createStatement();
511  String queryStr = "SELECT artifacts.artifact_id AS artifact_id,"
512  + " artifacts.obj_id AS obj_id,"
513  + " artifacts.artifact_obj_id AS artifact_obj_id,"
514  + " artifacts.data_source_obj_id AS data_source_obj_id,"
515  + " artifacts.artifact_type_id AS artifact_type_id,"
516  + " artifacts.review_status_id AS review_status_id"
517  + " FROM blackboard_artifacts AS artifacts"
518  + " JOIN blackboard_attributes AS attr_account_type"
519  + " ON artifacts.artifact_id = attr_account_type.artifact_id"
520  + " JOIN blackboard_attributes AS attr_account_id"
521  + " ON artifacts.artifact_id = attr_account_id.artifact_id"
522  + " AND attr_account_id.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID.getTypeID()
523  + " AND attr_account_id.value_text = '" + accountUniqueID + "'"
524  + " WHERE artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()
525  + " AND attr_account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()
526  + " AND attr_account_type.value_text = '" + accountType.getTypeName() + "'"
527  + " AND artifacts.obj_id = " + sourceFile.getId(); //NON-NLS
528 
529  rs = connection.executeQuery(s, queryStr); //NON-NLS
530  if (rs.next()) {
531  BlackboardArtifact.Type bbartType = db.getArtifactType(rs.getInt("artifact_type_id"));
532 
533  accountArtifact = new BlackboardArtifact(db, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"),
534  bbartType.getTypeID(), bbartType.getTypeName(), bbartType.getDisplayName(),
535  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")));
536  }
537  } catch (SQLException ex) {
538  throw new TskCoreException("Error getting account", ex);
539  } finally {
540  closeResultSet(rs);
541  closeStatement(s);
542  connection.close();
544  }
545 
546  return accountArtifact;
547  }
548 
558  // NOTE: Full name given for Type for doxygen linking
559  public org.sleuthkit.datamodel.Account.Type getAccountType(String accountTypeName) throws TskCoreException {
560  if (this.typeNameToAccountTypeMap.containsKey(accountTypeName)) {
561  return this.typeNameToAccountTypeMap.get(accountTypeName);
562  }
563 
564  CaseDbConnection connection = db.getConnection();
566  Statement s = null;
567  ResultSet rs = null;
568 
569  try {
570  s = connection.createStatement();
571  rs = connection.executeQuery(s, "SELECT account_type_id, type_name, display_name FROM account_types WHERE type_name = '" + accountTypeName + "'"); //NON-NLS
572  Account.Type accountType = null;
573  if (rs.next()) {
574  accountType = new Account.Type(accountTypeName, rs.getString("display_name"));
575  this.accountTypeToTypeIdMap.put(accountType, rs.getInt("account_type_id"));
576  this.typeNameToAccountTypeMap.put(accountTypeName, accountType);
577  }
578  return accountType;
579  } catch (SQLException ex) {
580  throw new TskCoreException("Error getting account type id", ex);
581  } finally {
582  closeResultSet(rs);
583  closeStatement(s);
584  connection.close();
586  }
587  }
588 
602  private void addAccountsRelationship(long account1_id, long account2_id, BlackboardArtifact relationshipaArtifact, Relationship.Type relationshipType, long dateTime) throws TskCoreException {
603  CaseDbConnection connection = db.getConnection();
605  Statement s = null;
606  ResultSet rs = null;
607 
608  try {
609  String dateTimeValStr = (dateTime > 0) ? Long.toString(dateTime) : "NULL";
610 
611  connection.beginTransaction();
612  s = connection.createStatement();
613  String query = "INTO account_relationships (account1_id, account2_id, relationship_source_obj_id, date_time, relationship_type, data_source_obj_id ) "
614  + "VALUES ( " + account1_id + ", " + account2_id + ", " + relationshipaArtifact.getId() + ", " + dateTimeValStr + ", " + relationshipType.getTypeID() + ", " + relationshipaArtifact.getDataSourceObjectID() + ")";
615  switch (db.getDatabaseType()) {
616  case POSTGRESQL:
617  query = "INSERT " + query + " ON CONFLICT DO NOTHING";
618  break;
619  case SQLITE:
620  query = "INSERT OR IGNORE " + query;
621  break;
622  default:
623  throw new TskCoreException("Unknown DB Type: " + db.getDatabaseType().name());
624  }
625  s.execute(query); //NON-NLS
626  connection.commitTransaction();
627  } catch (SQLException ex) {
628  connection.rollbackTransaction();
629  throw new TskCoreException("Error adding accounts relationship", ex);
630  } finally {
631  closeResultSet(rs);
632  closeStatement(s);
633  connection.close();
635  }
636  }
637 
652  public List<AccountDeviceInstance> getAccountDeviceInstancesWithRelationships(CommunicationsFilter filter) throws TskCoreException {
653  CaseDbConnection connection = db.getConnection();
655  Statement s = null;
656  ResultSet rs = null;
657 
658  try {
659  s = connection.createStatement();
660 
661  //set up applicable filters
662  Set<String> applicableInnerQueryFilters = new HashSet<String>(Arrays.asList(
663  CommunicationsFilter.DateRangeFilter.class.getName(),
664  CommunicationsFilter.DeviceFilter.class.getName(),
666  ));
667  String relationshipFilterSQL = getCommunicationsFilterSQL(filter, applicableInnerQueryFilters);
668 
669  String relationshipLimitSQL = getMostRecentFilterLimitSQL(filter);
670 
671  String relTblfilterQuery
672  = "SELECT * "
673  + "FROM account_relationships as relationships"
674  + (relationshipFilterSQL.isEmpty() ? "" : " WHERE " + relationshipFilterSQL)
675  + (relationshipLimitSQL.isEmpty() ? "" : relationshipLimitSQL);
676 
677  String uniqueAccountQueryTemplate
678  = " SELECT %1$1s as account_id,"
679  + " data_source_obj_id"
680  + " FROM ( " + relTblfilterQuery + ")AS %2$s";
681 
682  String relationshipTableFilterQuery1 = String.format(uniqueAccountQueryTemplate, "account1_id", "union_query_1");
683  String relationshipTableFilterQuery2 = String.format(uniqueAccountQueryTemplate, "account2_id", "union_query_2");
684 
685  //this query groups by account_id and data_source_obj_id across both innerQueries
686  String uniqueAccountQuery
687  = "SELECT DISTINCT account_id, data_source_obj_id"
688  + " FROM ( " + relationshipTableFilterQuery1 + " UNION " + relationshipTableFilterQuery2 + " ) AS inner_union"
689  + " GROUP BY account_id, data_source_obj_id";
690 
691  // set up applicable filters
692  Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
694  ));
695 
696  String accountTypeFilterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
697 
698  String queryStr
699  = //account info
700  " accounts.account_id AS account_id,"
701  + " accounts.account_unique_identifier AS account_unique_identifier,"
702  //account type info
703  + " account_types.type_name AS type_name,"
704  //Account device instance info
705  + " data_source_info.device_id AS device_id"
706  + " FROM ( " + uniqueAccountQuery + " ) AS account_device_instances"
707  + " JOIN accounts AS accounts"
708  + " ON accounts.account_id = account_device_instances.account_id"
709  + " JOIN account_types AS account_types"
710  + " ON accounts.account_type_id = account_types.account_type_id"
711  + " JOIN data_source_info AS data_source_info"
712  + " ON account_device_instances.data_source_obj_id = data_source_info.obj_id"
713  + (accountTypeFilterSQL.isEmpty() ? "" : " WHERE " + accountTypeFilterSQL);
714 
715  switch (db.getDatabaseType()) {
716  case POSTGRESQL:
717  queryStr = "SELECT DISTINCT ON ( accounts.account_id, data_source_info.device_id) " + queryStr;
718  break;
719  case SQLITE:
720  queryStr = "SELECT " + queryStr + " GROUP BY accounts.account_id, data_source_info.device_id";
721  break;
722  default:
723  throw new TskCoreException("Unknown DB Type: " + db.getDatabaseType().name());
724  }
725 
726  rs = connection.executeQuery(s, queryStr); //NON-NLS
727  ArrayList<AccountDeviceInstance> accountDeviceInstances = new ArrayList<AccountDeviceInstance>();
728  while (rs.next()) {
729  long account_id = rs.getLong("account_id");
730  String deviceID = rs.getString("device_id");
731  final String type_name = rs.getString("type_name");
732  final String account_unique_identifier = rs.getString("account_unique_identifier");
733 
734  Account.Type accountType = typeNameToAccountTypeMap.get(type_name);
735  Account account = new Account(account_id, accountType, account_unique_identifier);
736  accountDeviceInstances.add(new AccountDeviceInstance(account, deviceID));
737  }
738 
739  return accountDeviceInstances;
740  } catch (SQLException ex) {
741  throw new TskCoreException("Error getting account device instances. " + ex.getMessage(), ex);
742  } finally {
743  closeResultSet(rs);
744  closeStatement(s);
745  connection.close();
747  }
748  }
749 
769  public Map<AccountPair, Long> getRelationshipCountsPairwise(Set<AccountDeviceInstance> accounts, CommunicationsFilter filter) throws TskCoreException {
770 
771  Set<Long> accountIDs = new HashSet<Long>();
772  Set<String> accountDeviceIDs = new HashSet<String>();
773  for (AccountDeviceInstance adi : accounts) {
774  accountIDs.add(adi.getAccount().getAccountID());
775  accountDeviceIDs.add("'" + adi.getDeviceId() + "'");
776  }
777  //set up applicable filters
778  Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
779  CommunicationsFilter.DateRangeFilter.class.getName(),
780  CommunicationsFilter.DeviceFilter.class.getName(),
782  ));
783 
784  String accountIDsCSL = StringUtils.buildCSVString(accountIDs);
785  String accountDeviceIDsCSL = StringUtils.buildCSVString(accountDeviceIDs);
786  String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
787 
788  final String queryString
789  = " SELECT count(DISTINCT relationships.relationship_source_obj_id) AS count," //realtionship count
790  + " data_source_info.device_id AS device_id,"
791  //account 1 info
792  + " accounts1.account_id AS account1_id,"
793  + " accounts1.account_unique_identifier AS account1_unique_identifier,"
794  + " account_types1.type_name AS type_name1,"
795  + " account_types1.display_name AS display_name1,"
796  //account 2 info
797  + " accounts2.account_id AS account2_id,"
798  + " accounts2.account_unique_identifier AS account2_unique_identifier,"
799  + " account_types2.type_name AS type_name2,"
800  + " account_types2.display_name AS display_name2"
801  + " FROM account_relationships AS relationships"
802  + " JOIN data_source_info AS data_source_info"
803  + " ON relationships.data_source_obj_id = data_source_info.obj_id "
804  //account1 aliases
805  + " JOIN accounts AS accounts1 "
806  + " ON accounts1.account_id = relationships.account1_id"
807  + " JOIN account_types AS account_types1"
808  + " ON accounts1.account_type_id = account_types1.account_type_id"
809  //account2 aliases
810  + " JOIN accounts AS accounts2 "
811  + " ON accounts2.account_id = relationships.account2_id"
812  + " JOIN account_types AS account_types2"
813  + " ON accounts2.account_type_id = account_types2.account_type_id"
814  + " WHERE (( relationships.account1_id IN (" + accountIDsCSL + ")) "
815  + " AND ( relationships.account2_id IN ( " + accountIDsCSL + " ))"
816  + " AND ( data_source_info.device_id IN (" + accountDeviceIDsCSL + "))) "
817  + (filterSQL.isEmpty() ? "" : " AND " + filterSQL)
818  + " GROUP BY data_source_info.device_id, "
819  + " accounts1.account_id, "
820  + " account_types1.type_name, "
821  + " account_types1.display_name, "
822  + " accounts2.account_id, "
823  + " account_types2.type_name, "
824  + " account_types2.display_name";
825  CaseDbConnection connection = db.getConnection();
827  Statement s = null;
828  ResultSet rs = null;
829 
830  Map<AccountPair, Long> results = new HashMap<AccountPair, Long>();
831 
832  try {
833  s = connection.createStatement();
834  rs = connection.executeQuery(s, queryString); //NON-NLS
835 
836  while (rs.next()) {
837  //make account 1
838  Account.Type type1 = new Account.Type(rs.getString("type_name1"), rs.getString("display_name1"));
839  AccountDeviceInstance adi1 = new AccountDeviceInstance(new Account(rs.getLong("account1_id"), type1,
840  rs.getString("account1_unique_identifier")),
841  rs.getString("device_id"));
842 
843  //make account 2
844  Account.Type type2 = new Account.Type(rs.getString("type_name2"), rs.getString("display_name2"));
845  AccountDeviceInstance adi2 = new AccountDeviceInstance(new Account(rs.getLong("account2_id"), type2,
846  rs.getString("account2_unique_identifier")),
847  rs.getString("device_id"));
848 
849  AccountPair relationshipKey = new AccountPair(adi1, adi2);
850  long count = rs.getLong("count");
851 
852  //merge counts for relationships that have the accounts flipped.
853  Long oldCount = results.get(relationshipKey);
854  if (oldCount != null) {
855  count += oldCount;
856  }
857  results.put(relationshipKey, count);
858  }
859  return results;
860  } catch (SQLException ex) {
861  throw new TskCoreException("Error getting relationships between accounts. " + ex.getMessage(), ex);
862  } finally {
863  closeResultSet(rs);
864  closeStatement(s);
865  connection.close();
867  }
868  }
869 
885  public long getRelationshipSourcesCount(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter) throws TskCoreException {
886 
887  long account_id = accountDeviceInstance.getAccount().getAccountID();
888 
889  // Get the list of Data source objects IDs correpsonding to this DeviceID.
890  String datasourceObjIdsCSV = StringUtils.buildCSVString(
891  db.getDataSourceObjIds(accountDeviceInstance.getDeviceId()));
892 
893  // set up applicable filters
894  Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
896  CommunicationsFilter.DateRangeFilter.class.getName()
897  ));
898  String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
899 
900  CaseDbConnection connection = db.getConnection();
902  Statement s = null;
903  ResultSet rs = null;
904 
905  try {
906  s = connection.createStatement();
907 
908  String innerQuery = " account_relationships AS relationships";
909  String limitStr = getMostRecentFilterLimitSQL(filter);
910 
911  if (!limitStr.isEmpty()) {
912  innerQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships";
913  }
914 
915  String queryStr
916  = "SELECT count(DISTINCT relationships.relationship_source_obj_id) as count "
917  + " FROM" + innerQuery
918  + " WHERE relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV + " )"
919  + " AND ( relationships.account1_id = " + account_id
920  + " OR relationships.account2_id = " + account_id + " )"
921  + (filterSQL.isEmpty() ? "" : " AND " + filterSQL);
922 
923  rs = connection.executeQuery(s, queryStr); //NON-NLS
924  rs.next();
925  return (rs.getLong("count"));
926  } catch (SQLException ex) {
927  throw new TskCoreException("Error getting relationships count for account device instance. " + ex.getMessage(), ex);
928  } finally {
929  closeResultSet(rs);
930  closeStatement(s);
931  connection.close();
933  }
934  }
935 
952  public Set<Content> getRelationshipSources(Set<AccountDeviceInstance> accountDeviceInstanceList, CommunicationsFilter filter) throws TskCoreException {
953 
954  if (accountDeviceInstanceList.isEmpty()) {
955  //log this?
956  return Collections.emptySet();
957  }
958 
959  Map<Long, Set<Long>> accountIdToDatasourceObjIdMap = new HashMap<Long, Set<Long>>();
960  for (AccountDeviceInstance accountDeviceInstance : accountDeviceInstanceList) {
961  long accountID = accountDeviceInstance.getAccount().getAccountID();
962  List<Long> dataSourceObjIds = db.getDataSourceObjIds(accountDeviceInstance.getDeviceId());
963 
964  if (accountIdToDatasourceObjIdMap.containsKey(accountID)) {
965  accountIdToDatasourceObjIdMap.get(accountID).addAll(dataSourceObjIds);
966  } else {
967  accountIdToDatasourceObjIdMap.put(accountID, new HashSet<Long>(dataSourceObjIds));
968  }
969  }
970 
971  List<String> adiSQLClauses = new ArrayList<String>();
972  for (Map.Entry<Long, Set<Long>> entry : accountIdToDatasourceObjIdMap.entrySet()) {
973  final Long accountID = entry.getKey();
974  String datasourceObjIdsCSV = StringUtils.buildCSVString(entry.getValue());
975 
976  adiSQLClauses.add(
977  "( ( relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV + " ) )"
978  + " AND ( relationships.account1_id = " + accountID
979  + " OR relationships.account2_id = " + accountID + " ) )"
980  );
981  }
982  String adiSQLClause = StringUtils.joinAsStrings(adiSQLClauses, " OR ");
983 
984  // set up applicable filters
985  Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
987  .getName(),
989  .getName()
990  ));
991  String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
992 
993  String limitQuery = " account_relationships AS relationships";
994  String limitStr = getMostRecentFilterLimitSQL(filter);
995  if (!limitStr.isEmpty()) {
996  limitQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships";
997  }
998 
999  CaseDbConnection connection = db.getConnection();
1001  Statement s = null;
1002  ResultSet rs = null;
1003 
1004  try {
1005  s = connection.createStatement();
1006  String queryStr
1007  = "SELECT DISTINCT artifacts.artifact_id AS artifact_id,"
1008  + " artifacts.obj_id AS obj_id,"
1009  + " artifacts.artifact_obj_id AS artifact_obj_id,"
1010  + " artifacts.data_source_obj_id AS data_source_obj_id, "
1011  + " artifacts.artifact_type_id AS artifact_type_id, "
1012  + " artifacts.review_status_id AS review_status_id "
1013  + " FROM blackboard_artifacts as artifacts"
1014  + " JOIN " + limitQuery
1015  + " ON artifacts.artifact_obj_id = relationships.relationship_source_obj_id"
1016  // append sql to restrict search to specified account device instances
1017  + " WHERE (" + adiSQLClause + " )"
1018  // plus other filters
1019  + (filterSQL.isEmpty() ? "" : " AND (" + filterSQL + " )");
1020 
1021  rs = connection.executeQuery(s, queryStr); //NON-NLS
1022  Set<Content> relationshipSources = new HashSet<Content>();
1023  while (rs.next()) {
1024  BlackboardArtifact.Type bbartType = db.getArtifactType(rs.getInt("artifact_type_id"));
1025  relationshipSources.add(new BlackboardArtifact(db, rs.getLong("artifact_id"),
1026  rs.getLong("obj_id"), rs.getLong("artifact_obj_id"),
1027  rs.getLong("data_source_obj_id"), bbartType.getTypeID(),
1028  bbartType.getTypeName(), bbartType.getDisplayName(),
1029  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))));
1030  }
1031 
1032  return relationshipSources;
1033  } catch (SQLException ex) {
1034  throw new TskCoreException("Error getting relationships for account. " + ex.getMessage(), ex);
1035  } finally {
1036  closeResultSet(rs);
1037  closeStatement(s);
1038  connection.close();
1040  }
1041  }
1042 
1058  public List<AccountDeviceInstance> getRelatedAccountDeviceInstances(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter) throws TskCoreException {
1059  final List<Long> dataSourceObjIds
1060  = getSleuthkitCase().getDataSourceObjIds(accountDeviceInstance.getDeviceId());
1061 
1062  //set up applicable filters
1063  Set<String> applicableInnerQueryFilters = new HashSet<String>(Arrays.asList(
1064  CommunicationsFilter.DateRangeFilter.class.getName(),
1065  CommunicationsFilter.DeviceFilter.class.getName(),
1067  ));
1068 
1069  String innerQueryfilterSQL = getCommunicationsFilterSQL(filter, applicableInnerQueryFilters);
1070 
1071  String innerQueryTemplate
1072  = " SELECT %1$1s as account_id,"
1073  + " data_source_obj_id"
1074  + " FROM account_relationships as relationships"
1075  + " WHERE %2$1s = " + accountDeviceInstance.getAccount().getAccountID() + ""
1076  + " AND data_source_obj_id IN (" + StringUtils.buildCSVString(dataSourceObjIds) + ")"
1077  + (innerQueryfilterSQL.isEmpty() ? "" : " AND " + innerQueryfilterSQL);
1078 
1079  String innerQuery1 = String.format(innerQueryTemplate, "account1_id", "account2_id");
1080  String innerQuery2 = String.format(innerQueryTemplate, "account2_id", "account1_id");
1081 
1082  //this query groups by account_id and data_source_obj_id across both innerQueries
1083  String combinedInnerQuery
1084  = "SELECT account_id, data_source_obj_id "
1085  + " FROM ( " + innerQuery1 + " UNION " + innerQuery2 + " ) AS inner_union"
1086  + " GROUP BY account_id, data_source_obj_id";
1087 
1088  // set up applicable filters
1089  Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
1090  CommunicationsFilter.AccountTypeFilter.class.getName()
1091  ));
1092 
1093  String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
1094 
1095  String queryStr
1096  = //account info
1097  " accounts.account_id AS account_id,"
1098  + " accounts.account_unique_identifier AS account_unique_identifier,"
1099  //account type info
1100  + " account_types.type_name AS type_name,"
1101  //Account device instance info
1102  + " data_source_info.device_id AS device_id"
1103  + " FROM ( " + combinedInnerQuery + " ) AS account_device_instances"
1104  + " JOIN accounts AS accounts"
1105  + " ON accounts.account_id = account_device_instances.account_id"
1106  + " JOIN account_types AS account_types"
1107  + " ON accounts.account_type_id = account_types.account_type_id"
1108  + " JOIN data_source_info AS data_source_info"
1109  + " ON account_device_instances.data_source_obj_id = data_source_info.obj_id"
1110  + (filterSQL.isEmpty() ? "" : " WHERE " + filterSQL);
1111 
1112  switch (db.getDatabaseType()) {
1113  case POSTGRESQL:
1114  queryStr = "SELECT DISTINCT ON ( accounts.account_id, data_source_info.device_id) " + queryStr;
1115  break;
1116  case SQLITE:
1117  queryStr = "SELECT " + queryStr + " GROUP BY accounts.account_id, data_source_info.device_id";
1118  break;
1119  default:
1120  throw new TskCoreException("Unknown DB Type: " + db.getDatabaseType().name());
1121  }
1122 
1123  CaseDbConnection connection = db.getConnection();
1125  Statement s = null;
1126  ResultSet rs = null;
1127 
1128  try {
1129  s = connection.createStatement();
1130 
1131  rs = connection.executeQuery(s, queryStr); //NON-NLS
1132  ArrayList<AccountDeviceInstance> accountDeviceInstances = new ArrayList<AccountDeviceInstance>();
1133  while (rs.next()) {
1134  long account_id = rs.getLong("account_id");
1135  String deviceID = rs.getString("device_id");
1136  final String type_name = rs.getString("type_name");
1137  final String account_unique_identifier = rs.getString("account_unique_identifier");
1138 
1139  Account.Type accountType = typeNameToAccountTypeMap.get(type_name);
1140  Account account = new Account(account_id, accountType, account_unique_identifier);
1141  accountDeviceInstances.add(new AccountDeviceInstance(account, deviceID));
1142  }
1143 
1144  return accountDeviceInstances;
1145  } catch (SQLException ex) {
1146  throw new TskCoreException("Error getting account device instances. " + ex.getMessage(), ex);
1147  } finally {
1148  closeResultSet(rs);
1149  closeStatement(s);
1150  connection.close();
1152  }
1153  }
1154 
1171  public List<Content> getRelationshipSources(AccountDeviceInstance account1, AccountDeviceInstance account2, CommunicationsFilter filter) throws TskCoreException {
1172 
1173  //set up applicable filters
1174  Set<String> applicableFilters = new HashSet<String>(Arrays.asList(
1175  CommunicationsFilter.DateRangeFilter.class.getName(),
1176  CommunicationsFilter.DeviceFilter.class.getName(),
1178  ));
1179 
1180  String limitQuery = " account_relationships AS relationships";
1181  String limitStr = getMostRecentFilterLimitSQL(filter);
1182  if (!limitStr.isEmpty()) {
1183  limitQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships";
1184  }
1185 
1186  String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters);
1187  final String queryString = "SELECT artifacts.artifact_id AS artifact_id,"
1188  + " artifacts.obj_id AS obj_id,"
1189  + " artifacts.artifact_obj_id AS artifact_obj_id,"
1190  + " artifacts.data_source_obj_id AS data_source_obj_id,"
1191  + " artifacts.artifact_type_id AS artifact_type_id,"
1192  + " artifacts.review_status_id AS review_status_id"
1193  + " FROM blackboard_artifacts AS artifacts"
1194  + " JOIN " + limitQuery
1195  + " ON artifacts.artifact_obj_id = relationships.relationship_source_obj_id"
1196  + " WHERE (( relationships.account1_id = " + account1.getAccount().getAccountID()
1197  + " AND relationships.account2_id = " + account2.getAccount().getAccountID()
1198  + " ) OR ( relationships.account2_id = " + account1.getAccount().getAccountID()
1199  + " AND relationships.account1_id =" + account2.getAccount().getAccountID() + " ))"
1200  + (filterSQL.isEmpty() ? "" : " AND " + filterSQL);
1201  CaseDbConnection connection = db.getConnection();
1203  Statement s = null;
1204  ResultSet rs = null;
1205  try {
1206  s = connection.createStatement();
1207  rs = connection.executeQuery(s, queryString); //NON-NLS
1208 
1209  ArrayList<Content> artifacts = new ArrayList<Content>();
1210  while (rs.next()) {
1211  BlackboardArtifact.Type bbartType = db.getArtifactType(rs.getInt("artifact_type_id"));
1212  artifacts.add(new BlackboardArtifact(db, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"),
1213  bbartType.getTypeID(), bbartType.getTypeName(), bbartType.getDisplayName(),
1214  BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))));
1215  }
1216 
1217  return artifacts;
1218  } catch (SQLException ex) {
1219  throw new TskCoreException("Error getting relationships between accounts. " + ex.getMessage(), ex);
1220  } finally {
1221  closeResultSet(rs);
1222  closeStatement(s);
1223  connection.close();
1225  }
1226  }
1227 
1238  public List<AccountFileInstance> getAccountFileInstances(Account account) throws TskCoreException {
1239  List<AccountFileInstance> accountFileInstanceList = new ArrayList<>();
1240 
1241  List<BlackboardArtifact> artifactList = getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID, account.getTypeSpecificID());
1242 
1243  if (artifactList != null && !artifactList.isEmpty()) {
1244  for (BlackboardArtifact artifact : artifactList) {
1245  accountFileInstanceList.add(new AccountFileInstance(artifact, account));
1246  }
1247  }
1248 
1249  if (!accountFileInstanceList.isEmpty()) {
1250  return accountFileInstanceList;
1251  } else {
1252  return null;
1253  }
1254  }
1255 
1264  public List<Account.Type> getAccountTypesInUse() throws TskCoreException {
1265  CaseDbConnection connection = db.getConnection();
1267  Statement s = null;
1268  ResultSet rs = null;
1269  List<Account.Type> inUseAccounts = new ArrayList<>();
1270 
1271  try {
1272  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";
1273  s = connection.createStatement();
1274  rs = connection.executeQuery(s, query); //NON-NLS
1275  Account.Type accountType = null;
1276  while (rs.next()) {
1277  String accountTypeName = rs.getString("type_name");
1278  accountType = this.typeNameToAccountTypeMap.get(accountTypeName);
1279 
1280  if (accountType == null) {
1281  accountType = new Account.Type(accountTypeName, rs.getString("display_name"));
1282  this.accountTypeToTypeIdMap.put(accountType, rs.getInt("account_type_id"));
1283  }
1284 
1285  inUseAccounts.add(accountType);
1286  }
1287  return inUseAccounts;
1288  } catch (SQLException ex) {
1289  throw new TskCoreException("Error getting account type id", ex);
1290  } finally {
1291  closeResultSet(rs);
1292  closeStatement(s);
1293  connection.close();
1295  }
1296  }
1297 
1307  public List<Account> getAccountsRelatedToArtifact(BlackboardArtifact artifact) throws TskCoreException {
1308  if (artifact == null) {
1309  throw new IllegalArgumentException("null arugment passed to getAccountsRelatedToArtifact");
1310  }
1311 
1312  List<Account> accountList = new ArrayList<>();
1313  try (CaseDbConnection connection = db.getConnection()) {
1315  try {
1316  // In order to get a list of all the unique accounts in a relationship with the given aritfact
1317  // we must first union a list of the unique account1_id in the relationship with artifact
1318  // then the unique account2_id (inner select with union). The outter select assures the list
1319  // of the inner select only contains unique accounts.
1320  String query = String.format("SELECT DISTINCT (account_id), account_type_id, account_unique_identifier"
1321  + " FROM ("
1322  + " SELECT DISTINCT (account_id), account_type_id, account_unique_identifier"
1323  + " FROM accounts"
1324  + " JOIN account_relationships ON account1_id = account_id"
1325  + " WHERE relationship_source_obj_id = %d"
1326  + " UNION "
1327  + " SELECT DISTINCT (account_id), account_type_id, account_unique_identifier"
1328  + " FROM accounts"
1329  + " JOIN account_relationships ON account2_id = account_id"
1330  + " WHERE relationship_source_obj_id = %d) AS unionOfRelationships", artifact.getId(), artifact.getId());
1331  try (Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(query)) {
1332  while (rs.next()) {
1333  Account.Type accountType = null;
1334  int accountTypeId = rs.getInt("account_type_id");
1335  for (Map.Entry<Account.Type, Integer> entry : accountTypeToTypeIdMap.entrySet()) {
1336  if (entry.getValue() == accountTypeId) {
1337  accountType = entry.getKey();
1338  break;
1339  }
1340  }
1341 
1342  accountList.add(new Account(rs.getInt("account_id"), accountType, rs.getString("account_unique_identifier")));
1343  }
1344  } catch (SQLException ex) {
1345  throw new TskCoreException("Unable to get account list for give artifact " + artifact.getId(), ex);
1346  }
1347 
1348  } finally {
1350  }
1351  }
1352 
1353  return accountList;
1354  }
1355 
1363  int getAccountTypeId(Account.Type accountType) {
1364  if (accountTypeToTypeIdMap.containsKey(accountType)) {
1365  return accountTypeToTypeIdMap.get(accountType);
1366  }
1367 
1368  return 0;
1369  }
1370 
1382  private String normalizeAccountID(Account.Type accountType, String accountUniqueID) throws InvalidAccountIDException {
1383 
1384  if (accountUniqueID == null || accountUniqueID.isEmpty()) {
1385  throw new InvalidAccountIDException("Account id is null or empty.");
1386  }
1387 
1388  String normalizedAccountID;
1389  if (accountType.equals(Account.Type.PHONE)) {
1390  normalizedAccountID = CommunicationsUtils.normalizePhoneNum(accountUniqueID);
1391  } else if (accountType.equals(Account.Type.EMAIL)) {
1392  normalizedAccountID = CommunicationsUtils.normalizeEmailAddress(accountUniqueID);
1393  } else {
1394  normalizedAccountID = accountUniqueID.toLowerCase().trim();
1395  }
1396 
1397  return normalizedAccountID;
1398  }
1399 
1412  private String getCommunicationsFilterSQL(CommunicationsFilter commFilter, Set<String> applicableFilters) {
1413  if (null == commFilter || commFilter.getAndFilters().isEmpty()) {
1414  return "";
1415  }
1416 
1417  String sqlStr = "";
1418  StringBuilder sqlSB = new StringBuilder();
1419  boolean first = true;
1420  for (CommunicationsFilter.SubFilter subFilter : commFilter.getAndFilters()) {
1421 
1422  // If the filter is applicable
1423  if (applicableFilters.contains(subFilter.getClass().getName())) {
1424  String subfilterSQL = subFilter.getSQL(this);
1425  if (!subfilterSQL.isEmpty()) {
1426  if (first) {
1427  first = false;
1428  } else {
1429  sqlSB.append(" AND ");
1430  }
1431  sqlSB.append("( ");
1432  sqlSB.append(subfilterSQL);
1433  sqlSB.append(" )");
1434  }
1435  }
1436  }
1437 
1438  if (!sqlSB.toString().isEmpty()) {
1439  sqlStr = "( " + sqlSB.toString() + " )";
1440  }
1441  return sqlStr;
1442  }
1443 
1452  private String getMostRecentFilterLimitSQL(CommunicationsFilter filter) {
1453  String limitStr = "";
1454 
1455  if (filter != null && !filter.getAndFilters().isEmpty()) {
1456 
1457  for (CommunicationsFilter.SubFilter subFilter : filter.getAndFilters()) {
1458  if (subFilter.getClass().getName().equals(CommunicationsFilter.MostRecentFilter.class.getName())) {
1459  limitStr = subFilter.getSQL(this);
1460  break;
1461  }
1462  }
1463  }
1464 
1465  return limitStr;
1466  }
1467 }
Set< Content > getRelationshipSources(Set< AccountDeviceInstance > accountDeviceInstanceList, CommunicationsFilter filter)
void postArtifact(BlackboardArtifact artifact, String moduleName)
Definition: Blackboard.java:64
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)
List< Account > getAccountsRelatedToArtifact(BlackboardArtifact artifact)
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-2020 Brian Carrier. (carrier -at- sleuthkit -dot- org)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.