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