/* * Autopsy Forensic Browser * * Copyright 2011-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.keywordsearch; import java.io.File; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.logging.Level; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.XMLUtil; import org.sleuthkit.datamodel.BlackboardAttribute; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; /** * Manages reading and writing of keyword lists to user settings XML file * keywords.xml or to any file provided in constructor */ final class XmlKeywordSearchList extends KeywordSearchList { private static final Logger xmlListslogger = Logger.getLogger(XmlKeywordSearchList.class.getName()); private static final String CUR_LISTS_FILE_NAME = "keywords.xml"; //NON-NLS private static final String CUR_LISTS_FILE = PlatformUtil.getUserConfigDirectory() + File.separator + CUR_LISTS_FILE_NAME; private static final String ROOT_EL = "keyword_lists"; //NON-NLS private static final String LIST_EL = "keyword_list"; //NON-NLS private static final String LIST_NAME_ATTR = "name"; //NON-NLS private static final String LIST_CREATE_ATTR = "created"; //NON-NLS private static final String LIST_MOD_ATTR = "modified"; //NON-NLS private static final String LIST_USE_FOR_INGEST = "use_for_ingest"; //NON-NLS private static final String LIST_INGEST_MSGS = "ingest_messages"; //NON-NLS private static final String KEYWORD_EL = "keyword"; //NON-NLS private static final String KEYWORD_LITERAL_ATTR = "literal"; //NON-NLS private static final String KEYWORD_WHOLE_ATTR = "whole"; //NON-NLS private static final String KEYWORD_SELECTOR_ATTR = "selector"; //NON-NLS private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; //NON-NLS private static final String ENCODING = "UTF-8"; //NON-NLS private static XmlKeywordSearchList currentInstance = null; private final DateFormat dateFormatter; static synchronized XmlKeywordSearchList getCurrent() { if (currentInstance == null) { currentInstance = new XmlKeywordSearchList(CUR_LISTS_FILE); currentInstance.reload(); } return currentInstance; } /** * Constructor to obtain handle on other that the current keyword list (such * as for import or export) * * @param xmlFile xmlFile to obtain XmlKeywordSearchList handle on */ XmlKeywordSearchList(String xmlFile) { super(xmlFile); dateFormatter = new SimpleDateFormat(DATE_FORMAT); } @Override public boolean save() { return save(false); } @Override public boolean save(boolean isExport) { boolean success = false; DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); try { DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); Document doc = docBuilder.newDocument(); Element rootEl = doc.createElement(ROOT_EL); doc.appendChild(rootEl); for (String listName : theLists.keySet()) { if (theLists.get(listName).isEditable() == true) { continue; } KeywordList list = theLists.get(listName); String created = dateFormatter.format(list.getDateCreated()); String modified = dateFormatter.format(list.getDateModified()); String useForIngest = list.getUseForIngest().toString(); String ingestMessages = list.getIngestMessages().toString(); List<Keyword> keywords = list.getKeywords(); Element listEl = doc.createElement(LIST_EL); listEl.setAttribute(LIST_NAME_ATTR, listName); listEl.setAttribute(LIST_CREATE_ATTR, created); listEl.setAttribute(LIST_MOD_ATTR, modified); // only write the 'useForIngest' and 'ingestMessages' attributes // if we're not exporting the list. if (!isExport) { listEl.setAttribute(LIST_USE_FOR_INGEST, useForIngest); listEl.setAttribute(LIST_INGEST_MSGS, ingestMessages); } for (Keyword keyword : keywords) { Element keywordEl = doc.createElement(KEYWORD_EL); String literal = keyword.searchTermIsLiteral() ? "true" : "false"; //NON-NLS keywordEl.setAttribute(KEYWORD_LITERAL_ATTR, literal); String whole = keyword.searchTermIsWholeWord() ? "true" : "false"; //NON-NLS keywordEl.setAttribute(KEYWORD_WHOLE_ATTR, whole); BlackboardAttribute.ATTRIBUTE_TYPE selectorType = keyword.getArtifactAttributeType(); if (selectorType != null) { keywordEl.setAttribute(KEYWORD_SELECTOR_ATTR, selectorType.getLabel()); } keywordEl.setTextContent(keyword.getSearchTerm()); listEl.appendChild(keywordEl); } rootEl.appendChild(listEl); } success = XMLUtil.saveDoc(XmlKeywordSearchList.class, filePath, ENCODING, doc); } catch (ParserConfigurationException e) { xmlListslogger.log(Level.SEVERE, "Error saving keyword list: can't initialize parser.", e); //NON-NLS } return success; } /** * load and parse XML, then dispose */ @Override public boolean load() { final Document doc = XMLUtil.loadDoc(XmlKeywordSearchList.class, filePath); if (doc == null) { return false; } Element root = doc.getDocumentElement(); if (root == null) { xmlListslogger.log(Level.SEVERE, "Error loading keyword list: invalid file format."); //NON-NLS return false; } try { NodeList listsNList = root.getElementsByTagName(LIST_EL); int numLists = listsNList.getLength(); for (int i = 0; i < numLists; ++i) { Element listEl = (Element) listsNList.item(i); final String name = listEl.getAttribute(LIST_NAME_ATTR); final String created = listEl.getAttribute(LIST_CREATE_ATTR); final String modified = listEl.getAttribute(LIST_MOD_ATTR); //set these bools to true by default, if they don't exist in XML Boolean useForIngestBool; Boolean ingestMessagesBool; if (listEl.hasAttribute(LIST_USE_FOR_INGEST)) { useForIngestBool = Boolean.parseBoolean(listEl.getAttribute(LIST_USE_FOR_INGEST)); } else { useForIngestBool = true; } if (listEl.hasAttribute(LIST_INGEST_MSGS)) { ingestMessagesBool = Boolean.parseBoolean(listEl.getAttribute(LIST_INGEST_MSGS)); } else { ingestMessagesBool = true; } Date createdDate = dateFormatter.parse(created); Date modDate = dateFormatter.parse(modified); List<Keyword> words = new ArrayList<>(); KeywordList list = new KeywordList(name, createdDate, modDate, useForIngestBool, ingestMessagesBool, words); //parse all words NodeList wordsNList = listEl.getElementsByTagName(KEYWORD_EL); final int numKeywords = wordsNList.getLength(); for (int j = 0; j < numKeywords; ++j) { Element wordEl = (Element) wordsNList.item(j); String literal = wordEl.getAttribute(KEYWORD_LITERAL_ATTR); boolean isLiteral = literal.equals("true"); //NON-NLS Keyword keyword; String whole = wordEl.getAttribute(KEYWORD_WHOLE_ATTR); if (whole.equals("")) { keyword = new Keyword(wordEl.getTextContent(), isLiteral); } else { boolean isWhole = whole.equals("true"); keyword = new Keyword(wordEl.getTextContent(), isLiteral, isWhole); } String selector = wordEl.getAttribute(KEYWORD_SELECTOR_ATTR); if (!selector.equals("")) { BlackboardAttribute.ATTRIBUTE_TYPE selectorType = BlackboardAttribute.ATTRIBUTE_TYPE.fromLabel(selector); keyword.setArtifactAttributeType(selectorType); } words.add(keyword); } theLists.put(name, list); } } catch (ParseException e) { //error parsing dates xmlListslogger.log(Level.SEVERE, "Error loading keyword list: can't parse dates.", e); //NON-NLS return false; } return true; } }