/* * 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 org.openide.util.NbBundle; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.logging.Level; /** * @author dfickling EnCaseKeywordSearchList adds support for Encase * tab-delimited keyword list exports to Autopsy. * * load() does the I/O operation, converting lines from the text file to an * unsorted list of EncaseFileEntrys The next step is to recreate the original * folder hierarchy, and finally the EncaseFileEntries are converted to * KeywordSearchLists * */ class EnCaseKeywordSearchList extends KeywordSearchList { ArrayList<EncaseFileEntry> entriesUnsorted; EncaseFileEntry rootEntry; public EnCaseKeywordSearchList(String encasePath) { super(encasePath); } /** * Follow the EncaseFileEntry hierarchy starting with given entry Create * list for each Folder entry, add keyword for each Expression * * @param entry * @param parentPath */ private void doCreateListsFromEntries(EncaseFileEntry entry, String parentPath) { String name; if (parentPath.isEmpty()) { name = entry.name; } else { name = parentPath + "/" + entry.name; } List<Keyword> children = new ArrayList<>(); for (EncaseFileEntry child : entry.children) { switch (child.type) { case Folder: doCreateListsFromEntries(child, name); break; case Expression: if (child.flags.contains(EncaseFlag.pg)) { // Skip GREP keywords break; } children.add(new Keyword(child.value, true)); break; } } // Give each list a unique name if (theLists.containsKey(name)) { int i = 2; while (theLists.containsKey(name + "(" + i + ")")) { i += 1; } name = name + "(" + i + ")"; } // Don't create lists if there are no keywords if (!children.isEmpty()) { KeywordList newList = new KeywordList(name, new Date(), new Date(), true, true, children); theLists.put(name, newList); } } /** * Convert entriesUnsorted (a list of childless and parentless * EncaseFileEntries) into an EncaseFileEntry structure */ private void doCreateEntryStructure(EncaseFileEntry parent) { if (!parent.isFull()) { EncaseFileEntry child = entriesUnsorted.remove(0); child.hasParent = true; child.parent = parent; parent.addChild(child); if (!child.isFull()) { doCreateEntryStructure(child); } if (!parent.isFull()) { doCreateEntryStructure(parent); } } if (parent.hasParent) { doCreateEntryStructure(parent.parent); } } @Override public boolean save() { throw new UnsupportedOperationException( NbBundle.getMessage(this.getClass(), "KeywordSearchListsEncase.save.exception.msg")); } @Override public boolean save(boolean isExport) { throw new UnsupportedOperationException( NbBundle.getMessage(this.getClass(), "KeywordSearchListsEncase.save2.exception.msg")); } @Override public boolean load() { try { BufferedReader readBuffer = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "utf-16")); //NON-NLS String structLine; String metaLine; entriesUnsorted = new ArrayList<>(); for (int line = 1; line < 6; line++) { readBuffer.readLine(); } while ((structLine = readBuffer.readLine()) != null && (metaLine = readBuffer.readLine()) != null) { String[] structArr = structLine.split("\t"); String[] metaArr = metaLine.split("\t"); EncaseMetaType type = EncaseMetaType.getType(metaArr[0]); String childCount = structArr[1]; String name = metaArr[1]; String value = metaArr[2]; ArrayList<EncaseFlag> flags = new ArrayList<>(); for (int i = 0; i < 17; i++) { if (metaArr.length < i + 4) { continue; } if (!metaArr[i + 3].equals("")) { flags.add(EncaseFlag.getFlag(i)); } } entriesUnsorted.add(new EncaseFileEntry(name, value, Integer.parseInt(childCount), false, null, type, flags)); } if (entriesUnsorted.isEmpty()) { return false; } this.rootEntry = entriesUnsorted.remove(0); doCreateEntryStructure(this.rootEntry); doCreateListsFromEntries(this.rootEntry, ""); return true; } catch (FileNotFoundException ex) { LOGGER.log(Level.INFO, "File at " + filePath + " does not exist!", ex); //NON-NLS } catch (IOException ex) { LOGGER.log(Level.INFO, "Failed to read file at " + filePath, ex); //NON-NLS } return false; } private enum EncaseMetaType { Expression, Folder; static EncaseMetaType getType(String type) { if (type.equals("5")) { return Folder; } else if (type.equals("")) { return Expression; } else { throw new IllegalArgumentException( NbBundle.getMessage(EnCaseKeywordSearchList.class, "KeywordSearchListsEncase.encaseMetaType.exception.msg", type)); } } } /* * Flags for EncaseFileEntries. p8 = UTF-8 p7 = UTF-7 pg = GREP */ private enum EncaseFlag { pc, pu, pb, p8, p7, pg, an, ph, or, di, um, st, ww, pr, lo, ta, cp; static EncaseFlag getFlag(int i) { return EncaseFlag.values()[i]; } } /** * An entry in the Encase keyword list file. */ private class EncaseFileEntry { String name; String value; int childCount; List<EncaseFileEntry> children; EncaseFileEntry parent; EncaseMetaType type; boolean hasParent; ArrayList<EncaseFlag> flags; EncaseFileEntry(String name, String value, int childCount, boolean hasParent, EncaseFileEntry parent, EncaseMetaType type, ArrayList<EncaseFlag> flags) { this.name = name; this.value = value; this.childCount = childCount; this.children = new ArrayList<>(); this.hasParent = hasParent; this.parent = parent; this.type = type; this.flags = flags; } boolean isFull() { return children.size() == childCount; } void addChild(EncaseFileEntry child) { children.add(child); } } }