custom content provider and args

This commit is contained in:
Greg DiCristofaro 2023-05-24 08:35:17 -04:00
parent 2fefce513f
commit 402af60d01
2 changed files with 169 additions and 3 deletions

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.casemodule;
import com.sun.xml.bind.v2.TODO;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
@ -29,8 +30,15 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@ -42,11 +50,15 @@ import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.coreutils.XMLUtil;
import org.sleuthkit.datamodel.ContentStream.ContentProvider;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
@ -104,13 +116,20 @@ public final class CaseMetadata {
private static final String SCHEMA_VERSION_FIVE = "5.0";
private final static String ORIGINAL_CASE_ELEMENT_NAME = "OriginalCase"; //NON-NLS
/*
* Fields from schema version 5
*/
private static final String SCHEMA_VERSION_SIX = "6.0";
private final static String CONTENT_PROVIDER_ELEMENT_NAME = "ContentProviderArgs";
private final static String CONTENT_PROVIDER_ARG_DEFAULT_KEY = "DEFAULT";
/*
* Unread fields, regenerated on save.
*/
private final static String MODIFIED_DATE_ELEMENT_NAME = "ModifiedDate"; //NON-NLS
private final static String AUTOPSY_SAVED_BY_ELEMENT_NAME = "SavedByAutopsyVersion"; //NON-NLS
private final static String CURRENT_SCHEMA_VERSION = SCHEMA_VERSION_FIVE;
private final static String CURRENT_SCHEMA_VERSION = SCHEMA_VERSION_SIX;
private final Path metadataFilePath;
private Case.CaseType caseType;
@ -122,6 +141,7 @@ public final class CaseMetadata {
private String createdDate;
private String createdByVersion;
private CaseMetadata originalMetadata = null; // For portable cases
private ContentProvider contentProvider;
/**
* Gets the file extension used for case metadata files.
@ -213,9 +233,13 @@ public final class CaseMetadata {
}
return null;
}
/**
* @return The custom provider for content byte data or null if no custom
* provider.
*/
public ContentProvider getContentProvider() {
return TODO;
return contentProvider;
}
/**
@ -474,6 +498,10 @@ public final class CaseMetadata {
Element originalCaseElement = doc.createElement(ORIGINAL_CASE_ELEMENT_NAME);
rootElement.appendChild(originalCaseElement);
if (originalMetadata != null) {
Element contentProviderEl = doc.createElement(CONTENT_PROVIDER_ELEMENT_NAME);
originalCaseElement.appendChild(contentProviderEl);
serializeContentProviderArgs(doc, argsTODO, contentProviderEl);
createChildElement(doc, originalCaseElement, CREATED_DATE_ELEMENT_NAME, originalMetadata.createdDate);
Element originalCaseDetailsElement = doc.createElement(CASE_ELEMENT_NAME);
originalCaseElement.appendChild(originalCaseDetailsElement);
@ -548,6 +576,8 @@ public final class CaseMetadata {
} else {
this.createdByVersion = getElementTextContent(rootElement, AUTOPSY_CREATED_BY_ELEMENT_NAME, true);
}
serialize TODO;
/*
* Get the content of the children of the case element.
@ -619,6 +649,105 @@ public final class CaseMetadata {
throw new CaseMetadataException(String.format("Error reading from case metadata file %s", metadataFilePath), ex);
}
}
/**
* Loads custom content provider arguments from an xml element.
* @param element The xml element.
* @return The custom content provider arguments.
*/
private Object loadContentProviderArgs(Element element) {
if (element == null) {
return null;
}
NodeList nodeList = element.getChildNodes();
if (nodeList != null && nodeList.getLength() > 0) {
String nodeTag = element.getTagName();
boolean childrenHaveSameTag = true;
List<Pair<String, Object>> children = new ArrayList<>();
for (int i = 0; i < nodeList.getLength(); i++) {
Node child = nodeList.item(i);
if (child instanceof Element) {
Element childElement = (Element) child;
String childTagName = childElement.getTagName();
Object childArg = loadContentProviderArgs(childElement);
children.add(Pair.of(childTagName, childArg));
if (childrenHaveSameTag && !childTagName.equalsIgnoreCase(nodeTag)) {
childrenHaveSameTag = false;
}
}
}
if (childrenHaveSameTag) {
return children.stream().map(Pair::getValue).collect(Collectors.toList());
} else {
Map<String, Object> toRet = new HashMap<>();
for (Pair<String, Object> child: children) {
toRet.put(child.getKey(), child.getValue());
}
return toRet;
}
} else {
return element.getTextContent();
}
}
/**
* Serializes custom content provider arguments to an xml element.
* @param doc The root xml document.
* @param arg The argument to serialize.
* @param el The xml element for the argument.
*/
private void serializeContentProviderArgs(Document doc, Object arg, Element el) {
if (arg == null) {
return;
} else if (arg instanceof List) {
String parentTagName = el.getTagName();
List<? extends Object> argList = (List<? extends Object>) arg;
for (Object childArg: argList) {
Element childEl = doc.createElement(parentTagName);
el.appendChild(childEl);
serializeContentProviderArgs(doc, childArg, childEl);
}
} else if (arg instanceof Map) {
Map<? extends Object, ? extends Object> argMap = (Map<? extends Object, ? extends Object>) arg;
for (Entry<? extends Object, ? extends Object> childEntry: argMap.entrySet()) {
String childTag = childEntry.getKey() == null ? null : childEntry.getKey().toString();
if (StringUtils.isBlank(childTag)) {
continue;
}
Element childEl = doc.createElement(childTag);
el.appendChild(childEl);
serializeContentProviderArgs(doc, childEntry.getValue(), childEl);
}
} else {
el.setTextContent(arg.toString());
}
}
/**
* Attempts to load a content provider for the provided arguments. Returns
* null if no content provider for the arguments can be identified.
*
* @param args The arguments.
* @return The content provider or null if no content provider can be
* provisioned for the arguments
*/
private ContentProvider loadContentProvider(Map<String, Object> args) {
Collection<? extends CustomContentProvider> customContentProviders = Lookup.getDefault().lookupAll(CustomContentProvider.class);
if (customContentProviders != null) {
for (CustomContentProvider customProvider : customContentProviders) {
ContentProvider contentProvider = customProvider.load(args);
if (contentProvider != null) {
return contentProvider;
}
}
}
return null;
}
/**
* Gets the text content of an XML element.

View File

@ -0,0 +1,37 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2023 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.autopsy.casemodule;
import java.util.Map;
import org.sleuthkit.datamodel.ContentStream.ContentProvider;
/**
* Loads content byte stream from custom source.
*/
public interface CustomContentProvider {
/**
* Attempts to create a ContentProvider given the specified args. Returns
* null if arguments are invalid for this custom content provider.
*
* @param args The key value pair of arguments loaded from the .aut xml file.
* @return The created content provider or null if arguments are invalid.
*/
ContentProvider load(Map<String, Object> args);
}