From eb425bbfd9b93f99790a97455e5038a30e84580a Mon Sep 17 00:00:00 2001 From: Oliver Spohngellert Date: Fri, 18 Mar 2016 15:03:54 -0400 Subject: [PATCH] Added content to ArtifactData to optimize getting time, source file. --- .../autopsy/report/ReportGenerator.java | 524 +++++++++--------- 1 file changed, 272 insertions(+), 252 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java index e0d201f4af..6bdaf6a065 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java @@ -51,12 +51,15 @@ import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.SwingWorker; import org.openide.filesystems.FileUtil; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -79,20 +82,20 @@ import org.sleuthkit.datamodel.BlackboardAttribute.Type; * objects displayed using a dialog box. */ class ReportGenerator { - + private static final Logger logger = Logger.getLogger(ReportGenerator.class.getName()); - + private Case currentCase = Case.getCurrentCase(); private SleuthkitCase skCase = currentCase.getSleuthkitCase(); - + private Map tableProgress; private Map generalProgress; private Map fileProgress; private Map> columnHeaderMap; - + private String reportPath; private ReportGenerationPanel panel = new ReportGenerationPanel(); - + static final String REPORTS_DIR = "Reports"; //NON-NLS private List errorList; @@ -114,14 +117,14 @@ class ReportGenerator { return; } } - + ReportGenerator(Map tableModuleStates, Map generalModuleStates, Map fileListModuleStates) { // Create the root reports directory path of the form: /Reports/ / DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss"); Date date = new Date(); String dateNoTime = dateFormat.format(date); this.reportPath = currentCase.getReportDirectory() + File.separator + currentCase.getName() + " " + dateNoTime + File.separator; - + this.errorList = new ArrayList(); // Create the root reports directory. @@ -166,7 +169,7 @@ class ReportGenerator { } } } - + if (null != generalModuleStates) { for (Entry entry : generalModuleStates.entrySet()) { if (entry.getValue()) { @@ -180,7 +183,7 @@ class ReportGenerator { } } } - + if (null != fileListModuleStates) { for (Entry entry : fileListModuleStates.entrySet()) { if (entry.getValue()) { @@ -206,21 +209,21 @@ class ReportGenerator { dialog.setTitle(NbBundle.getMessage(this.getClass(), "ReportGenerator.displayProgress.title.text")); dialog.add(this.panel); dialog.pack(); - + panel.addCloseAction(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { dialog.dispose(); } }); - + dialog.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { panel.close(); } }); - + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); int w = dialog.getSize().width; int h = dialog.getSize().height; @@ -276,7 +279,7 @@ class ReportGenerator { * SwingWorker to run GeneralReportModules. */ private class GeneralReportsWorker extends SwingWorker { - + @Override protected Integer doInBackground() throws Exception { for (Entry entry : generalProgress.entrySet()) { @@ -287,7 +290,7 @@ class ReportGenerator { } return 0; } - + @Override protected void done() { try { @@ -305,24 +308,24 @@ class ReportGenerator { errorList.clear(); } } - + } /** * SwingWorker to run FileReportModules. */ private class FileReportsWorker extends SwingWorker { - + private List enabledInfo = Arrays.asList(FileReportDataTypes.values()); private List fileModules = new ArrayList<>(); - + FileReportsWorker(List enabled) { enabledInfo = enabled; for (Entry entry : fileProgress.entrySet()) { fileModules.add(entry.getKey()); } } - + @Override protected Integer doInBackground() throws Exception { for (FileReportModule module : fileModules) { @@ -333,7 +336,7 @@ class ReportGenerator { NbBundle.getMessage(this.getClass(), "ReportGenerator.progress.queryingDb.text")); } } - + List files = getFiles(); int numFiles = files.size(); for (FileReportModule module : fileModules) { @@ -342,7 +345,7 @@ class ReportGenerator { fileProgress.get(module).setIndeterminate(false); fileProgress.get(module).setMaximumProgress(numFiles); } - + int i = 0; // Add files to report. for (AbstractFile file : files) { @@ -361,7 +364,7 @@ class ReportGenerator { module.addRow(file, enabledInfo); progress.increment(); } - + if ((i % 100) == 0) { progress.updateStatusLabel( NbBundle.getMessage(this.getClass(), "ReportGenerator.progress.processingFile.text", @@ -370,13 +373,13 @@ class ReportGenerator { } i++; } - + for (FileReportModule module : fileModules) { module.endTable(); module.endReport(); fileProgress.get(module).complete(ReportStatus.COMPLETE); } - + return 0; } @@ -400,7 +403,7 @@ class ReportGenerator { return Collections.emptyList(); } } - + @Override protected void done() { try { @@ -425,13 +428,13 @@ class ReportGenerator { * content tags, and blackboard artifact tags. */ private class TableReportsWorker extends SwingWorker { - + private List tableModules = new ArrayList<>(); private List artifactTypes = new ArrayList<>(); private HashSet tagNamesFilter = new HashSet<>(); - + private List images = new ArrayList<>(); - + TableReportsWorker(Map artifactTypeSelections, Map tagNameSelections) { // Get the report modules selected by the user. for (Entry entry : tableProgress.entrySet()) { @@ -454,7 +457,7 @@ class ReportGenerator { } } } - + @Override protected Integer doInBackground() throws Exception { // Start the progress indicators for each active TableReportModule. @@ -483,7 +486,7 @@ class ReportGenerator { tableProgress.get(module).complete(ReportStatus.COMPLETE); module.endReport(); } - + return 0; } @@ -505,7 +508,7 @@ class ReportGenerator { if (tableModules.isEmpty()) { return; } - + for (TableReportModule module : tableModules) { tableProgress.get(module).updateStatusLabel( NbBundle.getMessage(this.getClass(), "ReportGenerator.progress.processing", @@ -520,9 +523,9 @@ class ReportGenerator { writeHashsetHits(tableModules, comment.toString(), tagNamesFilter); continue; } - + List artifactList = getFilteredArtifacts(type, tagNamesFilter); - + if (artifactList.isEmpty()) { continue; } @@ -555,7 +558,7 @@ class ReportGenerator { for (Column currColumn : columns) { columnHeaderNames.add(currColumn.getColumnHeader()); } - + for (TableReportModule module : tableModules) { module.startDataType(type.getDisplayName(), comment.toString()); module.startTable(columnHeaderNames); @@ -570,7 +573,7 @@ class ReportGenerator { if (rowData.isEmpty()) { continue; } - + module.addRow(rowData); } } @@ -621,7 +624,7 @@ class ReportGenerator { NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeCreated"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.size"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.hash"))); - + StringBuilder comment = new StringBuilder(); if (!tagNamesFilter.isEmpty()) { comment.append( @@ -644,14 +647,14 @@ class ReportGenerator { if (passesTagNamesFilter(tag.getName().getDisplayName()) == false) { continue; } - + String fileName; try { fileName = tag.getContent().getUniquePath(); } catch (TskCoreException ex) { fileName = tag.getContent().getName(); } - + ArrayList rowData = new ArrayList<>(Arrays.asList(tag.getName().getDisplayName(), fileName, tag.getComment())); for (TableReportModule module : tableModules) { // @@@ This casting is a tricky little workaround to allow the HTML report module to slip in a content hyperlink. @@ -674,7 +677,7 @@ class ReportGenerator { module.endDataType(); } } - + @Override protected void done() { try { @@ -703,7 +706,7 @@ class ReportGenerator { if (tableModules.isEmpty()) { return; } - + List tags; try { tags = Case.getCurrentCase().getServices().getTagsManager().getAllBlackboardArtifactTags(); @@ -738,7 +741,7 @@ class ReportGenerator { if (passesTagNamesFilter(tag.getName().getDisplayName()) == false) { continue; } - + List row; for (TableReportModule module : tableModules) { row = new ArrayList<>(Arrays.asList(tag.getArtifact().getArtifactTypeName(), tag.getName().getDisplayName(), tag.getComment(), tag.getContent().getName())); @@ -767,7 +770,7 @@ class ReportGenerator { private boolean passesTagNamesFilter(String tagName) { return tagNamesFilter.isEmpty() || tagNamesFilter.contains(tagName); } - + void removeCancelledTableReportModules() { Iterator iter = tableModules.iterator(); while (iter.hasNext()) { @@ -785,7 +788,7 @@ class ReportGenerator { for (TableReportModule module : tableModules) { tableProgress.get(module).updateStatusLabel( NbBundle.getMessage(this.getClass(), "ReportGenerator.progress.createdThumb.text")); - + if (module instanceof ReportHTML) { ReportHTML htmlModule = (ReportHTML) module; htmlModule.startDataType( @@ -796,9 +799,9 @@ class ReportGenerator { emptyHeaders.add(""); } htmlModule.startTable(emptyHeaders); - + htmlModule.addThumbnailRows(images); - + htmlModule.endTable(); htmlModule.endDataType(); } @@ -821,7 +824,7 @@ class ReportGenerator { logger.log(Level.WARNING, "Error while getting content from a blackboard artifact to report on.", ex); //NON-NLS return; } - + if (file != null) { checkIfFileIsImage(file); } @@ -848,13 +851,13 @@ class ReportGenerator { * @param file */ private void checkIfFileIsImage(AbstractFile file) { - + if (file.isDir() || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) { return; } - + if (ImageUtils.thumbnailSupported(file)) { images.add(file); } @@ -866,7 +869,7 @@ class ReportGenerator { if (null == tagsNamesFilter || tagsNamesFilter.isEmpty()) { return false; } - + HashSet filteredTagNames = new HashSet<>(tagNames); filteredTagNames.retainAll(tagsNamesFilter); return filteredTagNames.isEmpty(); @@ -958,7 +961,7 @@ class ReportGenerator { logger.log(Level.SEVERE, "Failed to query keyword lists: ", ex); //NON-NLS return; } - + if (currentCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) { orderByClause = "ORDER BY convert_to(att3.value_text, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS + "convert_to(att1.value_text, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS @@ -984,7 +987,7 @@ class ReportGenerator { try (CaseDbQuery dbQuery = skCase.executeQuery(keywordsQuery)) { ResultSet resultSet = dbQuery.getResultSet(); - + String currentKeyword = ""; String currentList = ""; while (resultSet.next()) { @@ -1006,13 +1009,13 @@ class ReportGenerator { continue; } String tagsList = makeCommaSeparatedList(uniqueTagNames); - + Long objId = resultSet.getLong("obj_id"); //NON-NLS String keyword = resultSet.getString("keyword"); //NON-NLS String preview = resultSet.getString("preview"); //NON-NLS String list = resultSet.getString("list"); //NON-NLS String uniquePath = ""; - + try { AbstractFile f = skCase.getAbstractFileById(objId); if (f != null) { @@ -1059,7 +1062,7 @@ class ReportGenerator { module.startTable(columnHeaderNames); } } - + String previewreplace = EscapeUtil.escapeHtml(preview); for (TableReportModule module : tableModules) { module.addRow(Arrays.asList(new String[]{previewreplace.replaceAll(" { - + private BlackboardArtifact artifact; private List attributes; private HashSet tags; private List rowData = null; - + private Content content; + ArtifactData(BlackboardArtifact artifact, List attrs, HashSet tags) { this.artifact = artifact; this.attributes = attrs; this.tags = tags; + try { + this.content = Case.getCurrentCase().getSleuthkitCase().getContentById(artifact.getObjectID()); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Could not get content from database"); + } } - + public BlackboardArtifact getArtifact() { return artifact; } - + public List getAttributes() { return attributes; } - + public HashSet getTags() { return tags; } - + public long getArtifactID() { return artifact.getArtifactID(); } - + public long getObjectID() { return artifact.getObjectID(); } + /** + * @return the content + */ + public Content getContent() { + return content; + } + /** * Compares ArtifactData objects by the first attribute they have in * common in their List. Should only be used on two @@ -1878,11 +1893,11 @@ class ReportGenerator { * @throws TskCoreException */ private List getOrderedRowDataAsStrings() throws TskCoreException { - + List orderedRowData = new ArrayList<>(); if (ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID() == getArtifact().getArtifactTypeID()) { - AbstractFile file = skCase.getAbstractFileById(getObjectID()); - if (file != null) { + if (content != null && content instanceof AbstractFile) { + AbstractFile file = (AbstractFile) content; orderedRowData.add(file.getName()); orderedRowData.add(file.getNameExtension()); String mimeType = file.getMIMEType(); @@ -1900,7 +1915,7 @@ class ReportGenerator { orderedRowData.add(null); } orderedRowData.add(makeCommaSeparatedList(getTags())); - + } else if (ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() == getArtifact().getArtifactTypeID()) { String[] attributeDataArray = new String[3]; // Array is used so that the order of the attributes is @@ -1913,26 +1928,27 @@ class ReportGenerator { } else if (attr.getAttributeType().equals(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH))) { String pathToShow = attr.getDisplayString(); if (pathToShow.isEmpty()) { - pathToShow = getFileUniquePath(getObjectID()); + pathToShow = getFileUniquePath(getContent()); } attributeDataArray[2] = pathToShow; } } orderedRowData.addAll(Arrays.asList(attributeDataArray)); orderedRowData.add(makeCommaSeparatedList(getTags())); - + } else { if (ReportGenerator.this.columnHeaderMap.containsKey(this.artifact.getArtifactTypeID())) { - + for (Column currColumn : ReportGenerator.this.columnHeaderMap.get(this.artifact.getArtifactTypeID())) { String cellData = currColumn.getCellData(this); orderedRowData.add(cellData); } } } - + return orderedRowData; } + } /** @@ -1947,7 +1963,7 @@ class ReportGenerator { @SuppressWarnings("deprecation") private HashSet getUniqueTagNames(long artifactId) throws TskCoreException { HashSet uniqueTagNames = new HashSet<>(); - + String query = "SELECT display_name, artifact_id FROM tag_names AS tn, blackboard_artifact_tags AS bat " + //NON-NLS "WHERE tn.tag_name_id = bat.tag_name_id AND bat.artifact_id = " + artifactId; //NON-NLS @@ -1959,22 +1975,22 @@ class ReportGenerator { } catch (TskCoreException | SQLException ex) { throw new TskCoreException("Error getting tag names for artifact: ", ex); } - + return uniqueTagNames; - + } - + private interface Column { - + String getColumnHeader(); - + String getCellData(ArtifactData artData); - + Set removeTypeFromSet(Set types); } - + private class AttributeColumn implements Column { - + private String columnHeader; private BlackboardAttribute.Type attributeType; @@ -1988,102 +2004,106 @@ class ReportGenerator { this.columnHeader = Objects.requireNonNull(columnHeader); this.attributeType = attributeType; } - + @Override public String getColumnHeader() { return this.columnHeader; } - + @Override public String getCellData(ArtifactData artData) { List attributes = artData.getAttributes(); for (BlackboardAttribute attribute : attributes) { if (attribute.getAttributeType().equals(this.attributeType)) { - return attribute.getDisplayString(); + if (attribute.getAttributeType().getValueType() != BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) { + return attribute.getDisplayString(); + } else { + return ContentUtils.getStringTime(attribute.getValueLong(), artData.getContent()); + } } } return ""; } - + @Override public Set removeTypeFromSet(Set types) { types.remove(this.attributeType); return types; } } - + private class SourceFileColumn implements Column { - + private String columnHeader; - + SourceFileColumn(String columnHeader) { this.columnHeader = columnHeader; } - + @Override public String getColumnHeader() { return this.columnHeader; } - + @Override public String getCellData(ArtifactData artData) { - return getFileUniquePath(artData.getObjectID()); + return getFileUniquePath(artData.getContent()); /*else if (this.columnHeader.equals(NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.tags"))) { return makeCommaSeparatedList(artData.getTags()); } return "";*/ } - + @Override public Set removeTypeFromSet(Set types) { // This column doesn't have a type, so nothing to remove return types; } } - + private class TaggedResultsColumn implements Column { - + private String columnHeader; - + TaggedResultsColumn(String columnHeader) { this.columnHeader = columnHeader; } - + @Override public String getColumnHeader() { return this.columnHeader; } - + @Override public String getCellData(ArtifactData artData) { return makeCommaSeparatedList(artData.getTags()); } - + @Override public Set removeTypeFromSet(Set types) { // This column doesn't have a type, so nothing to remove return types; } } - + private class HeaderOnlyColumn implements Column { - + private String columnHeader; - + HeaderOnlyColumn(String columnHeader) { this.columnHeader = columnHeader; } - + @Override public String getColumnHeader() { return columnHeader; } - + @Override public String getCellData(ArtifactData artData) { throw new UnsupportedOperationException("Cannot get cell data of unspecified column"); } - + @Override public Set removeTypeFromSet(Set types) { // This column doesn't have a type, so nothing to remove