Autopsy  4.16.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;
23 import com.google.gson.Gson;
24 import com.google.gson.GsonBuilder;
25 import com.google.gson.JsonElement;
26 import com.google.gson.stream.JsonWriter;
28 import java.util.logging.Level;
29 import java.io.BufferedReader;
30 import java.io.File;
31 import java.io.FileOutputStream;
32 import java.io.FileWriter;
33 import java.io.InputStreamReader;
34 import java.io.IOException;
35 import java.io.OutputStream;
36 import java.io.OutputStreamWriter;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.nio.file.Paths;
40 import java.sql.ResultSet;
41 import java.sql.SQLException;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.HashMap;
45 import java.util.List;
46 import java.util.Map;
47 import org.apache.commons.io.FileUtils;
48 import org.openide.modules.InstalledFileLocator;
49 import org.openide.util.NbBundle;
61 import org.sleuthkit.caseuco.CaseUcoExporter;
62 import org.sleuthkit.datamodel.AbstractFile;
63 import org.sleuthkit.datamodel.BlackboardArtifact;
64 import org.sleuthkit.datamodel.BlackboardArtifactTag;
65 import org.sleuthkit.datamodel.BlackboardAttribute;
66 import org.sleuthkit.datamodel.CaseDbAccessManager;
67 import org.sleuthkit.datamodel.Content;
68 import org.sleuthkit.datamodel.ContentTag;
69 import org.sleuthkit.datamodel.DataSource;
70 import org.sleuthkit.datamodel.FileSystem;
71 import org.sleuthkit.datamodel.Image;
72 import org.sleuthkit.datamodel.LocalFilesDataSource;
73 import org.sleuthkit.datamodel.Pool;
74 import org.sleuthkit.datamodel.SleuthkitCase;
75 import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
76 import org.sleuthkit.datamodel.TagName;
77 import org.sleuthkit.datamodel.TaggingManager.ContentTagChange;
78 import org.sleuthkit.datamodel.TskCoreException;
79 import org.sleuthkit.datamodel.TskDataException;
80 import org.sleuthkit.datamodel.TskData;
81 import org.sleuthkit.datamodel.Volume;
82 import org.sleuthkit.datamodel.VolumeSystem;
83 
87 public class PortableCaseReportModule implements ReportModule {
88 
89  private static final Logger logger = Logger.getLogger(PortableCaseReportModule.class.getName());
90  private static final String FILE_FOLDER_NAME = "PortableCaseFiles"; // NON-NLS
91  private static final String UNKNOWN_FILE_TYPE_FOLDER = "Other"; // NON-NLS
92  private static final String MAX_ID_TABLE_NAME = "portable_case_max_ids"; // NON-NLS
93  private static final String CASE_UCO_FILE_NAME = "portable_CASE_UCO_output";
94  private static final String CASE_UCO_TMP_DIR = "case_uco_tmp";
96 
97  // These are the types for the exported file subfolders
98  private static final List<FileTypeCategory> FILE_TYPE_CATEGORIES = Arrays.asList(FileTypeCategory.AUDIO, FileTypeCategory.DOCUMENTS,
100 
101  private Case currentCase = null;
102  private SleuthkitCase portableSkCase = null;
103  private String caseName = "";
104  private File caseFolder = null;
105  private File copiedFilesFolder = null;
106 
107  // Maps old object ID from current case to new object in portable case
108  private final Map<Long, Content> oldIdToNewContent = new HashMap<>();
109 
110  // Maps new object ID to the new object
111  private final Map<Long, Content> newIdToContent = new HashMap<>();
112 
113  // Maps old TagName to new TagName
114  private final Map<TagName, TagName> oldTagNameToNewTagName = new HashMap<>();
115 
116  // Map of old artifact type ID to new artifact type ID. There will only be changes if custom artifact types are present.
117  private final Map<Integer, Integer> oldArtTypeIdToNewArtTypeId = new HashMap<>();
118 
119  // Map of old attribute type ID to new attribute type ID. There will only be changes if custom attr types are present.
120  private final Map<Integer, BlackboardAttribute.Type> oldAttrTypeIdToNewAttrType = new HashMap<>();
121 
122  // Map of old artifact ID to new artifact
123  private final Map<Long, BlackboardArtifact> oldArtifactIdToNewArtifact = new HashMap<>();
124 
126  }
127 
128  @NbBundle.Messages({
129  "PortableCaseReportModule.getName.name=Portable Case"
130  })
131  @Override
132  public String getName() {
133  return Bundle.PortableCaseReportModule_getName_name();
134  }
135 
136  @NbBundle.Messages({
137  "PortableCaseReportModule.getDescription.description=Copies selected items to a new single-user case that can be easily shared"
138  })
139  @Override
140  public String getDescription() {
141  return Bundle.PortableCaseReportModule_getDescription_description();
142  }
143 
144  @Override
145  public String getRelativeFilePath() {
146  try {
147  caseName = Case.getCurrentCaseThrows().getDisplayName() + " (Portable)"; // NON-NLS
148  } catch (NoCurrentCaseException ex) {
149  // a case may not be open yet
150  return "";
151  }
152  return caseName;
153  }
154 
160  private void handleCancellation(ReportProgressPanel progressPanel) {
161  logger.log(Level.INFO, "Portable case creation canceled by user"); // NON-NLS
162  progressPanel.setIndeterminate(false);
164  cleanup();
165  }
166 
177  private void handleError(String logWarning, String dialogWarning, Exception ex, ReportProgressPanel progressPanel) {
178  if (ex == null) {
179  logger.log(Level.WARNING, logWarning);
180  } else {
181  logger.log(Level.SEVERE, logWarning, ex);
182  }
183  progressPanel.setIndeterminate(false);
184  progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, dialogWarning);
185  cleanup();
186  }
187 
188  @NbBundle.Messages({
189  "PortableCaseReportModule.generateReport.verifying=Verifying selected parameters...",
190  "PortableCaseReportModule.generateReport.creatingCase=Creating portable case database...",
191  "PortableCaseReportModule.generateReport.copyingTags=Copying tags...",
192  "# {0} - tag name",
193  "PortableCaseReportModule.generateReport.copyingFiles=Copying files tagged as {0}...",
194  "# {0} - tag name",
195  "PortableCaseReportModule.generateReport.copyingArtifacts=Copying artifacts tagged as {0}...",
196  "# {0} - output folder",
197  "PortableCaseReportModule.generateReport.outputDirDoesNotExist=Output folder {0} does not exist",
198  "# {0} - output folder",
199  "PortableCaseReportModule.generateReport.outputDirIsNotDir=Output folder {0} is not a folder",
200  "PortableCaseReportModule.generateReport.caseClosed=Current case has been closed",
201  "PortableCaseReportModule.generateReport.interestingItemError=Error loading intersting items",
202  "PortableCaseReportModule.generateReport.errorReadingTags=Error while reading tags from case database",
203  "PortableCaseReportModule.generateReport.errorReadingSets=Error while reading interesting items sets from case database",
204  "PortableCaseReportModule.generateReport.noContentToCopy=No interesting files, results, or tagged items to copy",
205  "PortableCaseReportModule.generateReport.errorCopyingTags=Error copying tags",
206  "PortableCaseReportModule.generateReport.errorCopyingFiles=Error copying tagged files",
207  "PortableCaseReportModule.generateReport.errorCopyingArtifacts=Error copying tagged artifacts",
208  "PortableCaseReportModule.generateReport.errorCopyingInterestingFiles=Error copying interesting files",
209  "PortableCaseReportModule.generateReport.errorCopyingInterestingResults=Error copying interesting results",
210  "PortableCaseReportModule.generateReport.errorCreatingImageTagTable=Error creating image tags table",
211  "PortableCaseReportModule.generateReport.errorCopyingAutopsy=Error copying application",
212  "# {0} - attribute type name",
213  "PortableCaseReportModule.generateReport.errorLookingUpAttrType=Error looking up attribute type {0}",
214  "PortableCaseReportModule.generateReport.compressingCase=Compressing case...",
215  "PortableCaseReportModule_generateReport_copyingAutopsy=Copying application..."
216  })
217 
218  public void generateReport(String reportPath, PortableCaseReportModuleSettings options, ReportProgressPanel progressPanel) {
219  this.settings = options;
220  progressPanel.setIndeterminate(true);
221  progressPanel.start();
222  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_verifying());
223 
224  // Clear out any old values
225  cleanup();
226 
227  // Validate the input parameters
228  File outputDir = new File(reportPath);
229  if (!outputDir.exists()) {
230  handleError("Output folder " + outputDir.toString() + " does not exist",
231  Bundle.PortableCaseReportModule_generateReport_outputDirDoesNotExist(outputDir.toString()), null, progressPanel); // NON-NLS
232  return;
233  }
234 
235  if (!outputDir.isDirectory()) {
236  handleError("Output folder " + outputDir.toString() + " is not a folder",
237  Bundle.PortableCaseReportModule_generateReport_outputDirIsNotDir(outputDir.toString()), null, progressPanel); // NON-NLS
238  return;
239  }
240 
241  // Save the current case object
242  try {
243  currentCase = Case.getCurrentCaseThrows();
244  caseName = currentCase.getDisplayName() + " (Portable)"; // NON-NLS
245  } catch (NoCurrentCaseException ex) {
246  handleError("Current case has been closed",
247  Bundle.PortableCaseReportModule_generateReport_caseClosed(), null, progressPanel); // NON-NLS
248  return;
249  }
250 
251  // If the applciation is included add an extra level to the directory structure
252  if (options.includeApplication()) {
253  outputDir = Paths.get(outputDir.toString(), caseName).toFile();
254  }
255  // Check that there will be something to copy
256  List<TagName> tagNames;
257  if (options.areAllTagsSelected()) {
258  try {
260  } catch (NoCurrentCaseException | TskCoreException ex) {
261  handleError("Unable to get all tags",
262  Bundle.PortableCaseReportModule_generateReport_errorReadingTags(), ex, progressPanel); // NON-NLS
263  return;
264  }
265  } else {
266  tagNames = options.getSelectedTagNames();
267  }
268 
269  List<String> setNames;
270  if (options.areAllSetsSelected()) {
271  try {
272  setNames = getAllInterestingItemsSets();
273  } catch (NoCurrentCaseException | TskCoreException ex) {
274  handleError("Unable to get all interesting items sets",
275  Bundle.PortableCaseReportModule_generateReport_errorReadingSets(), ex, progressPanel); // NON-NLS
276  return;
277  }
278  } else {
279  setNames = options.getSelectedSetNames();
280  }
281 
282  if (tagNames.isEmpty() && setNames.isEmpty()) {
283  handleError("No content to copy",
284  Bundle.PortableCaseReportModule_generateReport_noContentToCopy(), null, progressPanel); // NON-NLS
285  return;
286  }
287 
288  // Create the case.
289  // portableSkCase and caseFolder will be set here.
290  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_creatingCase());
291  createCase(outputDir, progressPanel);
292  if (portableSkCase == null) {
293  // The error has already been handled
294  return;
295  }
296 
297  // Check for cancellation
298  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
299  handleCancellation(progressPanel);
300  return;
301  }
302 
303  // Set up the table for the image tags
304  try {
305  initializeImageTags(progressPanel);
306  } catch (TskCoreException ex) {
307  handleError("Error creating image tag table", Bundle.PortableCaseReportModule_generateReport_errorCreatingImageTagTable(), ex, progressPanel); // NON-NLS
308  return;
309  }
310 
311  // Copy the selected tags
312  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingTags());
313  try {
314  for (TagName tagName : tagNames) {
315  TagName newTagName = portableSkCase.addOrUpdateTagName(tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus());
316  oldTagNameToNewTagName.put(tagName, newTagName);
317  }
318  } catch (TskCoreException ex) {
319  handleError("Error copying tags", Bundle.PortableCaseReportModule_generateReport_errorCopyingTags(), ex, progressPanel); // NON-NLS
320  return;
321  }
322 
323  // Set up tracking to support any custom artifact or attribute types
324  for (BlackboardArtifact.ARTIFACT_TYPE type : BlackboardArtifact.ARTIFACT_TYPE.values()) {
325  oldArtTypeIdToNewArtTypeId.put(type.getTypeID(), type.getTypeID());
326  }
327  for (BlackboardAttribute.ATTRIBUTE_TYPE type : BlackboardAttribute.ATTRIBUTE_TYPE.values()) {
328  try {
329  oldAttrTypeIdToNewAttrType.put(type.getTypeID(), portableSkCase.getAttributeType(type.getLabel()));
330  } catch (TskCoreException ex) {
331  handleError("Error looking up attribute name " + type.getLabel(),
332  Bundle.PortableCaseReportModule_generateReport_errorLookingUpAttrType(type.getLabel()),
333  ex, progressPanel); // NON-NLS
334  }
335  }
336 
337  // Copy the tagged files
338  try {
339  for (TagName tagName : tagNames) {
340  // Check for cancellation
341  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
342  handleCancellation(progressPanel);
343  return;
344  }
345  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingFiles(tagName.getDisplayName()));
346  addFilesToPortableCase(tagName, progressPanel);
347 
348  // Check for cancellation
349  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
350  handleCancellation(progressPanel);
351  return;
352  }
353  }
354  } catch (TskCoreException ex) {
355  handleError("Error copying tagged files", Bundle.PortableCaseReportModule_generateReport_errorCopyingFiles(), ex, progressPanel); // NON-NLS
356  return;
357  }
358 
359  // Copy the tagged artifacts and associated files
360  try {
361  for (TagName tagName : tagNames) {
362  // Check for cancellation
363  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
364  handleCancellation(progressPanel);
365  return;
366  }
367  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingArtifacts(tagName.getDisplayName()));
368  addArtifactsToPortableCase(tagName, progressPanel);
369 
370  // Check for cancellation
371  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
372  handleCancellation(progressPanel);
373  return;
374  }
375  }
376  } catch (TskCoreException ex) {
377  handleError("Error copying tagged artifacts", Bundle.PortableCaseReportModule_generateReport_errorCopyingArtifacts(), ex, progressPanel); // NON-NLS
378  return;
379  }
380 
381  // Copy interesting files and results
382  if (!setNames.isEmpty()) {
383  try {
384  List<BlackboardArtifact> interestingFiles = currentCase.getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
385  for (BlackboardArtifact art : interestingFiles) {
386  // Check for cancellation
387  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
388  handleCancellation(progressPanel);
389  return;
390  }
391 
392  BlackboardAttribute setAttr = art.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
393  if (setNames.contains(setAttr.getValueString())) {
394  copyContentToPortableCase(art, progressPanel);
395  }
396  }
397  } catch (TskCoreException ex) {
398  handleError("Error copying interesting files", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingFiles(), ex, progressPanel); // NON-NLS
399  return;
400  }
401 
402  try {
403  List<BlackboardArtifact> interestingResults = currentCase.getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT);
404  for (BlackboardArtifact art : interestingResults) {
405  // Check for cancellation
406  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
407  handleCancellation(progressPanel);
408  return;
409  }
410  BlackboardAttribute setAttr = art.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
411  if (setNames.contains(setAttr.getValueString())) {
412  copyContentToPortableCase(art, progressPanel);
413  }
414  }
415  } catch (TskCoreException ex) {
416  handleError("Error copying interesting results", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingResults(), ex, progressPanel); // NON-NLS
417  return;
418  }
419  }
420 
421  // Check for cancellation
422  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
423  handleCancellation(progressPanel);
424  return;
425  }
426 
427  //Attempt to generate and included the CASE-UCO report.
428  generateCaseUcoReport(tagNames, setNames, progressPanel);
429 
430  if (options.includeApplication()) {
431  try {
432  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingAutopsy());
433  copyApplication(getApplicationBasePath(), outputDir.getAbsolutePath());
434  createAppLaunchBatFile(outputDir.getAbsolutePath());
435  } catch (IOException ex) {
436  handleError("Error copying autopsy", Bundle.PortableCaseReportModule_generateReport_errorCopyingAutopsy(), ex, progressPanel); // NON-NLS
437  }
438  }
439 
440  // Compress the case (if desired)
441  if (options.shouldCompress()) {
442  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_compressingCase());
443 
444  if(!compressCase(progressPanel, options.includeApplication() ? outputDir.getAbsolutePath() : caseFolder.getAbsolutePath())){
445  // Errors have been handled already
446  return;
447  }
448 
449  // Check for cancellation
450  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
451  handleCancellation(progressPanel);
452  return;
453  }
454  }
455 
456  // Close the case connections and clear out the maps
457  cleanup();
458 
460 
461  }
462 
474  @NbBundle.Messages({
475  "PortableCaseReportModule.generateCaseUcoReport.errorCreatingReportFolder=Could not make report folder",
476  "PortableCaseReportModule.generateCaseUcoReport.errorGeneratingCaseUcoReport=Problem while generating CASE-UCO report",
477  "PortableCaseReportModule.generateCaseUcoReport.startCaseUcoReportGeneration=Creating a CASE-UCO report of the portable case",
478  "PortableCaseReportModule.generateCaseUcoReport.successCaseUcoReportGeneration=Successfully created a CASE-UCO report of the portable case"
479  })
480  private void generateCaseUcoReport(List<TagName> tagNames, List<String> setNames, ReportProgressPanel progressPanel) {
481  //Create the 'Reports' directory to include a CASE-UCO report.
482  Path reportsDirectory = Paths.get(caseFolder.toString(), "Reports");
483  if (!reportsDirectory.toFile().mkdir()) {
484  logger.log(Level.SEVERE, "Could not make the report folder... skipping "
485  + "CASE-UCO report generation for the portable case");
486  return;
487  }
488 
489  Path reportFile = reportsDirectory.resolve(CASE_UCO_FILE_NAME);
490 
491  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_startCaseUcoReportGeneration());
492  try (OutputStream stream = new FileOutputStream(reportFile.toFile());
493  JsonWriter reportWriter = new JsonWriter(new OutputStreamWriter(stream, "UTF-8"))) {
494  Gson gson = new GsonBuilder().setPrettyPrinting().create();
495  reportWriter.setIndent(" ");
496  reportWriter.beginObject();
497  reportWriter.name("@graph");
498  reportWriter.beginArray();
499 
500  String caseTempDirectory = currentCase.getTempDirectory();
501  SleuthkitCase skCase = currentCase.getSleuthkitCase();
502  TagsManager tagsManager = currentCase.getServices().getTagsManager();
503 
504  //Create temp directory to filter out duplicate files.
505  //Clear out the old directory if it exists.
506  Path tmpDir = Paths.get(caseTempDirectory, CASE_UCO_TMP_DIR);
507  FileUtils.deleteDirectory(tmpDir.toFile());
508  Files.createDirectory(tmpDir);
509 
510  CaseUcoExporter exporter = new CaseUcoExporter(currentCase.getSleuthkitCase());
511  for (JsonElement element : exporter.exportSleuthkitCase()) {
512  gson.toJson(element, reportWriter);
513  }
514 
515  //Load all interesting BlackboardArtifacts that belong to the selected SET_NAMEs
516  //binned by data source id.
517  Multimap<Long, BlackboardArtifact> artifactsWithSetName = getInterestingArtifactsBySetName(skCase, setNames);
518 
519  //Search each data source looking for content tags and interesting
520  //items that match the selected tag names and set names.
521  for (DataSource dataSource : currentCase.getSleuthkitCase().getDataSources()) {
522  // Helper flag to ensure each data source is only written once in
523  // a report.
524  boolean dataSourceHasBeenIncluded = false;
525 
526  //Search content tags and artifact tags that match
527  for (TagName tagName : tagNames) {
528  for (ContentTag ct : tagsManager.getContentTagsByTagName(tagName, dataSource.getId())) {
529  dataSourceHasBeenIncluded |= addUniqueFile(ct.getContent(),
530  dataSource, tmpDir, gson, exporter, reportWriter, dataSourceHasBeenIncluded);
531  }
532  for (BlackboardArtifactTag bat : tagsManager.getBlackboardArtifactTagsByTagName(tagName, dataSource.getId())) {
533  dataSourceHasBeenIncluded |= addUniqueFile(bat.getContent(),
534  dataSource, tmpDir, gson, exporter, reportWriter, dataSourceHasBeenIncluded);
535  }
536  }
537  //Search artifacts that this data source contains
538  for (BlackboardArtifact bArt : artifactsWithSetName.get(dataSource.getId())) {
539  Content sourceContent = bArt.getParent();
540  dataSourceHasBeenIncluded |= addUniqueFile(sourceContent, dataSource,
541  tmpDir, gson, exporter, reportWriter, dataSourceHasBeenIncluded);
542  }
543  }
544 
545  // Finish the report.
546  reportWriter.endArray();
547  reportWriter.endObject();
548  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_successCaseUcoReportGeneration());
549  } catch (IOException | TskCoreException ex) {
550  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_errorGeneratingCaseUcoReport());
551  logger.log(Level.SEVERE, "Error encountered while trying to create "
552  + "CASE-UCO output for portable case.. the portable case will be "
553  + "completed without a CASE-UCO report.", ex);
554  }
555  }
556 
562  private Multimap<Long, BlackboardArtifact> getInterestingArtifactsBySetName(SleuthkitCase skCase, List<String> setNames) throws TskCoreException {
563  Multimap<Long, BlackboardArtifact> artifactsWithSetName = ArrayListMultimap.create();
564  if (!setNames.isEmpty()) {
565  List<BlackboardArtifact> allArtifacts = skCase.getBlackboardArtifacts(
566  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
567  allArtifacts.addAll(skCase.getBlackboardArtifacts(
568  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT));
569 
570  for (BlackboardArtifact bArt : allArtifacts) {
571  BlackboardAttribute setAttr = bArt.getAttribute(
572  new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
573  if (setNames.contains(setAttr.getValueString())) {
574  artifactsWithSetName.put(bArt.getDataSource().getId(), bArt);
575  }
576  }
577  }
578  return artifactsWithSetName;
579  }
580 
601  private boolean addUniqueFile(Content content, DataSource dataSource,
602  Path tmpDir, Gson gson, CaseUcoExporter exporter, JsonWriter reportWriter,
603  boolean dataSourceHasBeenIncluded) throws IOException, TskCoreException {
604  if (content instanceof AbstractFile && !(content instanceof DataSource)) {
605  AbstractFile absFile = (AbstractFile) content;
606  Path filePath = tmpDir.resolve(Long.toString(absFile.getId()));
607  if (!absFile.isDir() && !Files.exists(filePath)) {
608  if (!dataSourceHasBeenIncluded) {
609  for (JsonElement element : exporter.exportDataSource(dataSource)) {
610  gson.toJson(element, reportWriter);
611  }
612  }
613  String subFolder = getExportSubfolder(absFile);
614  String fileName = absFile.getId() + "-" + FileUtil.escapeFileName(absFile.getName());
615  for (JsonElement element : exporter.exportAbstractFile(absFile, Paths.get(FILE_FOLDER_NAME, subFolder, fileName).toString())) {
616  gson.toJson(element, reportWriter);
617  }
618  Files.createFile(filePath);
619  return true;
620  }
621  }
622  return false;
623  }
624 
625  private List<String> getAllInterestingItemsSets() throws NoCurrentCaseException, TskCoreException {
626 
627  // Get the set names in use for the current case.
628  List<String> setNames = new ArrayList<>();
629  Map<String, Long> setCounts;
630 
631  // There may not be a case open when configuring report modules for Command Line execution
632  // Get all SET_NAMEs from interesting item artifacts
633  String innerSelect = "SELECT (value_text) AS set_name FROM blackboard_attributes WHERE (artifact_type_id = '"
634  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + "' OR artifact_type_id = '"
635  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() + "') AND attribute_type_id = '"
636  + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + "'"; // NON-NLS
637 
638  // Get the count of each SET_NAME
639  String query = "set_name, count(1) AS set_count FROM (" + innerSelect + ") set_names GROUP BY set_name"; // NON-NLS
640 
642  Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select(query, callback);
643  setCounts = callback.getSetCountMap();
644  setNames.addAll(setCounts.keySet());
645  return setNames;
646  }
647 
655  @NbBundle.Messages({
656  "# {0} - case folder",
657  "PortableCaseReportModule.createCase.caseDirExists=Case folder {0} already exists",
658  "PortableCaseReportModule.createCase.errorCreatingCase=Error creating case",
659  "# {0} - folder",
660  "PortableCaseReportModule.createCase.errorCreatingFolder=Error creating folder {0}",
661  "PortableCaseReportModule.createCase.errorStoringMaxIds=Error storing maximum database IDs",})
662  private void createCase(File outputDir, ReportProgressPanel progressPanel) {
663 
664  // Create the case folder
665  caseFolder = Paths.get(outputDir.toString(), caseName).toFile();
666 
667  if (caseFolder.exists()) {
668  handleError("Case folder " + caseFolder.toString() + " already exists",
669  Bundle.PortableCaseReportModule_createCase_caseDirExists(caseFolder.toString()), null, progressPanel); // NON-NLS
670  return;
671  }
672 
673  // Create the case
674  try {
675  portableSkCase = currentCase.createPortableCase(caseName, caseFolder);
676  } catch (TskCoreException ex) {
677  handleError("Error creating case " + caseName + " in folder " + caseFolder.toString(),
678  Bundle.PortableCaseReportModule_createCase_errorCreatingCase(), ex, progressPanel); // NON-NLS
679  return;
680  }
681 
682  // Store the highest IDs
683  try {
684  saveHighestIds();
685  } catch (TskCoreException ex) {
686  handleError("Error storing maximum database IDs",
687  Bundle.PortableCaseReportModule_createCase_errorStoringMaxIds(), ex, progressPanel); // NON-NLS
688  return;
689  }
690 
691  // Create the base folder for the copied files
692  copiedFilesFolder = Paths.get(caseFolder.toString(), FILE_FOLDER_NAME).toFile();
693  if (!copiedFilesFolder.mkdir()) {
694  handleError("Error creating folder " + copiedFilesFolder.toString(),
695  Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(copiedFilesFolder.toString()), null, progressPanel); // NON-NLS
696  return;
697  }
698 
699  // Create subfolders for the copied files
700  for (FileTypeCategory cat : FILE_TYPE_CATEGORIES) {
701  File subFolder = Paths.get(copiedFilesFolder.toString(), cat.getDisplayName()).toFile();
702  if (!subFolder.mkdir()) {
703  handleError("Error creating folder " + subFolder.toString(),
704  Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(subFolder.toString()), null, progressPanel); // NON-NLS
705  return;
706  }
707  }
708  File unknownTypeFolder = Paths.get(copiedFilesFolder.toString(), UNKNOWN_FILE_TYPE_FOLDER).toFile();
709  if (!unknownTypeFolder.mkdir()) {
710  handleError("Error creating folder " + unknownTypeFolder.toString(),
711  Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(unknownTypeFolder.toString()), null, progressPanel); // NON-NLS
712  return;
713  }
714 
715  }
716 
722  private void saveHighestIds() throws TskCoreException {
723 
724  CaseDbAccessManager currentCaseDbManager = currentCase.getSleuthkitCase().getCaseDbAccessManager();
725 
726  String tableSchema = "( table_name TEXT PRIMARY KEY, "
727  + " max_id TEXT)"; // NON-NLS
728 
729  portableSkCase.getCaseDbAccessManager().createTable(MAX_ID_TABLE_NAME, tableSchema);
730 
731  currentCaseDbManager.select("max(obj_id) as max_id from tsk_objects", new StoreMaxIdCallback("tsk_objects")); // NON-NLS
732  currentCaseDbManager.select("max(tag_id) as max_id from content_tags", new StoreMaxIdCallback("content_tags")); // NON-NLS
733  currentCaseDbManager.select("max(tag_id) as max_id from blackboard_artifact_tags", new StoreMaxIdCallback("blackboard_artifact_tags")); // NON-NLS
734  currentCaseDbManager.select("max(examiner_id) as max_id from tsk_examiners", new StoreMaxIdCallback("tsk_examiners")); // NON-NLS
735  }
736 
744  private void initializeImageTags(ReportProgressPanel progressPanel) throws TskCoreException {
745 
746  // Create the image tags table in the portable case
747  CaseDbAccessManager portableDbAccessManager = portableSkCase.getCaseDbAccessManager();
748  if (!portableDbAccessManager.tableExists(ContentViewerTagManager.TABLE_NAME)) {
750  }
751  }
752 
761  private void addFilesToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel) throws TskCoreException {
762 
763  // Get all the tags in the current case
764  List<ContentTag> tags = currentCase.getServices().getTagsManager().getContentTagsByTagName(oldTagName);
765 
766  // Copy the files into the portable case and tag
767  for (ContentTag tag : tags) {
768 
769  // Check for cancellation
770  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
771  return;
772  }
773 
774  Content content = tag.getContent();
775  if (content instanceof AbstractFile) {
776 
777  long newFileId = copyContentToPortableCase(content, progressPanel);
778 
779  // Tag the file
780  if (!oldTagNameToNewTagName.containsKey(tag.getName())) {
781  throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS
782  }
783  ContentTagChange newContentTag = portableSkCase.getTaggingManager().addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
784 
785  // Get the image tag data associated with this tag (empty string if there is none)
786  // and save it if present
787  String appData = getImageTagDataForContentTag(tag);
788  if (!appData.isEmpty()) {
789  addImageTagToPortableCase(newContentTag.getAddedTag(), appData);
790  }
791  }
792  }
793  }
794 
805  private String getImageTagDataForContentTag(ContentTag tag) throws TskCoreException {
806 
807  GetImageTagCallback callback = new GetImageTagCallback();
808  String query = "* FROM " + ContentViewerTagManager.TABLE_NAME + " WHERE content_tag_id = " + tag.getId();
809  currentCase.getSleuthkitCase().getCaseDbAccessManager().select(query, callback);
810  return callback.getAppData();
811  }
812 
816  private static class GetImageTagCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
817 
818  private static final Logger logger = Logger.getLogger(PortableCaseReportModule.class.getName());
819  private String appData = "";
820 
821  @Override
822  public void process(ResultSet rs) {
823  try {
824  while (rs.next()) {
825  try {
826  appData = rs.getString("app_data"); // NON-NLS
827  } catch (SQLException ex) {
828  logger.log(Level.WARNING, "Unable to get app_data from result set", ex); // NON-NLS
829  }
830  }
831  } catch (SQLException ex) {
832  logger.log(Level.WARNING, "Failed to get next result for app_data", ex); // NON-NLS
833  }
834  }
835 
841  String getAppData() {
842  return appData;
843  }
844  }
845 
854  private void addImageTagToPortableCase(ContentTag newContentTag, String appData) throws TskCoreException {
855  String insert = "(content_tag_id, app_data) VALUES (" + newContentTag.getId() + ", '" + appData + "')";
856  portableSkCase.getCaseDbAccessManager().insert(ContentViewerTagManager.TABLE_NAME, insert);
857  }
858 
867  private void addArtifactsToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel) throws TskCoreException {
868 
869  List<BlackboardArtifactTag> tags = currentCase.getServices().getTagsManager().getBlackboardArtifactTagsByTagName(oldTagName);
870 
871  // Copy the artifacts into the portable case along with their content and tag
872  for (BlackboardArtifactTag tag : tags) {
873 
874  // Check for cancellation
875  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
876  return;
877  }
878 
879  // Copy the source content
880  Content content = tag.getContent();
881  long newContentId = copyContentToPortableCase(content, progressPanel);
882 
883  // Copy the artifact
884  BlackboardArtifact newArtifact = copyArtifact(newContentId, tag.getArtifact());
885 
886  // Tag the artfiact
887  if (!oldTagNameToNewTagName.containsKey(tag.getName())) {
888  throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS
889  }
890  portableSkCase.getTaggingManager().addArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment());
891  }
892  }
893 
906  private BlackboardArtifact copyArtifact(long newContentId, BlackboardArtifact artifactToCopy) throws TskCoreException {
907 
908  if (oldArtifactIdToNewArtifact.containsKey(artifactToCopy.getArtifactID())) {
909  return oldArtifactIdToNewArtifact.get(artifactToCopy.getArtifactID());
910  }
911 
912  // First create the associated artifact (if present)
913  BlackboardAttribute oldAssociatedAttribute = artifactToCopy.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
914  List<BlackboardAttribute> newAttrs = new ArrayList<>();
915  if (oldAssociatedAttribute != null) {
916  BlackboardArtifact oldAssociatedArtifact = currentCase.getSleuthkitCase().getBlackboardArtifact(oldAssociatedAttribute.getValueLong());
917  BlackboardArtifact newAssociatedArtifact = copyArtifact(newContentId, oldAssociatedArtifact);
918  newAttrs.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
919  String.join(",", oldAssociatedAttribute.getSources()), newAssociatedArtifact.getArtifactID()));
920  }
921 
922  // Create the new artifact
923  int newArtifactTypeId = getNewArtifactTypeId(artifactToCopy);
924  BlackboardArtifact newArtifact = portableSkCase.newBlackboardArtifact(newArtifactTypeId, newContentId);
925  List<BlackboardAttribute> oldAttrs = artifactToCopy.getAttributes();
926 
927  // Copy over each attribute, making sure the type is in the new case.
928  for (BlackboardAttribute oldAttr : oldAttrs) {
929 
930  // The associated artifact has already been handled
931  if (oldAttr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
932  continue;
933  }
934 
935  BlackboardAttribute.Type newAttributeType = getNewAttributeType(oldAttr);
936  switch (oldAttr.getValueType()) {
937  case BYTE:
938  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
939  oldAttr.getValueBytes()));
940  break;
941  case DOUBLE:
942  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
943  oldAttr.getValueDouble()));
944  break;
945  case INTEGER:
946  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
947  oldAttr.getValueInt()));
948  break;
949  case DATETIME:
950  case LONG:
951  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
952  oldAttr.getValueLong()));
953  break;
954  case STRING:
955  case JSON:
956  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
957  oldAttr.getValueString()));
958  break;
959  default:
960  throw new TskCoreException("Unexpected attribute value type found: " + oldAttr.getValueType().getLabel()); // NON-NLS
961  }
962  }
963 
964  newArtifact.addAttributes(newAttrs);
965 
966  oldArtifactIdToNewArtifact.put(artifactToCopy.getArtifactID(), newArtifact);
967  return newArtifact;
968  }
969 
979  private int getNewArtifactTypeId(BlackboardArtifact oldArtifact) throws TskCoreException {
980  if (oldArtTypeIdToNewArtTypeId.containsKey(oldArtifact.getArtifactTypeID())) {
981  return oldArtTypeIdToNewArtTypeId.get(oldArtifact.getArtifactTypeID());
982  }
983 
984  BlackboardArtifact.Type oldCustomType = currentCase.getSleuthkitCase().getArtifactType(oldArtifact.getArtifactTypeName());
985  try {
986  BlackboardArtifact.Type newCustomType = portableSkCase.addBlackboardArtifactType(oldCustomType.getTypeName(), oldCustomType.getDisplayName());
987  oldArtTypeIdToNewArtTypeId.put(oldArtifact.getArtifactTypeID(), newCustomType.getTypeID());
988  return newCustomType.getTypeID();
989  } catch (TskDataException ex) {
990  throw new TskCoreException("Error creating new artifact type " + oldCustomType.getTypeName(), ex); // NON-NLS
991  }
992  }
993 
1003  private BlackboardAttribute.Type getNewAttributeType(BlackboardAttribute oldAttribute) throws TskCoreException {
1004  BlackboardAttribute.Type oldAttrType = oldAttribute.getAttributeType();
1005  if (oldAttrTypeIdToNewAttrType.containsKey(oldAttrType.getTypeID())) {
1006  return oldAttrTypeIdToNewAttrType.get(oldAttrType.getTypeID());
1007  }
1008 
1009  try {
1010  BlackboardAttribute.Type newCustomType = portableSkCase.addArtifactAttributeType(oldAttrType.getTypeName(),
1011  oldAttrType.getValueType(), oldAttrType.getDisplayName());
1012  oldAttrTypeIdToNewAttrType.put(oldAttribute.getAttributeType().getTypeID(), newCustomType);
1013  return newCustomType;
1014  } catch (TskDataException ex) {
1015  throw new TskCoreException("Error creating new attribute type " + oldAttrType.getTypeName(), ex); // NON-NLS
1016  }
1017  }
1018 
1029  @NbBundle.Messages({
1030  "# {0} - File name",
1031  "PortableCaseReportModule.copyContentToPortableCase.copyingFile=Copying file {0}",})
1032  private long copyContentToPortableCase(Content content, ReportProgressPanel progressPanel) throws TskCoreException {
1033  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_copyContentToPortableCase_copyingFile(content.getUniquePath()));
1034  return copyContent(content);
1035  }
1036 
1046  private long copyContent(Content content) throws TskCoreException {
1047 
1048  // Check if we've already copied this content
1049  if (oldIdToNewContent.containsKey(content.getId())) {
1050  return oldIdToNewContent.get(content.getId()).getId();
1051  }
1052 
1053  // Otherwise:
1054  // - Make parent of this object (if applicable)
1055  // - Copy this content
1056  long parentId = 0;
1057  if (content.getParent() != null) {
1058  parentId = copyContent(content.getParent());
1059  }
1060 
1061  Content newContent;
1062  if (content instanceof BlackboardArtifact) {
1063  BlackboardArtifact artifactToCopy = (BlackboardArtifact) content;
1064  newContent = copyArtifact(parentId, artifactToCopy);
1065  } else {
1066  CaseDbTransaction trans = portableSkCase.beginTransaction();
1067  try {
1068  if (content instanceof Image) {
1069  Image image = (Image) content;
1070  newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(),
1071  new ArrayList<>(), image.getTimeZone(), image.getMd5(), image.getSha1(), image.getSha256(), image.getDeviceId(), trans);
1072  } else if (content instanceof VolumeSystem) {
1073  VolumeSystem vs = (VolumeSystem) content;
1074  newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
1075  } else if (content instanceof Volume) {
1076  Volume vs = (Volume) content;
1077  newContent = portableSkCase.addVolume(parentId, vs.getAddr(), vs.getStart(), vs.getLength(),
1078  vs.getDescription(), vs.getFlags(), trans);
1079  } else if (content instanceof Pool) {
1080  Pool pool = (Pool) content;
1081  newContent = portableSkCase.addPool(parentId, pool.getType(), trans);
1082  } else if (content instanceof FileSystem) {
1083  FileSystem fs = (FileSystem) content;
1084  newContent = portableSkCase.addFileSystem(parentId, fs.getImageOffset(), fs.getFsType(), fs.getBlock_size(),
1085  fs.getBlock_count(), fs.getRoot_inum(), fs.getFirst_inum(), fs.getLastInum(),
1086  fs.getName(), trans);
1087  } else if (content instanceof BlackboardArtifact) {
1088  BlackboardArtifact artifactToCopy = (BlackboardArtifact) content;
1089  newContent = copyArtifact(parentId, artifactToCopy);
1090  } else if (content instanceof AbstractFile) {
1091  AbstractFile abstractFile = (AbstractFile) content;
1092 
1093  if (abstractFile instanceof LocalFilesDataSource) {
1094  LocalFilesDataSource localFilesDS = (LocalFilesDataSource) abstractFile;
1095  newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), trans);
1096  } else {
1097  if (abstractFile.isDir()) {
1098  newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
1099  } else {
1100  try {
1101  // Copy the file
1102  String fileName = abstractFile.getId() + "-" + FileUtil.escapeFileName(abstractFile.getName());
1103  String exportSubFolder = getExportSubfolder(abstractFile);
1104  File exportFolder = Paths.get(copiedFilesFolder.toString(), exportSubFolder).toFile();
1105  File localFile = new File(exportFolder, fileName);
1106  ContentUtils.writeToFile(abstractFile, localFile);
1107 
1108  // Get the new parent object in the portable case database
1109  Content oldParent = abstractFile.getParent();
1110  if (!oldIdToNewContent.containsKey(oldParent.getId())) {
1111  throw new TskCoreException("Parent of file with ID " + abstractFile.getId() + " has not been created"); // NON-NLS
1112  }
1113  Content newParent = oldIdToNewContent.get(oldParent.getId());
1114 
1115  // Construct the relative path to the copied file
1116  String relativePath = FILE_FOLDER_NAME + File.separator + exportSubFolder + File.separator + fileName;
1117 
1118  newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
1119  abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getMtime(),
1120  abstractFile.getMd5Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
1121  true, TskData.EncodingType.NONE,
1122  newParent, trans);
1123  } catch (IOException ex) {
1124  throw new TskCoreException("Error copying file " + abstractFile.getName() + " with original obj ID "
1125  + abstractFile.getId(), ex); // NON-NLS
1126  }
1127  }
1128  }
1129  } else {
1130  throw new TskCoreException("Trying to copy unexpected Content type " + content.getClass().getName()); // NON-NLS
1131  }
1132  trans.commit();
1133  } catch (TskCoreException ex) {
1134  trans.rollback();
1135  throw (ex);
1136  }
1137  }
1138 
1139  // Save the new object
1140  oldIdToNewContent.put(content.getId(), newContent);
1141  newIdToContent.put(newContent.getId(), newContent);
1142  return oldIdToNewContent.get(content.getId()).getId();
1143  }
1144 
1152  private String getExportSubfolder(AbstractFile abstractFile) {
1153  if (abstractFile.getMIMEType() == null || abstractFile.getMIMEType().isEmpty()) {
1154  return UNKNOWN_FILE_TYPE_FOLDER;
1155  }
1156 
1157  for (FileTypeCategory cat : FILE_TYPE_CATEGORIES) {
1158  if (cat.getMediaTypes().contains(abstractFile.getMIMEType())) {
1159  return cat.getDisplayName();
1160  }
1161  }
1162  return UNKNOWN_FILE_TYPE_FOLDER;
1163  }
1164 
1170  private Path getApplicationBasePath() {
1171  return getAutopsyExePath().getParent().getParent();
1172  }
1173 
1179  private Path getAutopsyExePath() {
1180  // If this is an installed version, there should be an <appName>64.exe file in the bin folder
1181  String exeName = getAutopsyExeName();
1182  String installPath = PlatformUtil.getInstallPath();
1183 
1184  return Paths.get(installPath, "bin", exeName);
1185  }
1186 
1192  private String getAutopsyExeName() {
1193  String appName = UserPreferences.getAppName();
1194  return appName + "64.exe";
1195  }
1196 
1205  private void copyApplication(Path sourceFolder, String destBaseFolder) throws IOException {
1206 
1207  // Create an appName folder in the destination
1208  Path destAppFolder = Paths.get(destBaseFolder, UserPreferences.getAppName());
1209  if (!destAppFolder.toFile().exists() && !destAppFolder.toFile().mkdirs()) {
1210  throw new IOException("Failed to create directory " + destAppFolder.toString());
1211  }
1212 
1213  // Now copy the files
1214  FileUtils.copyDirectory(sourceFolder.toFile(), destAppFolder.toFile());
1215  }
1216 
1224  private void createAppLaunchBatFile(String destBaseFolder) throws IOException {
1225  Path filePath = Paths.get(destBaseFolder, "open.bat");
1226  String appName = UserPreferences.getAppName();
1227  String exePath = "\"%~dp0" + appName + "\\bin\\" + getAutopsyExeName() + "\"";
1228  String casePath = "..\\" + caseName;
1229  try (FileWriter writer = new FileWriter(filePath.toFile())) {
1230  writer.write(exePath + " \"" + casePath + "\"");
1231  }
1232  }
1233 
1237  private void cleanup() {
1238  oldIdToNewContent.clear();
1239  newIdToContent.clear();
1240  oldTagNameToNewTagName.clear();
1241  oldArtTypeIdToNewArtTypeId.clear();
1243  oldArtifactIdToNewArtifact.clear();
1244 
1246 
1247  currentCase = null;
1248  caseFolder = null;
1249  copiedFilesFolder = null;
1250  }
1251 
1255  private void closePortableCaseDatabase() {
1256  if (portableSkCase != null) {
1257  portableSkCase.close();
1258  portableSkCase = null;
1259  }
1260  }
1261 
1262  /*
1263  * @Override public JPanel getConfigurationPanel() { configPanel = new
1264  * CreatePortableCasePanel(); return configPanel; }
1265  */
1266  private class StoreMaxIdCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
1267 
1268  private final String tableName;
1269 
1270  StoreMaxIdCallback(String tableName) {
1271  this.tableName = tableName;
1272  }
1273 
1274  @Override
1275  public void process(ResultSet rs) {
1276 
1277  try {
1278  while (rs.next()) {
1279  try {
1280  Long maxId = rs.getLong("max_id"); // NON-NLS
1281  String query = " (table_name, max_id) VALUES ('" + tableName + "', '" + maxId + "')"; // NON-NLS
1282  portableSkCase.getCaseDbAccessManager().insert(MAX_ID_TABLE_NAME, query);
1283 
1284  } catch (SQLException ex) {
1285  logger.log(Level.WARNING, "Unable to get maximum ID from result set", ex); // NON-NLS
1286  } catch (TskCoreException ex) {
1287  logger.log(Level.WARNING, "Unable to save maximum ID from result set", ex); // NON-NLS
1288  }
1289 
1290  }
1291  } catch (SQLException ex) {
1292  logger.log(Level.WARNING, "Failed to get maximum ID from result set", ex); // NON-NLS
1293  }
1294  }
1295  }
1296 
1297  @NbBundle.Messages({
1298  "PortableCaseReportModule.compressCase.errorFinding7zip=Could not locate 7-Zip executable",
1299  "# {0} - Temp folder path",
1300  "PortableCaseReportModule.compressCase.errorCreatingTempFolder=Could not create temporary folder {0}",
1301  "PortableCaseReportModule.compressCase.errorCompressingCase=Error compressing case",
1302  "PortableCaseReportModule.compressCase.canceled=Compression canceled by user",})
1303  private boolean compressCase(ReportProgressPanel progressPanel, String folderToCompress) {
1304 
1306 
1307  // Make a temporary folder for the compressed case
1308  Path dirToCompress = Paths.get(folderToCompress);
1309  File tempZipFolder = Paths.get(dirToCompress.getParent().toString(), "temp", "portableCase" + System.currentTimeMillis()).toFile();
1310  if (!tempZipFolder.mkdirs()) {
1311  handleError("Error creating temporary folder " + tempZipFolder.toString(),
1312  Bundle.PortableCaseReportModule_compressCase_errorCreatingTempFolder(tempZipFolder.toString()), null, progressPanel); // NON-NLS
1313  return false;
1314  }
1315 
1316  // Find 7-Zip
1317  File sevenZipExe = locate7ZipExecutable();
1318  if (sevenZipExe == null) {
1319  handleError("Error finding 7-Zip exectuable", Bundle.PortableCaseReportModule_compressCase_errorFinding7zip(), null, progressPanel); // NON-NLS
1320  return false;
1321  }
1322 
1323  // Create the chunk option
1324  String chunkOption = "";
1326  chunkOption = "-v" + settings.getChunkSize().getSevenZipParam();
1327  }
1328 
1329  File zipFile = Paths.get(tempZipFolder.getAbsolutePath(), caseName + ".zip").toFile(); // NON-NLS
1330  ProcessBuilder procBuilder = new ProcessBuilder();
1331  procBuilder.command(
1332  sevenZipExe.getAbsolutePath(),
1333  "a", // Add to archive
1334  zipFile.getAbsolutePath(),
1335  dirToCompress.toAbsolutePath().toString(),
1336  chunkOption
1337  );
1338 
1339  try {
1340  Process process = procBuilder.start();
1341 
1342  while (process.isAlive()) {
1343  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
1344  process.destroy();
1345  return false;
1346  }
1347  Thread.sleep(200);
1348  }
1349  int exitCode = process.exitValue();
1350  if (exitCode != 0) {
1351  // Save any errors so they can be logged
1352  StringBuilder sb = new StringBuilder();
1353  try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
1354  String line;
1355  while ((line = br.readLine()) != null) {
1356  sb.append(line).append(System.getProperty("line.separator")); // NON-NLS
1357  }
1358  }
1359 
1360  handleError("Error compressing case\n7-Zip output: " + sb.toString(), Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), null, progressPanel); // NON-NLS
1361  return false;
1362  }
1363  } catch (IOException | InterruptedException ex) {
1364  handleError("Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel); // NON-NLS
1365  return false;
1366  }
1367 
1368  // Delete everything in the case folder then copy over the compressed file(s)
1369  try {
1370  FileUtils.cleanDirectory(dirToCompress.toFile());
1371  FileUtils.copyDirectory(tempZipFolder, dirToCompress.toFile());
1372  FileUtils.deleteDirectory(new File(tempZipFolder.getParent()));
1373  } catch (IOException ex) {
1374  handleError("Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel); // NON-NLS
1375  return false;
1376  }
1377 
1378  return true;
1379  }
1380 
1386  private static File locate7ZipExecutable() {
1387  if (!PlatformUtil.isWindowsOS()) {
1388  return null;
1389  }
1390 
1391  String executableToFindName = Paths.get("7-Zip", "7z.exe").toString(); // NON-NLS
1392  File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, PortableCaseReportModule.class.getPackage().getName(), false);
1393  if (null == exeFile) {
1394  return null;
1395  }
1396 
1397  if (!exeFile.canExecute()) {
1398  return null;
1399  }
1400 
1401  return exeFile;
1402  }
1403 
1407  public static class GetInterestingItemSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
1408 
1409  private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(GetInterestingItemSetNamesCallback.class.getName());
1410  private final Map<String, Long> setCounts = new HashMap<>();
1411 
1412  @Override
1413  public void process(ResultSet rs) {
1414  try {
1415  while (rs.next()) {
1416  try {
1417  Long setCount = rs.getLong("set_count"); // NON-NLS
1418  String setName = rs.getString("set_name"); // NON-NLS
1419 
1420  setCounts.put(setName, setCount);
1421 
1422  } catch (SQLException ex) {
1423  logger.log(Level.WARNING, "Unable to get data_source_obj_id or value from result set", ex); // NON-NLS
1424  }
1425  }
1426  } catch (SQLException ex) {
1427  logger.log(Level.WARNING, "Failed to get next result for values by datasource", ex); // NON-NLS
1428  }
1429  }
1430 
1436  public Map<String, Long> getSetCountMap() {
1437  return setCounts;
1438  }
1439  }
1440 }
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:2212
boolean addUniqueFile(Content content, DataSource dataSource, Path tmpDir, Gson gson, CaseUcoExporter exporter, JsonWriter reportWriter, boolean dataSourceHasBeenIncluded)
static String escapeFileName(String fileName)
Definition: FileUtil.java:169
boolean compressCase(ReportProgressPanel progressPanel, String folderToCompress)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
List< ContentTag > getContentTagsByTagName(TagName tagName)

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