19package org.sleuthkit.datamodel.blackboardutils;
21import java.util.ArrayList;
22import java.util.Arrays;
23import java.util.Collection;
24import java.util.Collections;
26import java.util.Optional;
27import java.util.logging.Level;
28import java.util.logging.Logger;
29import org.apache.commons.lang3.StringUtils;
30import org.sleuthkit.datamodel.AbstractFile;
31import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
32import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
33import org.sleuthkit.datamodel.Account;
34import org.sleuthkit.datamodel.AccountFileInstance;
35import org.sleuthkit.datamodel.Blackboard.BlackboardException;
36import org.sleuthkit.datamodel.BlackboardArtifact;
37import org.sleuthkit.datamodel.BlackboardAttribute;
38import org.sleuthkit.datamodel.Content;
39import org.sleuthkit.datamodel.DataArtifact;
40import org.sleuthkit.datamodel.DataSource;
41import org.sleuthkit.datamodel.InvalidAccountIDException;
42import org.sleuthkit.datamodel.Relationship;
43import org.sleuthkit.datamodel.SleuthkitCase;
44import org.sleuthkit.datamodel.TskCoreException;
45import org.sleuthkit.datamodel.TskDataException;
46import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
47import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
48import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.FileAttachment;
87 private final String msgReadStr;
90 this.msgReadStr = readStatus;
106 private final String dirStr;
125 private final String typeStr;
140 private final String selfAccountId;
166 super(caseDb, moduleName, srcContent, ingestJobId);
167 this.moduleAccountsType = accountsType;
169 this.selfAccountId = ((
DataSource) getContent().getDataSource()).getDeviceId();
195 super(caseDb, moduleName, srcContent, ingestJobId);
196 this.moduleAccountsType = accountsType;
197 this.selfAccountType = selfAccountType;
198 this.selfAccountId = selfAccountId;
222 this(caseDb, moduleName, srcContent, accountsType,
null);
249 this(caseDb, moduleName, srcContent, accountsType, selfAccountType, selfAccountId,
null);
274 String phoneNumber, String homePhoneNumber,
277 homePhoneNumber, mobilePhoneNumber, emailAddr,
278 Collections.emptyList());
307 String phoneNumber, String homePhoneNumber,
308 String mobilePhoneNumber, String emailAddr,
312 boolean hasAnyIdAttribute =
false;
313 if (additionalAttributes !=
null) {
315 if ((attr.getAttributeType().getTypeName().startsWith(
"TSK_PHONE"))
316 || (attr.getAttributeType().getTypeName().startsWith(
"TSK_EMAIL"))
317 || (attr.getAttributeType().getTypeName().startsWith(
"TSK_ID"))) {
318 hasAnyIdAttribute =
true;
326 if (StringUtils.isEmpty(phoneNumber) && StringUtils.isEmpty(homePhoneNumber)
327 && StringUtils.isEmpty(mobilePhoneNumber) && StringUtils.isEmpty(emailAddr)
328 && (!hasAnyIdAttribute)) {
329 throw new IllegalArgumentException(
"At least one phone number or email address or an id must be provided.");
333 Collection<BlackboardAttribute>
attributes =
new ArrayList<>();
345 Content content = getContent();
350 createContactMethodAccountAndRelationship(
Account.
Type.
PHONE, phoneNumber, contactArtifact, 0);
351 createContactMethodAccountAndRelationship(
Account.
Type.
PHONE, homePhoneNumber, contactArtifact, 0);
352 createContactMethodAccountAndRelationship(
Account.
Type.
PHONE, mobilePhoneNumber, contactArtifact, 0);
353 createContactMethodAccountAndRelationship(
Account.
Type.
EMAIL, emailAddr, contactArtifact, 0);
356 if ((additionalAttributes !=
null) && hasAnyIdAttribute) {
358 if (bba.getAttributeType().getTypeName().startsWith(
"TSK_PHONE")) {
359 createContactMethodAccountAndRelationship(
Account.
Type.
PHONE, bba.getValueString(), contactArtifact, 0);
360 }
else if (bba.getAttributeType().getTypeName().startsWith(
"TSK_EMAIL")) {
361 createContactMethodAccountAndRelationship(
Account.
Type.
EMAIL, bba.getValueString(), contactArtifact, 0);
362 }
else if (bba.getAttributeType().getTypeName().startsWith(
"TSK_ID")) {
363 createContactMethodAccountAndRelationship(this.moduleAccountsType, bba.getValueString(), contactArtifact, 0);
369 Optional<Long> ingestJobId = getIngestJobId();
370 getSleuthkitCase().getBlackboard().postArtifact(contactArtifact, getModuleName(), ingestJobId.orElse(
null));
372 return contactArtifact;
383 private void createContactMethodAccountAndRelationship(
Account.
Type accountType,
389 if (StringUtils.isNotBlank(accountUniqueID)) {
391 AccountFileInstance contactAccountInstance = createAccountInstance(accountType, accountUniqueID);
398 throw new TskCoreException(String.format(
"Failed to create relationship between account = %s and account = %s.",
399 getSelfAccountInstance().getAccount(), contactAccountInstance.
getAccount()), ex);
401 }
catch (InvalidAccountIDException ex) {
402 LOGGER.log(Level.WARNING, String.format(
"Failed to create account with id %s", accountUniqueID));
419 private AccountFileInstance createAccountInstance(Account.Type accountType, String accountUniqueID)
throws TskCoreException, InvalidAccountIDException {
420 Optional<Long> ingestJobId = getIngestJobId();
421 return getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(accountType, accountUniqueID, getModuleName(), getContent(),
null, ingestJobId.orElse(
null));
453 senderId, recipientId, dateTime, readStatus,
454 subject, messageText, threadId,
455 Collections.emptyList());
485 String messageText, String threadId,
490 Arrays.asList(recipientId),
491 dateTime, readStatus,
492 subject, messageText, threadId,
493 otherAttributesList);
521 List<String> recipientIdsList,
525 senderId, recipientIdsList,
526 dateTime, readStatus,
527 subject, messageText, threadId,
528 Collections.emptyList());
556 List<String> recipientIdsList,
558 String subject, String messageText,
563 Collection<BlackboardAttribute>
attributes =
new ArrayList<>();
569 addMessageReadStatusIfKnown(readStatus,
attributes);
570 addCommDirectionIfKnown(direction,
attributes);
575 selfAccountInstanceLocal = getSelfAccountInstance();
577 LOGGER.log(Level.WARNING, String.format(
"Failed to get/create self account with id %s", selfAccountId), ex);
582 if (StringUtils.isNotBlank(senderId)) {
584 senderAccountInstance = createAccountInstance(moduleAccountsType, senderId);
586 LOGGER.log(Level.WARNING, String.format(
"Invalid account identifier %s", senderId));
591 List<AccountFileInstance> recipientAccountsList =
new ArrayList<>();
592 String recipientsStr =
"";
593 if (!isEffectivelyEmpty(recipientIdsList)) {
594 for (String recipient : recipientIdsList) {
595 if (StringUtils.isNotBlank(recipient)) {
597 recipientAccountsList.add(createAccountInstance(moduleAccountsType, recipient));
599 LOGGER.log(Level.WARNING, String.format(
"Invalid account identifier %s", recipient));
604 recipientsStr = addressListToString(recipientIdsList);
610 if (StringUtils.isEmpty(senderId) && selfAccountInstanceLocal !=
null) {
611 senderAccountInstance = selfAccountInstanceLocal;
614 if (senderAccountInstance !=
null) {
623 if (isEffectivelyEmpty(recipientIdsList) && selfAccountInstanceLocal !=
null) {
625 recipientAccountsList.add(selfAccountInstanceLocal);
628 if (senderAccountInstance !=
null) {
635 if (StringUtils.isEmpty(senderId) && selfAccountInstanceLocal !=
null) {
637 senderAccountInstance = selfAccountInstanceLocal;
638 }
else if (isEffectivelyEmpty(recipientIdsList) && selfAccountInstanceLocal !=
null) {
641 recipientAccountsList.add(selfAccountInstanceLocal);
645 if (senderAccountInstance !=
null) {
660 Content content = getContent();
665 getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance,
668 throw new TskCoreException(String.format(
"Failed to create Message relationships between sender account = %s and recipients = %s.",
673 Optional<Long> ingestJobId = getIngestJobId();
674 getSleuthkitCase().getBlackboard().postArtifact(msgArtifact, getModuleName(), ingestJobId.orElse(
null));
703 String callerId, String calleeId,
705 return addCalllog(direction, callerId, calleeId,
706 startDateTime, endDateTime, mediaType,
707 Collections.emptyList());
736 long startDateTime,
long endDateTime,
741 Arrays.asList(calleeId),
742 startDateTime, endDateTime,
744 otherAttributesList);
771 Collection<String> calleeIdsList,
772 long startDateTime,
long endDateTime,
775 return addCalllog(direction, callerId, calleeIdsList,
776 startDateTime, endDateTime,
778 Collections.emptyList());
806 Collection<String> calleeIdsList,
807 long startDateTime,
long endDateTime,
812 if (StringUtils.isEmpty(callerId) && (isEffectivelyEmpty(calleeIdsList))) {
813 throw new IllegalArgumentException(
"Either a caller id, or at least one callee id must be provided for a call log.");
818 selfAccountInstanceLocal = getSelfAccountInstance();
820 LOGGER.log(Level.WARNING, String.format(
"Failed to get/create self account with id %s", selfAccountId), ex);
823 Collection<BlackboardAttribute>
attributes =
new ArrayList<>();
828 addCommDirectionIfKnown(direction,
attributes);
831 if (StringUtils.isNotBlank(callerId)) {
833 callerAccountInstance = createAccountInstance(moduleAccountsType, callerId);
835 LOGGER.log(Level.WARNING, String.format(
"Failed to create account with id %s", callerId));
840 List<AccountFileInstance> recipientAccountsList =
new ArrayList<>();
841 String calleesStr =
"";
842 if (!isEffectivelyEmpty(calleeIdsList)) {
843 calleesStr = addressListToString(calleeIdsList);
844 for (String callee : calleeIdsList) {
845 if (StringUtils.isNotBlank(callee)) {
847 recipientAccountsList.add(createAccountInstance(moduleAccountsType, callee));
849 LOGGER.log(Level.WARNING, String.format(
"Failed to create account with id %s", callee));
858 if (isEffectivelyEmpty(calleeIdsList)) {
859 throw new IllegalArgumentException(
"Callee not provided for an outgoing call.");
862 if (StringUtils.isEmpty(callerId) && selfAccountInstanceLocal !=
null) {
863 callerAccountInstance = selfAccountInstanceLocal;
866 if (callerAccountInstance !=
null) {
875 if (StringUtils.isEmpty(callerId)) {
876 throw new IllegalArgumentException(
"Caller Id not provided for incoming call.");
879 if (isEffectivelyEmpty(calleeIdsList) && selfAccountInstanceLocal !=
null) {
881 recipientAccountsList.add(selfAccountInstanceLocal);
884 if (callerAccountInstance !=
null) {
893 if (callerAccountInstance !=
null) {
903 Content content = getContent();
910 getSleuthkitCase().getCommunicationsManager().addRelationships(callerAccountInstance,
913 throw new TskCoreException(String.format(
"Failed to create Call log relationships between caller account = %s and callees = %s.",
914 (callerAccountInstance !=
null) ? callerAccountInstance.
getAccount() :
"", calleesStr), ex);
918 Optional<Long> ingestJobId = getIngestJobId();
919 getSleuthkitCase().getBlackboard().postArtifact(callLogArtifact, getModuleName(), ingestJobId.orElse(
null));
922 return callLogArtifact;
936 message.addAttribute(blackboardAttribute);
939 List<BlackboardArtifact> assocObjectArtifacts =
new ArrayList<>();
940 Collection<FileAttachment> fileAttachments = attachments.getFileAttachments();
942 long attachedFileObjId = fileAttachment.getObjectId();
943 if (attachedFileObjId >= 0) {
945 DataArtifact artifact = associateAttachmentWithMessage(message, attachedFile);
946 assocObjectArtifacts.add(artifact);
951 Optional<Long> ingestJobId = getIngestJobId();
952 getSleuthkitCase().getBlackboard().postArtifacts(assocObjectArtifacts, getModuleName(), ingestJobId.orElse(
null));
954 throw new TskCoreException(
"Error posting TSK_ASSOCIATED_ARTIFACT artifacts for attachments", ex);
971 Collection<BlackboardAttribute>
attributes =
new ArrayList<>();
973 return attachedFile.newDataArtifact(ASSOCIATED_OBJ_TYPE,
attributes);
979 private String addressListToString(Collection<String> addressList) {
981 String toAddresses =
"";
982 if (addressList !=
null && (!addressList.isEmpty())) {
983 StringBuilder toAddressesSb =
new StringBuilder();
984 for (String address : addressList) {
985 if (!StringUtils.isEmpty(address)) {
986 toAddressesSb = toAddressesSb.length() > 0 ? toAddressesSb.append(
", ").append(address) : toAddressesSb.append(address);
989 toAddresses = toAddressesSb.toString();
1004 private boolean isEffectivelyEmpty(Collection<String> idList) {
1006 if (idList ==
null || idList.isEmpty()) {
1010 for (String
id : idList) {
1011 if (!StringUtils.isEmpty(
id)) {
1023 private void addCommDirectionIfKnown(
CommunicationDirection direction, Collection<BlackboardAttribute> attributes) {
1025 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DIRECTION, getModuleName(), direction.getDisplayName()));
1032 private void addMessageReadStatusIfKnown(
MessageReadStatus readStatus, Collection<BlackboardAttribute> attributes) {
1034 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_READ_STATUS, getModuleName(), (readStatus ==
MessageReadStatus.
READ) ? 1 : 0));
1046 private synchronized AccountFileInstance getSelfAccountInstance() throws TskCoreException, InvalidAccountIDException {
1047 if (selfAccountInstance ==
null) {
1048 Optional<Long> ingestJobId = getIngestJobId();
1049 selfAccountInstance = getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(selfAccountType, selfAccountId, this.getModuleName(), getContent(),
null, ingestJobId.orElse(
null));
1051 return selfAccountInstance;
SleuthkitCase getSleuthkitCase()
static final Account.Type PHONE
static final Account.Type DEVICE
static final Account.Type EMAIL
String getTypeSpecificID()
void addAttributes(Collection< BlackboardAttribute > attributes)
void addRelationships(AccountFileInstance sender, List< AccountFileInstance > recipients, BlackboardArtifact sourceArtifact, org.sleuthkit.datamodel.Relationship.Type relationshipType, long dateTime)
static final Relationship.Type CALL_LOG
static final Relationship.Type MESSAGE
static final Relationship.Type CONTACT
CommunicationsManager getCommunicationsManager()
AbstractFile getAbstractFileById(long id)
CommunicationArtifactsHelper(SleuthkitCase caseDb, String moduleName, Content srcContent, Account.Type accountsType, Account.Type selfAccountType, String selfAccountId, Long ingestJobId)
BlackboardArtifact addContact(String contactName, String phoneNumber, String homePhoneNumber, String mobilePhoneNumber, String emailAddr, Collection< BlackboardAttribute > additionalAttributes)
BlackboardArtifact addCalllog(CommunicationDirection direction, String callerId, Collection< String > calleeIdsList, long startDateTime, long endDateTime, CallMediaType mediaType, Collection< BlackboardAttribute > otherAttributesList)
CommunicationArtifactsHelper(SleuthkitCase caseDb, String moduleName, Content srcContent, Account.Type accountsType)
BlackboardArtifact addCalllog(CommunicationDirection direction, String callerId, Collection< String > calleeIdsList, long startDateTime, long endDateTime, CallMediaType mediaType)
BlackboardArtifact addMessage(String messageType, CommunicationDirection direction, String senderId, List< String > recipientIdsList, long dateTime, MessageReadStatus readStatus, String subject, String messageText, String threadId)
BlackboardArtifact addMessage(String messageType, CommunicationDirection direction, String senderId, String recipientId, long dateTime, MessageReadStatus readStatus, String subject, String messageText, String threadId, Collection< BlackboardAttribute > otherAttributesList)
void addAttachments(BlackboardArtifact message, MessageAttachments attachments)
BlackboardArtifact addCalllog(CommunicationDirection direction, String callerId, String calleeId, long startDateTime, long endDateTime, CallMediaType mediaType)
BlackboardArtifact addMessage(String messageType, CommunicationDirection direction, String senderId, List< String > recipientIdsList, long dateTime, MessageReadStatus readStatus, String subject, String messageText, String threadId, Collection< BlackboardAttribute > otherAttributesList)
BlackboardArtifact addCalllog(CommunicationDirection direction, String callerId, String calleeId, long startDateTime, long endDateTime, CallMediaType mediaType, Collection< BlackboardAttribute > otherAttributesList)
CommunicationArtifactsHelper(SleuthkitCase caseDb, String moduleName, Content srcContent, Account.Type accountsType, Long ingestJobId)
BlackboardArtifact addMessage(String messageType, CommunicationDirection direction, String senderId, String recipientId, long dateTime, MessageReadStatus readStatus, String subject, String messageText, String threadId)
BlackboardArtifact addContact(String contactName, String phoneNumber, String homePhoneNumber, String mobilePhoneNumber, String emailAddr)
CommunicationArtifactsHelper(SleuthkitCase caseDb, String moduleName, Content srcContent, Account.Type accountsType, Account.Type selfAccountType, String selfAccountId)
static< T > BlackboardAttribute toAttribute(BlackboardAttribute.Type attrType, String moduleName, T attrValue)
CommunicationDirection(String dir)
MessageReadStatus(String readStatus)
UNREAD
read status is unknown
READ
message has not been read
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection< BlackboardAttribute > attributesList)