/* * Autopsy Forensic Browser * * Copyright 2012-2014 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.coreutils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.nio.file.Paths; import java.util.logging.Level; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import org.w3c.dom.Document; import org.xml.sax.SAXException; /** * XML Utilities * * This class provides basic utilities for working with XML files, such as * -Validating XML files against a given schema -Saving documents to disk * -Loading documents from disk */ public class XMLUtil { /** * Creates a W3C DOM. * * @return The document object. * * @throws ParserConfigurationException */ public static Document createDocument() throws ParserConfigurationException { DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = builderFactory.newDocumentBuilder(); return builder.newDocument(); } /** * Loads an XML document into a WC3 DOM and validates it using a schema * packaged as a class resource. * * @param docPath The full path to the XML document. * @param clazz The class associated with the schema resource. * @param schemaResourceName The name of the schema resource. * * @return The WC3 DOM document object. * * @throws IOException * @throws ParserConfigurationException * @throws SAXException */ public static <T> Document loadDocument(String docPath, Class<T> clazz, String schemaResourceName) throws IOException, ParserConfigurationException, SAXException { Document doc = loadDocument(docPath); validateDocument(doc, clazz, schemaResourceName); return doc; } /** * Loads an XML document into a WC3 DOM. * * @param docPath The full path to the XML document. * * @return The WC3 DOM document object. * * @throws ParserConfigurationException * @throws SAXException * @throws IOException */ public static Document loadDocument(String docPath) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = builderFactory.newDocumentBuilder(); Document doc = builder.parse(new FileInputStream(docPath)); return doc; } /** * Validates a WC3 DOM using a schema packaged as a class resource. * * @param doc * @param clazz * @param schemaResourceName * * @throws SAXException * @throws IOException */ public static <T> void validateDocument(final Document doc, Class<T> clazz, String schemaResourceName) throws SAXException, IOException { PlatformUtil.extractResourceToUserConfigDir(clazz, schemaResourceName, false); File schemaFile = new File(Paths.get(PlatformUtil.getUserConfigDirectory(), schemaResourceName).toAbsolutePath().toString()); SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schema = schemaFactory.newSchema(schemaFile); Validator validator = schema.newValidator(); validator.validate(new DOMSource(doc), new DOMResult()); } /** * Saves a WC3 DOM by writing it to an XML document. * * @param doc The WC3 DOM document object. * @param docPath The full path to the XML document. * @param encoding Encoding scheme to use for the XML document, e.g., * "UTF-8." * * @throws TransformerConfigurationException * @throws FileNotFoundException * @throws UnsupportedEncodingException * @throws TransformerException * @throws IOException */ public static void saveDocument(final Document doc, String encoding, String docPath) throws TransformerConfigurationException, FileNotFoundException, UnsupportedEncodingException, TransformerException, IOException { TransformerFactory xf = TransformerFactory.newInstance(); xf.setAttribute("indent-number", 1); //NON-NLS Transformer xformer = xf.newTransformer(); xformer.setOutputProperty(OutputKeys.METHOD, "xml"); //NON-NLS xformer.setOutputProperty(OutputKeys.INDENT, "yes"); //NON-NLS xformer.setOutputProperty(OutputKeys.ENCODING, encoding); xformer.setOutputProperty(OutputKeys.STANDALONE, "yes"); //NON-NLS xformer.setOutputProperty(OutputKeys.VERSION, "1.0"); File file = new File(docPath); try (FileOutputStream stream = new FileOutputStream(file)) { Result out = new StreamResult(new OutputStreamWriter(stream, encoding)); xformer.transform(new DOMSource(doc), out); stream.flush(); } } /** * Utility to validate XML files against pre-defined schema files. * * The schema files are extracted automatically when this function is * called, the XML being validated is not. Be sure the XML file is already * extracted otherwise it will return false. * * @param xmlfile The XML file to validate, in DOMSource format * @param clazz class frm package to extract schema file from * @param schemaFile The file name of the schema to validate against, must * exist as a resource in the same package as where this * function is being called. * * For example usages, please see KeywordSearchListsXML, HashDbXML, or * IngestModuleLoader. * */ // TODO: Deprecate. public static <T> boolean xmlIsValid(DOMSource xmlfile, Class<T> clazz, String schemaFile) { try { PlatformUtil.extractResourceToUserConfigDir(clazz, schemaFile, false); File schemaLoc = new File(PlatformUtil.getUserConfigDirectory() + File.separator + schemaFile); SchemaFactory schm = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); try { Schema schema = schm.newSchema(schemaLoc); Validator validator = schema.newValidator(); DOMResult result = new DOMResult(); validator.validate(xmlfile, result); return true; } catch (SAXException e) { Logger.getLogger(clazz.getName()).log(Level.WARNING, "Unable to validate XML file.", e); //NON-NLS return false; } } catch (IOException e) { Logger.getLogger(clazz.getName()).log(Level.WARNING, "Unable to load XML file [" + xmlfile.toString() + "] of type [" + schemaFile + "]", e); //NON-NLS return false; } } /** * Evaluates XML files against an XSD. * * The schema files are extracted automatically when this function is * called, the XML being validated is not. Be sure the XML file is already * extracted otherwise it will return false. * * @param doc The XML DOM to validate * @param clazz class from package to extract schema from * @param type The file name of the schema to validate against, must exist * as a resource in the same package as where this function is * being called * * For example usages, please see KeywordSearchListsXML, HashDbXML, or * IngestModuleLoader. * */ // TODO: Deprecate. public static <T> boolean xmlIsValid(Document doc, Class<T> clazz, String type) { DOMSource dms = new DOMSource(doc); return xmlIsValid(dms, clazz, type); } /** * Loads XML files from disk * * @param clazz the class this method is invoked from * @param xmlPath the full path to the file to load */ // TODO: Deprecate. public static <T> Document loadDoc(Class<T> clazz, String xmlPath) { DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); Document ret = null; try { DocumentBuilder builder = builderFactory.newDocumentBuilder(); ret = builder.parse(new FileInputStream(xmlPath)); } catch (ParserConfigurationException e) { Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error loading XML file " + xmlPath + " : can't initialize parser.", e); //NON-NLS } catch (SAXException e) { Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error loading XML file " + xmlPath + " : can't parse XML.", e); //NON-NLS } catch (IOException e) { //error reading file Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error loading XML file " + xmlPath + " : can't read file.", e); //NON-NLS } return ret; } /** * Loads XML files from disk * * @param clazz the class this method is invoked from * @param xmlPath the full path to the file to load * @param xsdPath the full path to the file to validate against */ // TODO: Deprecate public static <T> Document loadDoc(Class<T> clazz, String xmlPath, String xsdPath) { Document ret = loadDoc(clazz, xmlPath); if (!XMLUtil.xmlIsValid(ret, clazz, xsdPath)) { Logger.getLogger(clazz.getName()).log(Level.WARNING, "Error loading XML file: could not validate against [{0}], results may not be accurate", xsdPath); //NON-NLS } return ret; } /** * Saves XML files to disk * * @param clazz the class this method is invoked from * @param xmlPath the full path to save the XML to * @param encoding to encoding, such as "UTF-8", to encode the file with * @param doc the document to save */ // TODO: Deprecate. public static <T> boolean saveDoc(Class<T> clazz, String xmlPath, String encoding, final Document doc) { TransformerFactory xf = TransformerFactory.newInstance(); xf.setAttribute("indent-number", 1); //NON-NLS boolean success = false; try { Transformer xformer = xf.newTransformer(); xformer.setOutputProperty(OutputKeys.METHOD, "xml"); //NON-NLS xformer.setOutputProperty(OutputKeys.INDENT, "yes"); //NON-NLS xformer.setOutputProperty(OutputKeys.ENCODING, encoding); xformer.setOutputProperty(OutputKeys.STANDALONE, "yes"); //NON-NLS xformer.setOutputProperty(OutputKeys.VERSION, "1.0"); File file = new File(xmlPath); try (FileOutputStream stream = new FileOutputStream(file)) { Result out = new StreamResult(new OutputStreamWriter(stream, encoding)); xformer.transform(new DOMSource(doc), out); stream.flush(); } success = true; } catch (UnsupportedEncodingException e) { Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Should not happen", e); //NON-NLS } catch (TransformerConfigurationException e) { Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error writing XML file", e); //NON-NLS } catch (TransformerException e) { Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error writing XML file", e); //NON-NLS } catch (FileNotFoundException e) { Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error writing XML file: cannot write to file: " + xmlPath, e); //NON-NLS } catch (IOException e) { Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error writing XML file: cannot write to file: " + xmlPath, e); //NON-NLS } return success; } }