Autopsy  4.14.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
PortableCaseReportModule.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019-2020 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.report.modules.portablecase;
20 
21 import com.google.common.collect.ArrayListMultimap;
22 import com.google.common.collect.Multimap;
24 import java.util.logging.Level;
25 import java.io.BufferedReader;
26 import java.io.File;
27 import java.io.InputStreamReader;
28 import java.io.IOException;
29 import java.nio.file.Files;
30 import java.nio.file.Path;
31 import java.nio.file.Paths;
32 import java.sql.ResultSet;
33 import java.sql.SQLException;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import org.apache.commons.io.FileUtils;
40 import org.openide.modules.InstalledFileLocator;
41 import org.openide.util.NbBundle;
53 import org.sleuthkit.datamodel.AbstractFile;
54 import org.sleuthkit.datamodel.BlackboardArtifact;
55 import org.sleuthkit.datamodel.BlackboardArtifactTag;
56 import org.sleuthkit.datamodel.BlackboardAttribute;
57 import org.sleuthkit.datamodel.CaseDbAccessManager;
58 import org.sleuthkit.datamodel.Content;
59 import org.sleuthkit.datamodel.ContentTag;
60 import org.sleuthkit.datamodel.DataSource;
61 import org.sleuthkit.datamodel.FileSystem;
62 import org.sleuthkit.datamodel.Image;
63 import org.sleuthkit.datamodel.LocalFilesDataSource;
64 import org.sleuthkit.datamodel.Pool;
65 import org.sleuthkit.datamodel.SleuthkitCase;
66 import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
67 import org.sleuthkit.datamodel.TagName;
68 import org.sleuthkit.datamodel.TskCoreException;
69 import org.sleuthkit.datamodel.TskDataException;
70 import org.sleuthkit.datamodel.TskData;
71 import org.sleuthkit.datamodel.Volume;
72 import org.sleuthkit.datamodel.VolumeSystem;
73 
77 public class PortableCaseReportModule implements ReportModule {
78  private static final Logger logger = Logger.getLogger(PortableCaseReportModule.class.getName());
79  private static final String FILE_FOLDER_NAME = "PortableCaseFiles"; // NON-NLS
80  private static final String UNKNOWN_FILE_TYPE_FOLDER = "Other"; // NON-NLS
81  private static final String MAX_ID_TABLE_NAME = "portable_case_max_ids"; // NON-NLS
82  private static final String CASE_UCO_FILE_NAME = "portable_CASE_UCO_output";
83  private static final String CASE_UCO_TMP_DIR = "case_uco_tmp";
85 
86  // These are the types for the exported file subfolders
87  private static final List<FileTypeCategory> FILE_TYPE_CATEGORIES = Arrays.asList(FileTypeCategory.AUDIO, FileTypeCategory.DOCUMENTS,
89 
90  private Case currentCase = null;
91  private SleuthkitCase portableSkCase = null;
92  private String caseName = "";
93  private File caseFolder = null;
94  private File copiedFilesFolder = null;
95 
96  // Maps old object ID from current case to new object in portable case
97  private final Map<Long, Content> oldIdToNewContent = new HashMap<>();
98 
99  // Maps new object ID to the new object
100  private final Map<Long, Content> newIdToContent = new HashMap<>();
101 
102  // Maps old TagName to new TagName
103  private final Map<TagName, TagName> oldTagNameToNewTagName = new HashMap<>();
104 
105  // Map of old artifact type ID to new artifact type ID. There will only be changes if custom artifact types are present.
106  private final Map<Integer, Integer> oldArtTypeIdToNewArtTypeId = new HashMap<>();
107 
108  // Map of old attribute type ID to new attribute type ID. There will only be changes if custom attr types are present.
109  private final Map<Integer, BlackboardAttribute.Type> oldAttrTypeIdToNewAttrType = new HashMap<>();
110 
111  // Map of old artifact ID to new artifact
112  private final Map<Long, BlackboardArtifact> oldArtifactIdToNewArtifact = new HashMap<>();
113 
115  }
116 
117  @NbBundle.Messages({
118  "PortableCaseReportModule.getName.name=Portable Case"
119  })
120  @Override
121  public String getName() {
122  return Bundle.PortableCaseReportModule_getName_name();
123  }
124 
125  @NbBundle.Messages({
126  "PortableCaseReportModule.getDescription.description=Copies selected items to a new single-user case that can be easily shared"
127  })
128  @Override
129  public String getDescription() {
130  return Bundle.PortableCaseReportModule_getDescription_description();
131  }
132 
133  @Override
134  public String getRelativeFilePath() {
135  try {
136  caseName = Case.getCurrentCaseThrows().getDisplayName() + " (Portable)"; // NON-NLS
137  } catch (NoCurrentCaseException ex) {
138  // a case may not be open yet
139  return "";
140  }
141  return caseName;
142  }
143 
149  private void handleCancellation(ReportProgressPanel progressPanel) {
150  logger.log(Level.INFO, "Portable case creation canceled by user"); // NON-NLS
151  progressPanel.setIndeterminate(false);
153  cleanup();
154  }
155 
166  private void handleError(String logWarning, String dialogWarning, Exception ex, ReportProgressPanel progressPanel) {
167  if (ex == null) {
168  logger.log(Level.WARNING, logWarning);
169  } else {
170  logger.log(Level.SEVERE, logWarning, ex);
171  }
172  progressPanel.setIndeterminate(false);
173  progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, dialogWarning);
174  cleanup();
175  }
176 
177  @NbBundle.Messages({
178  "PortableCaseReportModule.generateReport.verifying=Verifying selected parameters...",
179  "PortableCaseReportModule.generateReport.creatingCase=Creating portable case database...",
180  "PortableCaseReportModule.generateReport.copyingTags=Copying tags...",
181  "# {0} - tag name",
182  "PortableCaseReportModule.generateReport.copyingFiles=Copying files tagged as {0}...",
183  "# {0} - tag name",
184  "PortableCaseReportModule.generateReport.copyingArtifacts=Copying artifacts tagged as {0}...",
185  "# {0} - output folder",
186  "PortableCaseReportModule.generateReport.outputDirDoesNotExist=Output folder {0} does not exist",
187  "# {0} - output folder",
188  "PortableCaseReportModule.generateReport.outputDirIsNotDir=Output folder {0} is not a folder",
189  "PortableCaseReportModule.generateReport.caseClosed=Current case has been closed",
190  "PortableCaseReportModule.generateReport.interestingItemError=Error loading intersting items",
191  "PortableCaseReportModule.generateReport.errorReadingTags=Error while reading tags from case database",
192  "PortableCaseReportModule.generateReport.errorReadingSets=Error while reading interesting items sets from case database",
193  "PortableCaseReportModule.generateReport.noContentToCopy=No interesting files, results, or tagged items to copy",
194  "PortableCaseReportModule.generateReport.errorCopyingTags=Error copying tags",
195  "PortableCaseReportModule.generateReport.errorCopyingFiles=Error copying tagged files",
196  "PortableCaseReportModule.generateReport.errorCopyingArtifacts=Error copying tagged artifacts",
197  "PortableCaseReportModule.generateReport.errorCopyingInterestingFiles=Error copying interesting files",
198  "PortableCaseReportModule.generateReport.errorCopyingInterestingResults=Error copying interesting results",
199  "PortableCaseReportModule.generateReport.errorCreatingImageTagTable=Error creating image tags table",
200  "# {0} - attribute type name",
201  "PortableCaseReportModule.generateReport.errorLookingUpAttrType=Error looking up attribute type {0}",
202  "PortableCaseReportModule.generateReport.compressingCase=Compressing case..."
203  })
204 
205  public void generateReport(String reportPath, PortableCaseReportModuleSettings options, ReportProgressPanel progressPanel) {
206  this.settings = options;
207  progressPanel.setIndeterminate(true);
208  progressPanel.start();
209  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_verifying());
210 
211  // Clear out any old values
212  cleanup();
213 
214  // Validate the input parameters
215  File outputDir = new File(reportPath);
216  if (! outputDir.exists()) {
217  handleError("Output folder " + outputDir.toString() + " does not exist",
218  Bundle.PortableCaseReportModule_generateReport_outputDirDoesNotExist(outputDir.toString()), null, progressPanel); // NON-NLS
219  return;
220  }
221 
222  if (! outputDir.isDirectory()) {
223  handleError("Output folder " + outputDir.toString() + " is not a folder",
224  Bundle.PortableCaseReportModule_generateReport_outputDirIsNotDir(outputDir.toString()), null, progressPanel); // NON-NLS
225  return;
226  }
227 
228  // Save the current case object
229  try {
230  currentCase = Case.getCurrentCaseThrows();
231  caseName = currentCase.getDisplayName() + " (Portable)"; // NON-NLS
232  } catch (NoCurrentCaseException ex) {
233  handleError("Current case has been closed",
234  Bundle.PortableCaseReportModule_generateReport_caseClosed(), null, progressPanel); // NON-NLS
235  return;
236  }
237 
238  // Check that there will be something to copy
239  List<TagName> tagNames;
240  if (options.areAllTagsSelected()) {
241  try {
243  } catch (NoCurrentCaseException | TskCoreException ex) {
244  handleError("Unable to get all tags",
245  Bundle.PortableCaseReportModule_generateReport_errorReadingTags(), ex, progressPanel); // NON-NLS
246  return;
247  }
248  } else {
249  tagNames = options.getSelectedTagNames();
250  }
251 
252  List<String> setNames;
253  if (options.areAllSetsSelected()) {
254  try {
255  setNames = getAllInterestingItemsSets();
256  } catch (NoCurrentCaseException | TskCoreException ex) {
257  handleError("Unable to get all interesting items sets",
258  Bundle.PortableCaseReportModule_generateReport_errorReadingSets(), ex, progressPanel); // NON-NLS
259  return;
260  }
261  } else {
262  setNames = options.getSelectedSetNames();
263  }
264 
265  if (tagNames.isEmpty() && setNames.isEmpty()) {
266  handleError("No content to copy",
267  Bundle.PortableCaseReportModule_generateReport_noContentToCopy(), null, progressPanel); // NON-NLS
268  return;
269  }
270 
271  // Create the case.
272  // portableSkCase and caseFolder will be set here.
273  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_creatingCase());
274  createCase(outputDir, progressPanel);
275  if (portableSkCase == null) {
276  // The error has already been handled
277  return;
278  }
279 
280  // Check for cancellation
281  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
282  handleCancellation(progressPanel);
283  return;
284  }
285 
286  // Set up the table for the image tags
287  try {
288  initializeImageTags(progressPanel);
289  } catch (TskCoreException ex) {
290  handleError("Error creating image tag table", Bundle.PortableCaseReportModule_generateReport_errorCreatingImageTagTable(), ex, progressPanel); // NON-NLS
291  return;
292  }
293 
294  // Copy the selected tags
295  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingTags());
296  try {
297  for(TagName tagName:tagNames) {
298  TagName newTagName = portableSkCase.addOrUpdateTagName(tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus());
299  oldTagNameToNewTagName.put(tagName, newTagName);
300  }
301  } catch (TskCoreException ex) {
302  handleError("Error copying tags", Bundle.PortableCaseReportModule_generateReport_errorCopyingTags(), ex, progressPanel); // NON-NLS
303  return;
304  }
305 
306  // Set up tracking to support any custom artifact or attribute types
307  for (BlackboardArtifact.ARTIFACT_TYPE type:BlackboardArtifact.ARTIFACT_TYPE.values()) {
308  oldArtTypeIdToNewArtTypeId.put(type.getTypeID(), type.getTypeID());
309  }
310  for (BlackboardAttribute.ATTRIBUTE_TYPE type:BlackboardAttribute.ATTRIBUTE_TYPE.values()) {
311  try {
312  oldAttrTypeIdToNewAttrType.put(type.getTypeID(), portableSkCase.getAttributeType(type.getLabel()));
313  } catch (TskCoreException ex) {
314  handleError("Error looking up attribute name " + type.getLabel(),
315  Bundle.PortableCaseReportModule_generateReport_errorLookingUpAttrType(type.getLabel()),
316  ex, progressPanel); // NON-NLS
317  }
318  }
319 
320  // Copy the tagged files
321  try {
322  for(TagName tagName:tagNames) {
323  // Check for cancellation
324  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
325  handleCancellation(progressPanel);
326  return;
327  }
328  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingFiles(tagName.getDisplayName()));
329  addFilesToPortableCase(tagName, progressPanel);
330 
331  // Check for cancellation
332  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
333  handleCancellation(progressPanel);
334  return;
335  }
336  }
337  } catch (TskCoreException ex) {
338  handleError("Error copying tagged files", Bundle.PortableCaseReportModule_generateReport_errorCopyingFiles(), ex, progressPanel); // NON-NLS
339  return;
340  }
341 
342  // Copy the tagged artifacts and associated files
343  try {
344  for(TagName tagName:tagNames) {
345  // Check for cancellation
346  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
347  handleCancellation(progressPanel);
348  return;
349  }
350  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingArtifacts(tagName.getDisplayName()));
351  addArtifactsToPortableCase(tagName, progressPanel);
352 
353  // Check for cancellation
354  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
355  handleCancellation(progressPanel);
356  return;
357  }
358  }
359  } catch (TskCoreException ex) {
360  handleError("Error copying tagged artifacts", Bundle.PortableCaseReportModule_generateReport_errorCopyingArtifacts(), ex, progressPanel); // NON-NLS
361  return;
362  }
363 
364  // Copy interesting files and results
365  if (! setNames.isEmpty()) {
366  try {
367  List<BlackboardArtifact> interestingFiles = currentCase.getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
368  for (BlackboardArtifact art:interestingFiles) {
369  // Check for cancellation
370  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
371  handleCancellation(progressPanel);
372  return;
373  }
374 
375  BlackboardAttribute setAttr = art.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
376  if (setNames.contains(setAttr.getValueString())) {
377  copyContentToPortableCase(art, progressPanel);
378  }
379  }
380  } catch (TskCoreException ex) {
381  handleError("Error copying interesting files", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingFiles(), ex, progressPanel); // NON-NLS
382  return;
383  }
384 
385  try {
386  List<BlackboardArtifact> interestingResults = currentCase.getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT);
387  for (BlackboardArtifact art:interestingResults) {
388  // Check for cancellation
389  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
390  handleCancellation(progressPanel);
391  return;
392  }
393  BlackboardAttribute setAttr = art.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
394  if (setNames.contains(setAttr.getValueString())) {
395  copyContentToPortableCase(art, progressPanel);
396  }
397  }
398  } catch (TskCoreException ex) {
399  handleError("Error copying interesting results", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingResults(), ex, progressPanel); // NON-NLS
400  return;
401  }
402  }
403 
404  // Check for cancellation
405  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
406  handleCancellation(progressPanel);
407  return;
408  }
409 
410  //Attempt to generate and included the CASE-UCO report.
411  generateCaseUcoReport(tagNames, setNames, progressPanel);
412 
413  // Compress the case (if desired)
414  if (options.shouldCompress()) {
415  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_compressingCase());
416 
417  boolean success = compressCase(progressPanel);
418 
419  // Check for cancellation
420  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
421  handleCancellation(progressPanel);
422  return;
423  }
424 
425  if (! success) {
426  // Errors have been handled already
427  return;
428  }
429  }
430 
431  // Close the case connections and clear out the maps
432  cleanup();
433 
435 
436  }
437 
449  @NbBundle.Messages({
450  "PortableCaseReportModule.generateCaseUcoReport.errorCreatingReportFolder=Could not make report folder",
451  "PortableCaseReportModule.generateCaseUcoReport.errorGeneratingCaseUcoReport=Problem while generating CASE-UCO report",
452  "PortableCaseReportModule.generateCaseUcoReport.startCaseUcoReportGeneration=Creating a CASE-UCO report of the portable case",
453  "PortableCaseReportModule.generateCaseUcoReport.successCaseUcoReportGeneration=Successfully created a CASE-UCO report of the portable case"
454  })
455  private void generateCaseUcoReport(List<TagName> tagNames, List<String> setNames, ReportProgressPanel progressPanel) {
456  //Create the 'Reports' directory to include a CASE-UCO report.
457  Path reportsDirectory = Paths.get(caseFolder.toString(), "Reports");
458  if(!reportsDirectory.toFile().mkdir()) {
459  logger.log(Level.SEVERE, "Could not make the report folder... skipping "
460  + "CASE-UCO report generation for the portable case");
461  return;
462  }
463 
464  try {
465  //Try to generate case uco output.
466  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_startCaseUcoReportGeneration());
467  CaseUcoReportGenerator reportGenerator = new CaseUcoReportGenerator(reportsDirectory, CASE_UCO_FILE_NAME);
468  //Acquire references for file discovery
469  String caseTempDirectory = currentCase.getTempDirectory();
470  SleuthkitCase skCase = currentCase.getSleuthkitCase();
471  TagsManager tagsManager = currentCase.getServices().getTagsManager();
472 
473  //Create temp directory to filter out duplicate files.
474  //Clear out the old directory if it exists.
475  Path tmpDir = Paths.get(caseTempDirectory, CASE_UCO_TMP_DIR);
476  FileUtils.deleteDirectory(tmpDir.toFile());
477  Files.createDirectory(tmpDir);
478 
479  reportGenerator.addCase(currentCase);
480 
481  //Load all interesting BlackboardArtifacts that belong to the selected SET_NAMEs
482  //binned by data source id.
483  Multimap<Long, BlackboardArtifact> artifactsWithSetName = getInterestingArtifactsBySetName(skCase, setNames);
484 
485  //Search each data source looking for content tags and interesting
486  //items that match the selected tag names and set names.
487  for (Content dataSource : currentCase.getDataSources()) {
494  boolean dataSourceHasBeenIncluded = false;
495  //Search content tags and artifact tags that match
496  for (TagName tagName : tagNames) {
497  for (ContentTag ct : tagsManager.getContentTagsByTagName(tagName, dataSource.getId())) {
498  dataSourceHasBeenIncluded |= addUniqueFile(ct.getContent(),
499  dataSource, tmpDir, reportGenerator, dataSourceHasBeenIncluded);
500  }
501  for (BlackboardArtifactTag bat : tagsManager.getBlackboardArtifactTagsByTagName(tagName, dataSource.getId())) {
502  dataSourceHasBeenIncluded |= addUniqueFile(bat.getContent(),
503  dataSource, tmpDir, reportGenerator, dataSourceHasBeenIncluded);
504  }
505  }
506  //Search artifacts that this data source contains
507  for(BlackboardArtifact bArt : artifactsWithSetName.get(dataSource.getId())) {
508  Content sourceContent = bArt.getParent();
509  dataSourceHasBeenIncluded |= addUniqueFile(sourceContent, dataSource,
510  tmpDir, reportGenerator, dataSourceHasBeenIncluded);
511  }
512  }
513 
514  //Create the report.
515  reportGenerator.generateReport();
516  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_successCaseUcoReportGeneration());
517  } catch (IOException | TskCoreException ex) {
518  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_errorGeneratingCaseUcoReport());
519  logger.log(Level.SEVERE, "Error encountered while trying to create "
520  + "CASE-UCO output for portable case.. the portable case will be "
521  + "completed without a CASE-UCO report.", ex);
522  }
523  }
524 
530  private Multimap<Long, BlackboardArtifact> getInterestingArtifactsBySetName(SleuthkitCase skCase, List<String> setNames) throws TskCoreException {
531  Multimap<Long, BlackboardArtifact> artifactsWithSetName = ArrayListMultimap.create();
532  if(!setNames.isEmpty()) {
533  List<BlackboardArtifact> allArtifacts = skCase.getBlackboardArtifacts(
534  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
535  allArtifacts.addAll(skCase.getBlackboardArtifacts(
536  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT));
537 
538  for(BlackboardArtifact bArt : allArtifacts) {
539  BlackboardAttribute setAttr = bArt.getAttribute(
540  new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
541  if (setNames.contains(setAttr.getValueString())) {
542  artifactsWithSetName.put(bArt.getDataSource().getId(), bArt);
543  }
544  }
545  }
546  return artifactsWithSetName;
547  }
548 
563  private boolean addUniqueFile(Content content, Content dataSource,
564  Path tmpDir, CaseUcoReportGenerator reportGenerator,
565  boolean dataSourceHasBeenIncluded) throws IOException, TskCoreException {
566  if (content instanceof AbstractFile && !(content instanceof DataSource)) {
567  AbstractFile absFile = (AbstractFile) content;
568  Path filePath = tmpDir.resolve(Long.toString(absFile.getId()));
569  if (!absFile.isDir() && !Files.exists(filePath)) {
570  if(!dataSourceHasBeenIncluded) {
571  reportGenerator.addDataSource(dataSource, currentCase);
572  }
573  String subFolder = getExportSubfolder(absFile);
574  String fileName = absFile.getId() + "-" + FileUtil.escapeFileName(absFile.getName());
575  reportGenerator.addFile(absFile, dataSource, Paths.get(FILE_FOLDER_NAME, subFolder, fileName));
576  Files.createFile(filePath);
577  return true;
578  }
579  }
580  return false;
581  }
582 
583  private List<String> getAllInterestingItemsSets() throws NoCurrentCaseException, TskCoreException {
584 
585  // Get the set names in use for the current case.
586  List<String> setNames = new ArrayList<>();
587  Map<String, Long> setCounts;
588 
589  // There may not be a case open when configuring report modules for Command Line execution
590  // Get all SET_NAMEs from interesting item artifacts
591  String innerSelect = "SELECT (value_text) AS set_name FROM blackboard_attributes WHERE (artifact_type_id = '"
592  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + "' OR artifact_type_id = '"
593  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() + "') AND attribute_type_id = '"
594  + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + "'"; // NON-NLS
595 
596  // Get the count of each SET_NAME
597  String query = "set_name, count(1) AS set_count FROM (" + innerSelect + ") set_names GROUP BY set_name"; // NON-NLS
598 
600  Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select(query, callback);
601  setCounts = callback.getSetCountMap();
602  setNames.addAll(setCounts.keySet());
603  return setNames;
604  }
605 
606 
614  @NbBundle.Messages({
615  "# {0} - case folder",
616  "PortableCaseReportModule.createCase.caseDirExists=Case folder {0} already exists",
617  "PortableCaseReportModule.createCase.errorCreatingCase=Error creating case",
618  "# {0} - folder",
619  "PortableCaseReportModule.createCase.errorCreatingFolder=Error creating folder {0}",
620  "PortableCaseReportModule.createCase.errorStoringMaxIds=Error storing maximum database IDs",
621  })
622  private void createCase(File outputDir, ReportProgressPanel progressPanel) {
623 
624  // Create the case folder
625  caseFolder = Paths.get(outputDir.toString(), caseName).toFile();
626 
627  if (caseFolder.exists()) {
628  handleError("Case folder " + caseFolder.toString() + " already exists",
629  Bundle.PortableCaseReportModule_createCase_caseDirExists(caseFolder.toString()), null, progressPanel); // NON-NLS
630  return;
631  }
632 
633  // Create the case
634  try {
635  portableSkCase = currentCase.createPortableCase(caseName, caseFolder);
636  } catch (TskCoreException ex) {
637  handleError("Error creating case " + caseName + " in folder " + caseFolder.toString(),
638  Bundle.PortableCaseReportModule_createCase_errorCreatingCase(), ex, progressPanel); // NON-NLS
639  return;
640  }
641 
642  // Store the highest IDs
643  try {
644  saveHighestIds();
645  } catch (TskCoreException ex) {
646  handleError("Error storing maximum database IDs",
647  Bundle.PortableCaseReportModule_createCase_errorStoringMaxIds(), ex, progressPanel); // NON-NLS
648  return;
649  }
650 
651  // Create the base folder for the copied files
652  copiedFilesFolder = Paths.get(caseFolder.toString(), FILE_FOLDER_NAME).toFile();
653  if (! copiedFilesFolder.mkdir()) {
654  handleError("Error creating folder " + copiedFilesFolder.toString(),
655  Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(copiedFilesFolder.toString()), null, progressPanel); // NON-NLS
656  return;
657  }
658 
659  // Create subfolders for the copied files
660  for (FileTypeCategory cat:FILE_TYPE_CATEGORIES) {
661  File subFolder = Paths.get(copiedFilesFolder.toString(), cat.getDisplayName()).toFile();
662  if (! subFolder.mkdir()) {
663  handleError("Error creating folder " + subFolder.toString(),
664  Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(subFolder.toString()), null, progressPanel); // NON-NLS
665  return;
666  }
667  }
668  File unknownTypeFolder = Paths.get(copiedFilesFolder.toString(), UNKNOWN_FILE_TYPE_FOLDER).toFile();
669  if (! unknownTypeFolder.mkdir()) {
670  handleError("Error creating folder " + unknownTypeFolder.toString(),
671  Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(unknownTypeFolder.toString()), null, progressPanel); // NON-NLS
672  return;
673  }
674 
675  }
676 
682  private void saveHighestIds() throws TskCoreException {
683 
684  CaseDbAccessManager currentCaseDbManager = currentCase.getSleuthkitCase().getCaseDbAccessManager();
685 
686  String tableSchema = "( table_name TEXT PRIMARY KEY, "
687  + " max_id TEXT)"; // NON-NLS
688 
689  portableSkCase.getCaseDbAccessManager().createTable(MAX_ID_TABLE_NAME, tableSchema);
690 
691  currentCaseDbManager.select("max(obj_id) as max_id from tsk_objects", new StoreMaxIdCallback("tsk_objects")); // NON-NLS
692  currentCaseDbManager.select("max(tag_id) as max_id from content_tags", new StoreMaxIdCallback("content_tags")); // NON-NLS
693  currentCaseDbManager.select("max(tag_id) as max_id from blackboard_artifact_tags", new StoreMaxIdCallback("blackboard_artifact_tags")); // NON-NLS
694  currentCaseDbManager.select("max(examiner_id) as max_id from tsk_examiners", new StoreMaxIdCallback("tsk_examiners")); // NON-NLS
695  }
696 
704  private void initializeImageTags(ReportProgressPanel progressPanel) throws TskCoreException {
705 
706  // Create the image tags table in the portable case
707  CaseDbAccessManager portableDbAccessManager = portableSkCase.getCaseDbAccessManager();
708  if (! portableDbAccessManager.tableExists(ContentViewerTagManager.TABLE_NAME)) {
710  }
711  }
712 
721  private void addFilesToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel) throws TskCoreException {
722 
723  // Get all the tags in the current case
724  List<ContentTag> tags = currentCase.getServices().getTagsManager().getContentTagsByTagName(oldTagName);
725 
726  // Copy the files into the portable case and tag
727  for (ContentTag tag : tags) {
728 
729  // Check for cancellation
730  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
731  return;
732  }
733 
734  Content content = tag.getContent();
735  if (content instanceof AbstractFile) {
736 
737  long newFileId = copyContentToPortableCase(content, progressPanel);
738 
739  // Tag the file
740  if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
741  throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS
742  }
743  ContentTag newContentTag = portableSkCase.addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
744 
745  // Get the image tag data associated with this tag (empty string if there is none)
746  // and save it if present
747  String appData = getImageTagDataForContentTag(tag);
748  if (! appData.isEmpty()) {
749  addImageTagToPortableCase(newContentTag, appData);
750  }
751  }
752  }
753  }
754 
764  private String getImageTagDataForContentTag(ContentTag tag) throws TskCoreException {
765 
766  GetImageTagCallback callback = new GetImageTagCallback();
767  String query = "* FROM " + ContentViewerTagManager.TABLE_NAME + " WHERE content_tag_id = " + tag.getId();
768  currentCase.getSleuthkitCase().getCaseDbAccessManager().select(query, callback);
769  return callback.getAppData();
770  }
771 
775  private static class GetImageTagCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
776 
777  private static final Logger logger = Logger.getLogger(PortableCaseReportModule.class.getName());
778  private String appData = "";
779 
780  @Override
781  public void process(ResultSet rs) {
782  try {
783  while (rs.next()) {
784  try {
785  appData = rs.getString("app_data"); // NON-NLS
786  } catch (SQLException ex) {
787  logger.log(Level.WARNING, "Unable to get app_data from result set", ex); // NON-NLS
788  }
789  }
790  } catch (SQLException ex) {
791  logger.log(Level.WARNING, "Failed to get next result for app_data", ex); // NON-NLS
792  }
793  }
794 
800  String getAppData() {
801  return appData;
802  }
803  }
804 
813  private void addImageTagToPortableCase(ContentTag newContentTag, String appData) throws TskCoreException {
814  String insert = "(content_tag_id, app_data) VALUES (" + newContentTag.getId() + ", '" + appData + "')";
815  portableSkCase.getCaseDbAccessManager().insert(ContentViewerTagManager.TABLE_NAME, insert);
816  }
817 
818 
827  private void addArtifactsToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel) throws TskCoreException {
828 
829  List<BlackboardArtifactTag> tags = currentCase.getServices().getTagsManager().getBlackboardArtifactTagsByTagName(oldTagName);
830 
831  // Copy the artifacts into the portable case along with their content and tag
832  for (BlackboardArtifactTag tag : tags) {
833 
834  // Check for cancellation
835  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
836  return;
837  }
838 
839  // Copy the source content
840  Content content = tag.getContent();
841  long newContentId = copyContentToPortableCase(content, progressPanel);
842 
843  // Copy the artifact
844  BlackboardArtifact newArtifact = copyArtifact(newContentId, tag.getArtifact());
845 
846  // Tag the artfiact
847  if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
848  throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS
849  }
850  portableSkCase.addBlackboardArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment());
851  }
852  }
853 
864  private BlackboardArtifact copyArtifact(long newContentId, BlackboardArtifact artifactToCopy) throws TskCoreException {
865 
866  if (oldArtifactIdToNewArtifact.containsKey(artifactToCopy.getArtifactID())) {
867  return oldArtifactIdToNewArtifact.get(artifactToCopy.getArtifactID());
868  }
869 
870  // First create the associated artifact (if present)
871  BlackboardAttribute oldAssociatedAttribute = artifactToCopy.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
872  List<BlackboardAttribute> newAttrs = new ArrayList<>();
873  if (oldAssociatedAttribute != null) {
874  BlackboardArtifact oldAssociatedArtifact = currentCase.getSleuthkitCase().getBlackboardArtifact(oldAssociatedAttribute.getValueLong());
875  BlackboardArtifact newAssociatedArtifact = copyArtifact(newContentId, oldAssociatedArtifact);
876  newAttrs.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
877  String.join(",", oldAssociatedAttribute.getSources()), newAssociatedArtifact.getArtifactID()));
878  }
879 
880  // Create the new artifact
881  int newArtifactTypeId = getNewArtifactTypeId(artifactToCopy);
882  BlackboardArtifact newArtifact = portableSkCase.newBlackboardArtifact(newArtifactTypeId, newContentId);
883  List<BlackboardAttribute> oldAttrs = artifactToCopy.getAttributes();
884 
885  // Copy over each attribute, making sure the type is in the new case.
886  for (BlackboardAttribute oldAttr:oldAttrs) {
887 
888  // The associated artifact has already been handled
889  if (oldAttr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
890  continue;
891  }
892 
893  BlackboardAttribute.Type newAttributeType = getNewAttributeType(oldAttr);
894  switch (oldAttr.getValueType()) {
895  case BYTE:
896  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
897  oldAttr.getValueBytes()));
898  break;
899  case DOUBLE:
900  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
901  oldAttr.getValueDouble()));
902  break;
903  case INTEGER:
904  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
905  oldAttr.getValueInt()));
906  break;
907  case DATETIME:
908  case LONG:
909  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
910  oldAttr.getValueLong()));
911  break;
912  case STRING:
913  case JSON:
914  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
915  oldAttr.getValueString()));
916  break;
917  default:
918  throw new TskCoreException("Unexpected attribute value type found: " + oldAttr.getValueType().getLabel()); // NON-NLS
919  }
920  }
921 
922  newArtifact.addAttributes(newAttrs);
923 
924  oldArtifactIdToNewArtifact.put(artifactToCopy.getArtifactID(), newArtifact);
925  return newArtifact;
926  }
927 
936  private int getNewArtifactTypeId(BlackboardArtifact oldArtifact) throws TskCoreException {
937  if (oldArtTypeIdToNewArtTypeId.containsKey(oldArtifact.getArtifactTypeID())) {
938  return oldArtTypeIdToNewArtTypeId.get(oldArtifact.getArtifactTypeID());
939  }
940 
941  BlackboardArtifact.Type oldCustomType = currentCase.getSleuthkitCase().getArtifactType(oldArtifact.getArtifactTypeName());
942  try {
943  BlackboardArtifact.Type newCustomType = portableSkCase.addBlackboardArtifactType(oldCustomType.getTypeName(), oldCustomType.getDisplayName());
944  oldArtTypeIdToNewArtTypeId.put(oldArtifact.getArtifactTypeID(), newCustomType.getTypeID());
945  return newCustomType.getTypeID();
946  } catch (TskDataException ex) {
947  throw new TskCoreException("Error creating new artifact type " + oldCustomType.getTypeName(), ex); // NON-NLS
948  }
949  }
950 
959  private BlackboardAttribute.Type getNewAttributeType(BlackboardAttribute oldAttribute) throws TskCoreException {
960  BlackboardAttribute.Type oldAttrType = oldAttribute.getAttributeType();
961  if (oldAttrTypeIdToNewAttrType.containsKey(oldAttrType.getTypeID())) {
962  return oldAttrTypeIdToNewAttrType.get(oldAttrType.getTypeID());
963  }
964 
965  try {
966  BlackboardAttribute.Type newCustomType = portableSkCase.addArtifactAttributeType(oldAttrType.getTypeName(),
967  oldAttrType.getValueType(), oldAttrType.getDisplayName());
968  oldAttrTypeIdToNewAttrType.put(oldAttribute.getAttributeType().getTypeID(), newCustomType);
969  return newCustomType;
970  } catch (TskDataException ex) {
971  throw new TskCoreException("Error creating new attribute type " + oldAttrType.getTypeName(), ex); // NON-NLS
972  }
973  }
974 
985  @NbBundle.Messages({
986  "# {0} - File name",
987  "PortableCaseReportModule.copyContentToPortableCase.copyingFile=Copying file {0}",
988  })
989  private long copyContentToPortableCase(Content content, ReportProgressPanel progressPanel) throws TskCoreException {
990  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_copyContentToPortableCase_copyingFile(content.getUniquePath()));
991  return copyContent(content);
992  }
993 
1003  private long copyContent(Content content) throws TskCoreException {
1004 
1005  // Check if we've already copied this content
1006  if (oldIdToNewContent.containsKey(content.getId())) {
1007  return oldIdToNewContent.get(content.getId()).getId();
1008  }
1009 
1010  // Otherwise:
1011  // - Make parent of this object (if applicable)
1012  // - Copy this content
1013  long parentId = 0;
1014  if (content.getParent() != null) {
1015  parentId = copyContent(content.getParent());
1016  }
1017 
1018  Content newContent;
1019  if (content instanceof BlackboardArtifact) {
1020  BlackboardArtifact artifactToCopy = (BlackboardArtifact)content;
1021  newContent = copyArtifact(parentId, artifactToCopy);
1022  } else {
1023  CaseDbTransaction trans = portableSkCase.beginTransaction();
1024  try {
1025  if (content instanceof Image) {
1026  Image image = (Image)content;
1027  newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(),
1028  new ArrayList<>(), image.getTimeZone(), image.getMd5(), image.getSha1(), image.getSha256(), image.getDeviceId(), trans);
1029  } else if (content instanceof VolumeSystem) {
1030  VolumeSystem vs = (VolumeSystem)content;
1031  newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
1032  } else if (content instanceof Volume) {
1033  Volume vs = (Volume)content;
1034  newContent = portableSkCase.addVolume(parentId, vs.getAddr(), vs.getStart(), vs.getLength(),
1035  vs.getDescription(), vs.getFlags(), trans);
1036  } else if (content instanceof Pool) {
1037  Pool pool = (Pool)content;
1038  newContent = portableSkCase.addPool(parentId, pool.getType(), trans);
1039  } else if (content instanceof FileSystem) {
1040  FileSystem fs = (FileSystem)content;
1041  newContent = portableSkCase.addFileSystem(parentId, fs.getImageOffset(), fs.getFsType(), fs.getBlock_size(),
1042  fs.getBlock_count(), fs.getRoot_inum(), fs.getFirst_inum(), fs.getLastInum(),
1043  fs.getName(), trans);
1044  } else if (content instanceof BlackboardArtifact) {
1045  BlackboardArtifact artifactToCopy = (BlackboardArtifact)content;
1046  newContent = copyArtifact(parentId, artifactToCopy);
1047  } else if (content instanceof AbstractFile) {
1048  AbstractFile abstractFile = (AbstractFile)content;
1049 
1050  if (abstractFile instanceof LocalFilesDataSource) {
1051  LocalFilesDataSource localFilesDS = (LocalFilesDataSource)abstractFile;
1052  newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), trans);
1053  } else {
1054  if (abstractFile.isDir()) {
1055  newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
1056  } else {
1057  try {
1058  // Copy the file
1059  String fileName = abstractFile.getId() + "-" + FileUtil.escapeFileName(abstractFile.getName());
1060  String exportSubFolder = getExportSubfolder(abstractFile);
1061  File exportFolder = Paths.get(copiedFilesFolder.toString(), exportSubFolder).toFile();
1062  File localFile = new File(exportFolder, fileName);
1063  ContentUtils.writeToFile(abstractFile, localFile);
1064 
1065  // Get the new parent object in the portable case database
1066  Content oldParent = abstractFile.getParent();
1067  if (! oldIdToNewContent.containsKey(oldParent.getId())) {
1068  throw new TskCoreException("Parent of file with ID " + abstractFile.getId() + " has not been created"); // NON-NLS
1069  }
1070  Content newParent = oldIdToNewContent.get(oldParent.getId());
1071 
1072  // Construct the relative path to the copied file
1073  String relativePath = FILE_FOLDER_NAME + File.separator + exportSubFolder + File.separator + fileName;
1074 
1075  newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
1076  abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getMtime(),
1077  abstractFile.getMd5Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
1078  true, TskData.EncodingType.NONE,
1079  newParent, trans);
1080  } catch (IOException ex) {
1081  throw new TskCoreException("Error copying file " + abstractFile.getName() + " with original obj ID "
1082  + abstractFile.getId(), ex); // NON-NLS
1083  }
1084  }
1085  }
1086  } else {
1087  throw new TskCoreException("Trying to copy unexpected Content type " + content.getClass().getName()); // NON-NLS
1088  }
1089  trans.commit();
1090  } catch (TskCoreException ex) {
1091  trans.rollback();
1092  throw(ex);
1093  }
1094  }
1095 
1096  // Save the new object
1097  oldIdToNewContent.put(content.getId(), newContent);
1098  newIdToContent.put(newContent.getId(), newContent);
1099  return oldIdToNewContent.get(content.getId()).getId();
1100  }
1101 
1109  private String getExportSubfolder(AbstractFile abstractFile) {
1110  if (abstractFile.getMIMEType() == null || abstractFile.getMIMEType().isEmpty()) {
1111  return UNKNOWN_FILE_TYPE_FOLDER;
1112  }
1113 
1114  for (FileTypeCategory cat:FILE_TYPE_CATEGORIES) {
1115  if (cat.getMediaTypes().contains(abstractFile.getMIMEType())) {
1116  return cat.getDisplayName();
1117  }
1118  }
1119  return UNKNOWN_FILE_TYPE_FOLDER;
1120  }
1121 
1125  private void cleanup() {
1126  oldIdToNewContent.clear();
1127  newIdToContent.clear();
1128  oldTagNameToNewTagName.clear();
1129  oldArtTypeIdToNewArtTypeId.clear();
1131  oldArtifactIdToNewArtifact.clear();
1132 
1134 
1135  currentCase = null;
1136  caseFolder = null;
1137  copiedFilesFolder = null;
1138  }
1139 
1143  private void closePortableCaseDatabase() {
1144  if (portableSkCase != null) {
1145  portableSkCase.close();
1146  portableSkCase = null;
1147  }
1148  }
1149 
1150  /*@Override
1151  public JPanel getConfigurationPanel() {
1152  configPanel = new CreatePortableCasePanel();
1153  return configPanel;
1154  } */
1155 
1156  private class StoreMaxIdCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
1157 
1158  private final String tableName;
1159 
1160  StoreMaxIdCallback(String tableName) {
1161  this.tableName = tableName;
1162  }
1163 
1164  @Override
1165  public void process(ResultSet rs) {
1166 
1167  try {
1168  while (rs.next()) {
1169  try {
1170  Long maxId = rs.getLong("max_id"); // NON-NLS
1171  String query = " (table_name, max_id) VALUES ('" + tableName + "', '" + maxId + "')"; // NON-NLS
1172  portableSkCase.getCaseDbAccessManager().insert(MAX_ID_TABLE_NAME, query);
1173 
1174  } catch (SQLException ex) {
1175  logger.log(Level.WARNING, "Unable to get maximum ID from result set", ex); // NON-NLS
1176  } catch (TskCoreException ex) {
1177  logger.log(Level.WARNING, "Unable to save maximum ID from result set", ex); // NON-NLS
1178  }
1179 
1180  }
1181  } catch (SQLException ex) {
1182  logger.log(Level.WARNING, "Failed to get maximum ID from result set", ex); // NON-NLS
1183  }
1184  }
1185  }
1186 
1187  @NbBundle.Messages({
1188  "PortableCaseReportModule.compressCase.errorFinding7zip=Could not locate 7-Zip executable",
1189  "# {0} - Temp folder path",
1190  "PortableCaseReportModule.compressCase.errorCreatingTempFolder=Could not create temporary folder {0}",
1191  "PortableCaseReportModule.compressCase.errorCompressingCase=Error compressing case",
1192  "PortableCaseReportModule.compressCase.canceled=Compression canceled by user",
1193  })
1194  private boolean compressCase(ReportProgressPanel progressPanel) {
1195 
1196  // Close the portable case database (we still need some of the variables that would be cleared by cleanup())
1198 
1199  // Make a temporary folder for the compressed case
1200  File tempZipFolder = Paths.get(currentCase.getTempDirectory(), "portableCase" + System.currentTimeMillis()).toFile(); // NON-NLS
1201  if (! tempZipFolder.mkdir()) {
1202  handleError("Error creating temporary folder " + tempZipFolder.toString(),
1203  Bundle.PortableCaseReportModule_compressCase_errorCreatingTempFolder(tempZipFolder.toString()), null, progressPanel); // NON-NLS
1204  return false;
1205  }
1206 
1207  // Find 7-Zip
1208  File sevenZipExe = locate7ZipExecutable();
1209  if (sevenZipExe == null) {
1210  handleError("Error finding 7-Zip exectuable", Bundle.PortableCaseReportModule_compressCase_errorFinding7zip(), null, progressPanel); // NON-NLS
1211  return false;
1212  }
1213 
1214  // Create the chunk option
1215  String chunkOption = "";
1217  chunkOption = "-v" + settings.getChunkSize().getSevenZipParam();
1218  }
1219 
1220  File zipFile = Paths.get(tempZipFolder.getAbsolutePath(), caseName + ".zip").toFile(); // NON-NLS
1221  ProcessBuilder procBuilder = new ProcessBuilder();
1222  procBuilder.command(
1223  sevenZipExe.getAbsolutePath(),
1224  "a", // Add to archive
1225  zipFile.getAbsolutePath(),
1226  caseFolder.getAbsolutePath(),
1227  chunkOption
1228  );
1229 
1230  try {
1231  Process process = procBuilder.start();
1232 
1233  while (process.isAlive()) {
1234  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
1235  process.destroy();
1236  return false;
1237  }
1238  Thread.sleep(200);
1239  }
1240  int exitCode = process.exitValue();
1241  if (exitCode != 0) {
1242  // Save any errors so they can be logged
1243  StringBuilder sb = new StringBuilder();
1244  try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
1245  String line;
1246  while ((line = br.readLine()) != null) {
1247  sb.append(line).append(System.getProperty("line.separator")); // NON-NLS
1248  }
1249  }
1250 
1251  handleError("Error compressing case\n7-Zip output: " + sb.toString(), Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), null, progressPanel); // NON-NLS
1252  return false;
1253  }
1254  } catch (IOException | InterruptedException ex) {
1255  handleError("Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel); // NON-NLS
1256  return false;
1257  }
1258 
1259  // Delete everything in the case folder then copy over the compressed file(s)
1260  try {
1261  FileUtils.cleanDirectory(caseFolder);
1262  FileUtils.copyDirectory(tempZipFolder, caseFolder);
1263  FileUtils.deleteDirectory(tempZipFolder);
1264  } catch (IOException ex) {
1265  handleError("Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel); // NON-NLS
1266  return false;
1267  }
1268 
1269  return true;
1270  }
1271 
1277  private static File locate7ZipExecutable() {
1278  if (!PlatformUtil.isWindowsOS()) {
1279  return null;
1280  }
1281 
1282  String executableToFindName = Paths.get("7-Zip", "7z.exe").toString(); // NON-NLS
1283  File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, PortableCaseReportModule.class.getPackage().getName(), false);
1284  if (null == exeFile) {
1285  return null;
1286  }
1287 
1288  if (!exeFile.canExecute()) {
1289  return null;
1290  }
1291 
1292  return exeFile;
1293  }
1294 
1298  public static class GetInterestingItemSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
1299 
1300  private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(GetInterestingItemSetNamesCallback.class.getName());
1301  private final Map<String, Long> setCounts = new HashMap<>();
1302 
1303  @Override
1304  public void process(ResultSet rs) {
1305  try {
1306  while (rs.next()) {
1307  try {
1308  Long setCount = rs.getLong("set_count"); // NON-NLS
1309  String setName = rs.getString("set_name"); // NON-NLS
1310 
1311  setCounts.put(setName, setCount);
1312 
1313  } catch (SQLException ex) {
1314  logger.log(Level.WARNING, "Unable to get data_source_obj_id or value from result set", ex); // NON-NLS
1315  }
1316  }
1317  } catch (SQLException ex) {
1318  logger.log(Level.WARNING, "Failed to get next result for values by datasource", ex); // NON-NLS
1319  }
1320  }
1321 
1327  public Map<String, Long> getSetCountMap() {
1328  return setCounts;
1329  }
1330  }
1331 }
List< Content > getDataSources()
Definition: Case.java:1438
void handleError(String logWarning, String dialogWarning, Exception ex, ReportProgressPanel progressPanel)
long copyContentToPortableCase(Content content, ReportProgressPanel progressPanel)
void addArtifactsToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel)
Multimap< Long, BlackboardArtifact > getInterestingArtifactsBySetName(SleuthkitCase skCase, List< String > setNames)
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
Logger(String name, String resourceBundleName)
Definition: Logger.java:160
BlackboardAttribute.Type getNewAttributeType(BlackboardAttribute oldAttribute)
void addFilesToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel)
void generateReport(String reportPath, PortableCaseReportModuleSettings options, ReportProgressPanel progressPanel)
List< BlackboardArtifactTag > getBlackboardArtifactTagsByTagName(TagName tagName)
BlackboardArtifact copyArtifact(long newContentId, BlackboardArtifact artifactToCopy)
void generateCaseUcoReport(List< TagName > tagNames, List< String > setNames, ReportProgressPanel progressPanel)
SleuthkitCase createPortableCase(String caseName, File portableCaseFolder)
Definition: Case.java:2057
static String escapeFileName(String fileName)
Definition: FileUtil.java:169
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
boolean addUniqueFile(Content content, Content dataSource, Path tmpDir, CaseUcoReportGenerator reportGenerator, boolean dataSourceHasBeenIncluded)
List< ContentTag > getContentTagsByTagName(TagName tagName)

Copyright © 2012-2020 Basis Technology. Generated on: Wed Apr 8 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.