19 package org.sleuthkit.autopsy.casemodule;
21 import java.io.BufferedWriter;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.OutputStreamWriter;
26 import java.io.StringWriter;
27 import java.nio.charset.StandardCharsets;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.text.DateFormat;
31 import java.text.SimpleDateFormat;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.Date;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Locale;
40 import java.util.Map.Entry;
41 import java.util.stream.Collectors;
42 import javax.xml.parsers.DocumentBuilder;
43 import javax.xml.parsers.DocumentBuilderFactory;
44 import javax.xml.parsers.ParserConfigurationException;
45 import javax.xml.transform.OutputKeys;
46 import javax.xml.transform.Result;
47 import javax.xml.transform.Source;
48 import javax.xml.transform.Transformer;
49 import javax.xml.transform.TransformerException;
50 import javax.xml.transform.TransformerFactory;
51 import javax.xml.transform.dom.DOMSource;
52 import javax.xml.transform.stream.StreamResult;
53 import org.apache.commons.lang3.StringUtils;
54 import org.apache.commons.lang3.tuple.Pair;
55 import org.openide.util.Lookup;
58 import org.w3c.dom.Document;
59 import org.w3c.dom.Element;
60 import org.w3c.dom.Node;
61 import org.w3c.dom.NodeList;
62 import org.xml.sax.SAXException;
71 private static final DateFormat
DATE_FORMAT =
new SimpleDateFormat(DATE_FORMAT_STRING, Locale.US);
161 return new SimpleDateFormat(DATE_FORMAT_STRING, Locale.US);
189 CaseMetadata(Case.CaseType
caseType, String caseDirectory, String caseName, CaseDetails caseDetails, CaseMetadata originalMetadata) {
190 metadataFilePath = Paths.get(caseDirectory, caseDetails.getCaseDisplayName() +
FILE_EXTENSION);
194 caseDatabaseName =
"";
195 caseDatabasePath =
"";
197 createdByVersion = Version.getVersion();
198 createdDate = CaseMetadata.DATE_FORMAT.format(
new Date());
200 this.contentProviderName = originalMetadata == null ? null : originalMetadata.
contentProviderName;
226 final File[] files = directoryPath.toFile().listFiles();
228 for (File file : files) {
229 final String fileName = file.getName().toLowerCase();
231 return file.toPath();
261 return metadataFilePath.getParent().toString();
297 return caseDetails.getCaseDisplayName();
302 this.caseDetails = newCaseDetails;
305 }
catch (CaseMetadataException ex) {
306 this.caseDetails = oldCaseDetails;
317 return caseDetails.getCaseNumber();
326 return caseDetails.getExaminerName();
330 return caseDetails.getExaminerPhone();
334 return caseDetails.getExaminerEmail();
338 return caseDetails.getCaseNotes();
362 }
catch (CaseMetadataException ex) {
363 this.caseDatabaseName = oldCaseDatabaseName;
400 }
catch (CaseMetadataException ex) {
401 this.createdDate = oldCreatedDate;
411 String getCreatedByVersion() {
423 void setCreatedByVersion(String buildVersion)
throws CaseMetadataException {
425 this.createdByVersion = buildVersion;
428 }
catch (CaseMetadataException ex) {
429 this.createdByVersion = oldCreatedByVersion;
440 void writeToFile() throws CaseMetadataException {
445 Document doc = XMLUtil.createDocument();
452 Source source =
new DOMSource(doc);
453 StringWriter stringWriter =
new StringWriter();
454 Result streamResult =
new StreamResult(stringWriter);
455 Transformer transformer = TransformerFactory.newInstance().newTransformer();
456 transformer.setOutputProperty(OutputKeys.INDENT,
"yes");
457 transformer.setOutputProperty(
"{http://xml.apache.org/xslt}indent-amount",
"2");
458 transformer.transform(source, streamResult);
464 try (BufferedWriter fileWriter =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(metadataFilePath.toFile()), StandardCharsets.UTF_8))) {
465 fileWriter.write(stringWriter.toString());
469 }
catch (ParserConfigurationException | TransformerException | IOException ex) {
470 throw new CaseMetadataException(String.format(
"Error writing to case metadata file %s", metadataFilePath), ex);
481 Element rootElement = doc.createElement(ROOT_ELEMENT_NAME);
482 doc.appendChild(rootElement);
483 createChildElement(doc, rootElement, SCHEMA_VERSION_ELEMENT_NAME, CURRENT_SCHEMA_VERSION);
485 createChildElement(doc, rootElement, MODIFIED_DATE_ELEMENT_NAME, DATE_FORMAT.format(
new Date()));
486 createChildElement(doc, rootElement, AUTOPSY_CREATED_BY_ELEMENT_NAME, createdByVersion);
488 Element caseElement = doc.createElement(CASE_ELEMENT_NAME);
489 rootElement.appendChild(caseElement);
491 Element contentProviderEl = doc.createElement(CONTENT_PROVIDER_ELEMENT_NAME);
492 rootElement.appendChild(contentProviderEl);
494 Element contentProviderNameEl = doc.createElement(CONTENT_PROVIDER_NAME_ELEMENT_NAME);
495 if (this.contentProviderName != null) {
496 contentProviderNameEl.setTextContent(this.contentProviderName);
498 contentProviderEl.appendChild(contentProviderNameEl);
508 Element originalCaseElement = doc.createElement(ORIGINAL_CASE_ELEMENT_NAME);
509 rootElement.appendChild(originalCaseElement);
510 if (originalMetadata != null) {
511 createChildElement(doc, originalCaseElement, CREATED_DATE_ELEMENT_NAME, originalMetadata.createdDate);
512 Element originalCaseDetailsElement = doc.createElement(CASE_ELEMENT_NAME);
513 originalCaseElement.appendChild(originalCaseDetailsElement);
550 private void createChildElement(Document doc, Element parentElement, String elementName, String elementContent) {
551 Element element = doc.createElement(elementName);
552 element.appendChild(doc.createTextNode(elementContent));
553 parentElement.appendChild(element);
567 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
568 Document doc = builder.parse(this.
getFilePath().toFile());
569 doc.getDocumentElement().normalize();
570 Element rootElement = doc.getDocumentElement();
571 if (!rootElement.getNodeName().equals(ROOT_ELEMENT_NAME)) {
572 throw new CaseMetadataException(
"Case metadata file corrupted");
580 if (schemaVersion.equals(SCHEMA_VERSION_ONE)) {
586 Element contentProviderEl =
getChildElOrNull(rootElement, CONTENT_PROVIDER_ELEMENT_NAME);
587 if (contentProviderEl != null) {
588 Element contentProviderNameEl =
getChildElOrNull(contentProviderEl, CONTENT_PROVIDER_NAME_ELEMENT_NAME);
589 this.contentProviderName = contentProviderNameEl != null ? contentProviderNameEl.getTextContent() : null;
591 this.contentProviderName = null;
597 NodeList caseElements = doc.getElementsByTagName(CASE_ELEMENT_NAME);
598 if (caseElements.getLength() == 0) {
599 throw new CaseMetadataException(
"Case metadata file corrupted");
601 Element caseElement = (Element) caseElements.item(0);
603 String caseDisplayName;
605 if (schemaVersion.equals(SCHEMA_VERSION_ONE) || schemaVersion.equals(SCHEMA_VERSION_TWO)) {
612 String examinerPhone;
613 String examinerEmail;
615 if (schemaVersion.equals(SCHEMA_VERSION_ONE) || schemaVersion.equals(SCHEMA_VERSION_TWO) || schemaVersion.equals(SCHEMA_VERSION_THREE)) {
625 this.caseDetails =
new CaseDetails(caseDisplayName, caseNumber, examinerName, examinerPhone, examinerEmail, caseNotes);
628 throw new CaseMetadataException(
"Case metadata file corrupted");
630 switch (schemaVersion) {
631 case SCHEMA_VERSION_ONE:
635 case SCHEMA_VERSION_TWO:
636 this.caseDatabaseName =
getElementTextContent(caseElement, CASE_DB_ABSOLUTE_PATH_ELEMENT_NAME,
true);
640 this.caseDatabaseName =
getElementTextContent(caseElement, CASE_DB_NAME_RELATIVE_ELEMENT_NAME,
true);
651 Path possibleAbsoluteCaseDbPath = Paths.get(this.caseDatabaseName);
653 if (possibleAbsoluteCaseDbPath.getNameCount() > 1) {
655 this.caseDatabaseName = caseDirectoryPath.relativize(possibleAbsoluteCaseDbPath).toString();
657 this.caseDatabasePath = caseDirectoryPath.resolve(caseDatabaseName).toAbsolutePath().toString();
660 }
catch (ParserConfigurationException | SAXException | IOException ex) {
661 throw new CaseMetadataException(String.format(
"Error reading from case metadata file %s", metadataFilePath), ex);
666 NodeList nl = parent.getElementsByTagName(childTag);
667 if (nl != null && nl.getLength() > 0 && nl.item(0) instanceof Element) {
668 return (Element) nl.item(0);
686 private String
getElementTextContent(Element parentElement, String elementName,
boolean contentIsRequired)
throws CaseMetadataException {
687 NodeList elementsList = parentElement.getElementsByTagName(elementName);
688 if (elementsList.getLength() == 0) {
689 throw new CaseMetadataException(String.format(
"Missing %s element from case metadata file %s", elementName, metadataFilePath));
691 String textContent = elementsList.item(0).getTextContent();
692 if (textContent.isEmpty() && contentIsRequired) {
693 throw new CaseMetadataException(String.format(
"Empty %s element in case metadata file %s", elementName, metadataFilePath));
702 public final static class CaseMetadataException
extends Exception {
711 super(message, cause);
729 throw new UnsupportedOperationException();
static CaseType fromString(String typeName)
String getExaminerPhone()
String getCaseDisplayName()
static String getVersion()
String getExaminerEmail()