From f23608c1617b87f2478519a84563020611a90e2e Mon Sep 17 00:00:00 2001 From: Raman Arora <raman@basistech.com> Date: Thu, 31 Oct 2019 13:39:34 -0400 Subject: [PATCH] 5706: handle message attachments --- bindings/java/ivy.xml | 1 + bindings/java/nbproject/project.xml | 2 +- .../org/sleuthkit/datamodel/Blackboard.java | 3 +- .../datamodel/BlackboardAttribute.java | 33 +++- .../org/sleuthkit/datamodel/Bundle.properties | 1 + .../sleuthkit/datamodel/SleuthkitCase.java | 2 + .../CommunicationArtifactsHelper.java | 80 ++++++---- .../blackboardutils/FileAttachment.java | 148 ++++++++++++++++++ .../blackboardutils/MessageAttachments.java | 65 ++++++++ .../blackboardutils/URLAttachment.java | 47 ++++++ 10 files changed, 346 insertions(+), 36 deletions(-) create mode 100644 bindings/java/src/org/sleuthkit/datamodel/blackboardutils/FileAttachment.java create mode 100644 bindings/java/src/org/sleuthkit/datamodel/blackboardutils/MessageAttachments.java create mode 100644 bindings/java/src/org/sleuthkit/datamodel/blackboardutils/URLAttachment.java diff --git a/bindings/java/ivy.xml b/bindings/java/ivy.xml index 3b75f4575..14073535f 100644 --- a/bindings/java/ivy.xml +++ b/bindings/java/ivy.xml @@ -5,6 +5,7 @@ <dependency org="com.google.guava" name="guava" rev="19.0"/> <dependency org="org.apache.commons" name="commons-lang3" rev="3.0"/> + <dependency org="com.google.code.gson" name="gson" rev="2.8.5"/> <dependency org="junit" name="junit" rev="4.8.2"/> <dependency org="com.googlecode.java-diff-utils" name="diffutils" rev="1.2.1"/> diff --git a/bindings/java/nbproject/project.xml b/bindings/java/nbproject/project.xml index 3fb5ec577..ccbcee3fd 100755 --- a/bindings/java/nbproject/project.xml +++ b/bindings/java/nbproject/project.xml @@ -114,7 +114,7 @@ <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/4"> <compilation-unit> <package-root>src</package-root> - <classpath mode="compile">lib;lib/diffutils-1.2.1.jar;lib/junit-4.8.2.jar;lib/postgresql-9.4-1201.jdbc41.jar;lib/c3p0-0.9.5.jar;lib/mchange-commons-java-0.2.9.jar;lib/c3p0-0.9.5-sources.jar;lib/c3p0-0.9.5-javadoc.jar;lib/joda-time-2.4.jar;lib/commons-lang3-3.0.jar;lib/guava-19.0.jar;lib/SparseBitSet-1.1.jar</classpath> + <classpath mode="compile">lib;lib/diffutils-1.2.1.jar;lib/junit-4.8.2.jar;lib/postgresql-9.4-1201.jdbc41.jar;lib/c3p0-0.9.5.jar;lib/mchange-commons-java-0.2.9.jar;lib/c3p0-0.9.5-sources.jar;lib/c3p0-0.9.5-javadoc.jar;lib/joda-time-2.4.jar;lib/commons-lang3-3.0.jar;lib/guava-19.0.jar;lib/SparseBitSet-1.1.jar;lib/gson-2.8.5.jar</classpath> <built-to>build</built-to> <source-level>1.8</source-level> </compilation-unit> diff --git a/bindings/java/src/org/sleuthkit/datamodel/Blackboard.java b/bindings/java/src/org/sleuthkit/datamodel/Blackboard.java index 0745f70be..6497f1260 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Blackboard.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Blackboard.java @@ -344,7 +344,8 @@ private boolean attributesMatch(Collection<BlackboardAttribute> fileAttributesLi fileAttributeValue = fileAttribute.getValueLong(); expectedAttributeValue = expectedAttribute.getValueLong(); break; - case STRING: + case STRING: // Fall-thru + case JSON: fileAttributeValue = fileAttribute.getValueString(); expectedAttributeValue = expectedAttribute.getValueString(); break; diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java index a0501eabd..32ee4acd8 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java +++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java @@ -239,10 +239,13 @@ public BlackboardAttribute(Type attributeType, String source, double valueDouble * * @throws IllegalArgumentException If the value type of the specified * standard attribute type is not - * TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING. + * TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING + * or + * TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON */ public BlackboardAttribute(ATTRIBUTE_TYPE attributeType, String source, String valueString) throws IllegalArgumentException { - if (attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING) { + if (attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING + && attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) { throw new IllegalArgumentException("Value types do not match"); } this.artifactID = 0; @@ -273,7 +276,8 @@ public BlackboardAttribute(ATTRIBUTE_TYPE attributeType, String source, String v * TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING. */ public BlackboardAttribute(Type attributeType, String source, String valueString) throws IllegalArgumentException { - if (attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING) { + if (attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING + && attributeType.getValueType() != TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) { throw new IllegalArgumentException("Type mismatched with value type"); } this.artifactID = 0; @@ -414,7 +418,8 @@ public double getValueDouble() { /** * Gets the value of this attribute. The value is only valid if the - * attribute value type is TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING. + * attribute value type is TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING or + * TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON. * * @return The attribute value. */ @@ -539,6 +544,12 @@ public String getDisplayString() { return TimeUtilities.epochToTime(getValueLong()); } } + break; + case JSON: { + // @TODO 5726: convert JSON string to multilevel bulleted lists + // for display + return getValueString(); + } } return ""; } @@ -795,7 +806,11 @@ public enum TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE { * The value type of the attribute is a long representing seconds from * January 1, 1970. */ - DATETIME(5, "DateTime"); + DATETIME(5, "DateTime"), + /** + * The value type of the attribute is a JSON string. + */ + JSON(6, "Json" ); private final long typeId; private final String typeName; @@ -1354,7 +1369,13 @@ public enum ATTRIBUTE_TYPE { TSK_GROUPS (140, "TSK_GROUPS", bundle.getString("BlackboardAttribute.tskgroups.text"), - TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING); + TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING), + + TSK_ATTACHMENTS (141, "TSK_ATTACHMENTS", + bundle.getString("BlackboardAttribute.tskattachments.text"), + TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON); + + ; private final int typeID; private final String typeName; diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties index 9e0e01c75..eecf0cb0e 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties +++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties @@ -186,6 +186,7 @@ BlackboardAttribute.tskpasswordsettings.text=Password Settings BlackboardAttribute.tskaccountsettings.text=Account Settings BlackboardAttribute.tskpasswordhint.text=Password Hint BlackboardAttribute.tskgroups.text=Groups +BlackboardAttribute.tskattachments.text=Message Attachments AbstractFile.readLocal.exception.msg4.text=Error reading local file\: {0} AbstractFile.readLocal.exception.msg1.text=Error reading local file, local path is not set AbstractFile.readLocal.exception.msg2.text=Error reading local file, it does not exist at local path\: {0} diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 4b311acc6..c9898814d 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -3567,6 +3567,7 @@ private void addBlackBoardAttribute(BlackboardAttribute attr, int artifactTypeId PreparedStatement statement; switch (attr.getAttributeType().getValueType()) { case STRING: + case JSON: statement = connection.getPreparedStatement(PREPARED_STATEMENT.INSERT_STRING_ATTRIBUTE); statement.clearParameters(); statement.setString(7, attr.getValueString()); @@ -3642,6 +3643,7 @@ String addSourceToArtifactAttribute(BlackboardAttribute attr, String source) thr if (BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE != valueType) { switch (valueType) { case STRING: + case JSON: valueClause = " value_text = '" + escapeSingleQuotes(attr.getValueString()) + "'"; break; case INTEGER: diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/CommunicationArtifactsHelper.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/CommunicationArtifactsHelper.java index 267082450..704d18b32 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/CommunicationArtifactsHelper.java +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/CommunicationArtifactsHelper.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.datamodel.blackboardutils; +import com.google.gson.Gson; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -122,7 +123,7 @@ public String getDisplayName() { private final AccountFileInstance selfAccountInstance; // Type of accounts to be created for the module using this helper. - private final Account.Type accountsType; + private final Account.Type moduleAccountsType; /** * Constructs a communications artifacts helper for the given source file. @@ -146,7 +147,7 @@ public CommunicationArtifactsHelper(SleuthkitCase caseDb, super(caseDb, moduleName, srcFile); - this.accountsType = accountsType; + this.moduleAccountsType = accountsType; this.selfAccountInstance = getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.DEVICE, ((DataSource) getAbstractFile().getDataSource()).getDeviceId(), moduleName, getAbstractFile()); } @@ -173,7 +174,7 @@ public CommunicationArtifactsHelper(SleuthkitCase caseDb, String moduleName, Abs super(caseDb, moduleName, srcFile); - this.accountsType = accountsType; + this.moduleAccountsType = accountsType; this.selfAccountInstance = getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(selfAccountType, selfAccountId, moduleName, getAbstractFile()); } @@ -219,8 +220,8 @@ public BlackboardArtifact addContact(String contactName, * @param emailAddr Email address for the contact, may be empty * or null. * - * At least one phone number or email address or an Id is required. - * An Id may be passed in as a TSK_ID attribute in additionalAttributes. + * At least one phone number or email address or an Id is required. An Id + * may be passed in as a TSK_ID attribute in additionalAttributes. * * @param additionalAttributes Additional attributes for contact, may be an * empty list. @@ -245,11 +246,11 @@ public BlackboardArtifact addContact(String contactName, boolean hasAnyIdAttribute = false; if (additionalAttributes != null) { for (BlackboardAttribute attr : additionalAttributes) { - if ((attr.getAttributeType().getTypeName().startsWith("TSK_PHONE")) || - (attr.getAttributeType().getTypeName().startsWith("TSK_EMAIL")) || - (attr.getAttributeType().getTypeName().startsWith("TSK_ID"))) { - hasAnyIdAttribute = true; - break; + if ((attr.getAttributeType().getTypeName().startsWith("TSK_PHONE")) + || (attr.getAttributeType().getTypeName().startsWith("TSK_EMAIL")) + || (attr.getAttributeType().getTypeName().startsWith("TSK_ID"))) { + hasAnyIdAttribute = true; + break; } } } @@ -289,16 +290,16 @@ public BlackboardArtifact addContact(String contactName, // if the additional attribute list has any phone/email/id attributes, create accounts & relationships for those. if ((additionalAttributes != null) && hasAnyIdAttribute) { for (BlackboardAttribute bba : additionalAttributes) { - if (bba.getAttributeType().getTypeName().startsWith("TSK_PHONE")) { + if (bba.getAttributeType().getTypeName().startsWith("TSK_PHONE")) { createContactMethodAccountAndRelationship(Account.Type.PHONE, bba.getValueString(), contactArtifact, 0); - } else if (bba.getAttributeType().getTypeName().startsWith("TSK_EMAIL")) { - createContactMethodAccountAndRelationship(Account.Type.EMAIL, bba.getValueString(), contactArtifact, 0); - } else if (bba.getAttributeType().getTypeName().startsWith("TSK_ID")) { - createContactMethodAccountAndRelationship(this.accountsType, bba.getValueString(), contactArtifact, 0); - } - } + } else if (bba.getAttributeType().getTypeName().startsWith("TSK_EMAIL")) { + createContactMethodAccountAndRelationship(Account.Type.EMAIL, bba.getValueString(), contactArtifact, 0); + } else if (bba.getAttributeType().getTypeName().startsWith("TSK_ID")) { + createContactMethodAccountAndRelationship(this.moduleAccountsType, bba.getValueString(), contactArtifact, 0); + } + } } - + // post artifact getSleuthkitCase().getBlackboard().postArtifact(contactArtifact, getModuleName()); @@ -506,7 +507,7 @@ public BlackboardArtifact addMessage(String messageType, senderAccountInstance = selfAccountInstance; addAttributeIfNotNull(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, selfAccountInstance.getAccount().getTypeSpecificID(), attributes); } else { - senderAccountInstance = createAccountInstance(accountsType, senderId); + senderAccountInstance = createAccountInstance(moduleAccountsType, senderId); addAttributeIfNotNull(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, senderId, attributes); } @@ -516,7 +517,7 @@ public BlackboardArtifact addMessage(String messageType, if (recipientIdsList != null) { for (String recipient : recipientIdsList) { if (!StringUtils.isEmpty(recipient)) { - recipientAccountsList.add(createAccountInstance(accountsType, recipient)); + recipientAccountsList.add(createAccountInstance(moduleAccountsType, recipient)); } } // Create a comma separated string of recipients @@ -706,20 +707,20 @@ public BlackboardArtifact addCalllog(CommunicationDirection direction, throw new IllegalArgumentException("Caller Id not provided for incoming call."); } } else { - callerAccountInstance = createAccountInstance(accountsType, callerId); + callerAccountInstance = createAccountInstance(moduleAccountsType, callerId); addAttributeIfNotNull(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, callerId, attributes); } // Create a comma separated string of callee List<AccountFileInstance> recipientAccountsList = new ArrayList(); String calleesStr = ""; - if (! isEffectivelyEmpty(calleeIdsList)) { + if (!isEffectivelyEmpty(calleeIdsList)) { calleesStr = addressListToString(calleeIdsList); addAttributeIfNotNull(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, calleesStr, attributes); for (String callee : calleeIdsList) { if (!StringUtils.isEmpty(callee)) { - recipientAccountsList.add(createAccountInstance(accountsType, callee)); + recipientAccountsList.add(createAccountInstance(moduleAccountsType, callee)); } } } else { @@ -752,6 +753,27 @@ public BlackboardArtifact addCalllog(CommunicationDirection direction, return callLogArtifact; } + /** + * Adds attachments to a message. + * + * @param message Message artifact + * @param attachments Attachments to add to the message. + * + * @throws TskCoreException If there is an error in adding attachments + */ + public void addAttachments(BlackboardArtifact message, MessageAttachments attachments) throws TskCoreException { + + // Convert the MessageAttachments object to JSON string + Gson gson = new Gson(); + String attachmentsJson = gson.toJson(attachments); + + // Create attribute + message.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS, getModuleName(), attachmentsJson)); + + // @TODO 5698: create a TSK_ASSOCIATED_OBJECT artifact for each fileAttachment + // to associate the file with the message. + } + /** * Converts a list of ids into a single comma separated string. */ @@ -785,15 +807,17 @@ private boolean isEffectivelyEmpty(Collection<String> idList) { if (idList == null || idList.isEmpty()) { return true; } - - for (String id: idList) { - if (!StringUtils.isEmpty(id)) + + for (String id : idList) { + if (!StringUtils.isEmpty(id)) { return false; + } } - + return true; - + } + /** * Adds communication direction attribute to the list, if it is not unknown. */ diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/FileAttachment.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/FileAttachment.java new file mode 100644 index 000000000..327ac296e --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/FileAttachment.java @@ -0,0 +1,148 @@ +/* + * Sleuth Kit Data Model + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.datamodel.blackboardutils; + +import com.google.common.collect.ImmutableList; +import java.util.List; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.DerivedFile; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * File attachment to a message. + * + * The file may or may not have been downloaded, and hence may or may not be + * part of the data data source. + * + * A file attachment may also be created for a blob that is added as a derived + * file. + * + */ +public final class FileAttachment { + + private final String filePathName; + private final long objId; + + // Mobile phones often create mount points to refer to SD Cards or other + // fixed/removable storage media. + // + // Applications use these mount points when referring files. But they may + // not exist physically in the data source. + // + // Common, wellknown mount points are stripped from the file paths to + // accurately search for the file in the image. + transient private static final List<String> KNOWN_MOUNTPOINTS + = ImmutableList.of( + "/data/", // NON-NLS + "/storage/emulated/"); //NON-NLS + + /** + * Creates a file attachment from a file path. + * + * Searches the specified data source for the give file name and path, and + * if found, saves the object Id of the file. If no match is found, then + * just the pathName is remembered. + * + * @param caseDb Case database. + * @param dataSource Data source to search in. + * @param pathName Full path name of the attachment file. + * + * @throws TskCoreException IF there is an error in finding the attached + * file. + */ + public FileAttachment(SleuthkitCase caseDb, Content dataSource, String pathName) throws TskCoreException { + + //normalize the slashes. + this.filePathName = normalizePath(pathName); + + String fileName = filePathName.substring(filePathName.lastIndexOf('/') + 1); + String parentPathSubString = filePathName.substring(0, filePathName.lastIndexOf('/')); + + long matchedFileObjId = -1; + List<AbstractFile> matchedFiles = caseDb.findFiles(dataSource, fileName, parentPathSubString); + for (AbstractFile file : matchedFiles) { + if (file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) { + matchedFileObjId = file.getId(); + break; + } + } + objId = matchedFileObjId; + + } + + /** + * Creates a file attachments from a derived file. + * + * Occasionally the contents of an attachment may be stored as a blob in an + * application database. In that case, the ingest module must write out the contents + * to a local file in the case, and create a corresponding DerivedFile object. + * + * @param derivedFile Derived file for the attachment. + */ + public FileAttachment(DerivedFile derivedFile) { + objId = derivedFile.getId(); + filePathName = derivedFile.getLocalAbsPath() + "/" + derivedFile.getName(); + } + + + /** + * Returns the full path name of the file. + * + * @return full path name. + */ + public String getPathName() { + return filePathName; + } + + /** + * Returns the objId of the attachment file, if the file was found in the + * data source. + * + * @return object id of the file. -1 if no matching file is found. + */ + public long getObjectId() { + return objId; + } + + /** + * Normalizes the input path - convert all slashes to TSK convention, + * and checks for any well know mount point prefixes that need stripped. + * + * @param path path to normalize + * + * @return normalized path. + */ + private String normalizePath(String path) { + //normalize the slashes. + String adjustedPath = path.replace("\\", "/"); + + // Strip common known mountpoints. + for (String mountPoint : KNOWN_MOUNTPOINTS) { + if (adjustedPath.toLowerCase().startsWith(mountPoint)) { + adjustedPath = ("/").concat(adjustedPath.substring(mountPoint.length())); + break; + } + } + + return adjustedPath; + } +} diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/MessageAttachments.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/MessageAttachments.java new file mode 100644 index 000000000..174d3c322 --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/MessageAttachments.java @@ -0,0 +1,65 @@ +/* + * Sleuth Kit Data Model + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.datamodel.blackboardutils; + +import java.util.Collection; +import java.util.Collections; + +/** + * Class to represent attachments to a message. + * + * Attachments can be URL attachments or file attachments. + * + */ +public final class MessageAttachments { + + private final Collection<FileAttachment> fileAttachments; + private final Collection<URLAttachment> urlAttachments; + + /** + * Builds Message attachments from the given file attachments and URL + * attachments. + * + * @param fileAttachments Collection of file attachments. + * @param urlAttachments Collection of URL attachments. + */ + public MessageAttachments(Collection<FileAttachment> fileAttachments, Collection<URLAttachment> urlAttachments) { + this.fileAttachments = fileAttachments; + this.urlAttachments = urlAttachments; + } + + /** + * Returns collection of file attachments. + * + * @return Collection of File attachments. + */ + public Collection<FileAttachment> getFileAttachments() { + return Collections.unmodifiableCollection(fileAttachments); + } + + /** + * Returns collection of URL attachments. + * + * @return Collection of URL attachments. + */ + public Collection<URLAttachment> getUrlAttachments() { + return Collections.unmodifiableCollection(urlAttachments); + } + +} diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/URLAttachment.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/URLAttachment.java new file mode 100644 index 000000000..0cecda68c --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/URLAttachment.java @@ -0,0 +1,47 @@ +/* + * Sleuth Kit Data Model + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.datamodel.blackboardutils; + +/** + * Represents a message attachment where a URL of the attachment is available. + * + */ +public class URLAttachment { + + private final String url; + + /** + * Creates URL attachment. + * + * @param url URL of attachment. + */ + public URLAttachment(String url) { + this.url = url; + } + + /** + * Returns attachment URL. + * + * @return attachment URL. + */ + public String getURL() { + return url; + } + +} -- GitLab