Autopsy  4.19.1
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.Collection;
45 import java.util.HashMap;
46 import java.util.List;
47 import java.util.Map;
48 import org.apache.commons.io.FileUtils;
49 import org.openide.modules.InstalledFileLocator;
50 import org.openide.util.NbBundle;
62 import org.sleuthkit.caseuco.CaseUcoExporter;
63 import org.sleuthkit.datamodel.AbstractFile;
64 import org.sleuthkit.datamodel.Account;
65 import org.sleuthkit.datamodel.AnalysisResult;
66 import org.sleuthkit.datamodel.Blackboard.BlackboardException;
67 import org.sleuthkit.datamodel.BlackboardArtifact;
68 import org.sleuthkit.datamodel.BlackboardArtifactTag;
69 import org.sleuthkit.datamodel.BlackboardAttribute;
70 import org.sleuthkit.datamodel.CaseDbAccessManager;
71 import org.sleuthkit.datamodel.Content;
72 import org.sleuthkit.datamodel.ContentTag;
73 import org.sleuthkit.datamodel.DataArtifact;
74 import org.sleuthkit.datamodel.DataSource;
75 import org.sleuthkit.datamodel.FileSystem;
76 import org.sleuthkit.datamodel.Host;
77 import org.sleuthkit.datamodel.Image;
78 import org.sleuthkit.datamodel.LocalFilesDataSource;
79 import org.sleuthkit.datamodel.OsAccount;
80 import org.sleuthkit.datamodel.OsAccountManager;
81 import org.sleuthkit.datamodel.OsAccountManager.NotUserSIDException;
82 import org.sleuthkit.datamodel.OsAccountRealm;
83 import org.sleuthkit.datamodel.OsAccountRealmManager;
84 import org.sleuthkit.datamodel.Pool;
85 import org.sleuthkit.datamodel.Score;
86 import org.sleuthkit.datamodel.SleuthkitCase;
87 import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
88 import org.sleuthkit.datamodel.TagName;
89 import org.sleuthkit.datamodel.TaggingManager.ContentTagChange;
90 import org.sleuthkit.datamodel.TskCoreException;
91 import org.sleuthkit.datamodel.TskData;
92 import org.sleuthkit.datamodel.Volume;
93 import org.sleuthkit.datamodel.VolumeSystem;
94 import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
95 import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
96 import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
97 
101 public class PortableCaseReportModule implements ReportModule {
102 
104  private static final String FILE_FOLDER_NAME = "PortableCaseFiles"; // NON-NLS
105  private static final String UNKNOWN_FILE_TYPE_FOLDER = "Other"; // NON-NLS
106  private static final String MAX_ID_TABLE_NAME = "portable_case_max_ids"; // NON-NLS
107  private static final String CASE_UCO_FILE_NAME = "portable_CASE_UCO_output";
108  private static final String CASE_UCO_TMP_DIR = "case_uco_tmp";
110 
111  // These are the types for the exported file subfolders
112  private static final List<FileTypeCategory> FILE_TYPE_CATEGORIES = Arrays.asList(FileTypeCategory.AUDIO, FileTypeCategory.DOCUMENTS,
114 
115  // These are attribute types that have special handling and should not be copied
116  // into the new artifact directly.
117  private static final List<Integer> SPECIALLY_HANDLED_ATTRS = Arrays.asList(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID(),
118  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID());
119 
120  private Case currentCase = null;
121  private SleuthkitCase portableSkCase = null;
122  private String caseName = "";
123  private File caseFolder = null;
124  private File copiedFilesFolder = null;
125 
126  // Maps old object ID from current case to new object in portable case
127  private final Map<Long, Content> oldIdToNewContent = new HashMap<>();
128 
129  // Maps new object ID to the new object
130  private final Map<Long, Content> newIdToContent = new HashMap<>();
131 
132  // Maps old TagName to new TagName
133  private final Map<TagName, TagName> oldTagNameToNewTagName = new HashMap<>();
134 
135  // Map of old artifact type ID to new artifact type ID. There will only be changes if custom artifact types are present.
136  private final Map<Integer, Integer> oldArtTypeIdToNewArtTypeId = new HashMap<>();
137 
138  // Map of old attribute type ID to new attribute type ID. There will only be changes if custom attr types are present.
139  private final Map<Integer, BlackboardAttribute.Type> oldAttrTypeIdToNewAttrType = new HashMap<>();
140 
141  // Map of old artifact ID to new artifact
142  private final Map<Long, BlackboardArtifact> oldArtifactIdToNewArtifact = new HashMap<>();
143 
144  // Map of old OS account id to new OS account
145  private final Map<Long, OsAccount> oldOsAccountIdToNewOsAccount = new HashMap<>();
146 
147  // Map of old OS account realm id to new OS account ream id
148  private final Map<Long, OsAccountRealm> oldRealmIdToNewRealm = new HashMap<>();
149 
150  // Map of the old host id to the new host
151  private final Map<Long, Host> oldHostIdToNewHost = new HashMap<>();
152 
154  }
155 
156  @NbBundle.Messages({
157  "PortableCaseReportModule.getName.name=Portable Case"
158  })
159  @Override
160  public String getName() {
161  return Bundle.PortableCaseReportModule_getName_name();
162  }
163 
164  @NbBundle.Messages({
165  "PortableCaseReportModule.getDescription.description=Copies selected items to a new single-user case that can be easily shared"
166  })
167  @Override
168  public String getDescription() {
169  return Bundle.PortableCaseReportModule_getDescription_description();
170  }
171 
172  @Override
173  public String getRelativeFilePath() {
174  try {
175  caseName = Case.getCurrentCaseThrows().getDisplayName() + " (Portable)"; // NON-NLS
176  } catch (NoCurrentCaseException ex) {
177  // a case may not be open yet
178  return "";
179  }
180  return caseName;
181  }
182 
188  private void handleCancellation(ReportProgressPanel progressPanel) {
189  logger.log(Level.INFO, "Portable case creation canceled by user"); // NON-NLS
190  progressPanel.setIndeterminate(false);
192  cleanup();
193  }
194 
205  private void handleError(String logWarning, String dialogWarning, Exception ex, ReportProgressPanel progressPanel) {
206  if (ex == null) {
207  logger.log(Level.WARNING, logWarning);
208  } else {
209  logger.log(Level.SEVERE, logWarning, ex);
210  }
211  progressPanel.setIndeterminate(false);
212  progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, dialogWarning);
213  cleanup();
214  }
215 
216  @NbBundle.Messages({
217  "PortableCaseReportModule.generateReport.verifying=Verifying selected parameters...",
218  "PortableCaseReportModule.generateReport.creatingCase=Creating portable case database...",
219  "PortableCaseReportModule.generateReport.copyingTags=Copying tags...",
220  "# {0} - tag name",
221  "PortableCaseReportModule.generateReport.copyingFiles=Copying files tagged as {0}...",
222  "# {0} - tag name",
223  "PortableCaseReportModule.generateReport.copyingArtifacts=Copying artifacts tagged as {0}...",
224  "# {0} - output folder",
225  "PortableCaseReportModule.generateReport.outputDirDoesNotExist=Output folder {0} does not exist",
226  "# {0} - output folder",
227  "PortableCaseReportModule.generateReport.outputDirIsNotDir=Output folder {0} is not a folder",
228  "PortableCaseReportModule.generateReport.caseClosed=Current case has been closed",
229  "PortableCaseReportModule.generateReport.interestingItemError=Error loading intersting items",
230  "PortableCaseReportModule.generateReport.errorReadingTags=Error while reading tags from case database",
231  "PortableCaseReportModule.generateReport.errorReadingSets=Error while reading interesting items sets from case database",
232  "PortableCaseReportModule.generateReport.noContentToCopy=No interesting files, results, or tagged items to copy",
233  "PortableCaseReportModule.generateReport.errorCopyingTags=Error copying tags",
234  "PortableCaseReportModule.generateReport.errorCopyingFiles=Error copying tagged files",
235  "PortableCaseReportModule.generateReport.errorCopyingArtifacts=Error copying tagged artifacts",
236  "PortableCaseReportModule.generateReport.errorCopyingInterestingFiles=Error copying interesting files",
237  "PortableCaseReportModule.generateReport.errorCopyingInterestingResults=Error copying interesting results",
238  "PortableCaseReportModule.generateReport.errorCreatingImageTagTable=Error creating image tags table",
239  "PortableCaseReportModule.generateReport.errorCopyingAutopsy=Error copying application",
240  "# {0} - attribute type name",
241  "PortableCaseReportModule.generateReport.errorLookingUpAttrType=Error looking up attribute type {0}",
242  "PortableCaseReportModule.generateReport.compressingCase=Compressing case...",
243  "PortableCaseReportModule_generateReport_copyingAutopsy=Copying application..."
244  })
245 
246  public void generateReport(String reportPath, PortableCaseReportModuleSettings options, ReportProgressPanel progressPanel) {
247  this.settings = options;
248  progressPanel.setIndeterminate(true);
249  progressPanel.start();
250  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_verifying());
251 
252  // Clear out any old values
253  cleanup();
254 
255  // Validate the input parameters
256  File outputDir = new File(reportPath);
257  if (!outputDir.exists()) {
258  handleError("Output folder " + outputDir.toString() + " does not exist",
259  Bundle.PortableCaseReportModule_generateReport_outputDirDoesNotExist(outputDir.toString()), null, progressPanel); // NON-NLS
260  return;
261  }
262 
263  if (!outputDir.isDirectory()) {
264  handleError("Output folder " + outputDir.toString() + " is not a folder",
265  Bundle.PortableCaseReportModule_generateReport_outputDirIsNotDir(outputDir.toString()), null, progressPanel); // NON-NLS
266  return;
267  }
268 
269  // Save the current case object
270  try {
271  currentCase = Case.getCurrentCaseThrows();
272  caseName = currentCase.getDisplayName() + " (Portable)"; // NON-NLS
273  } catch (NoCurrentCaseException ex) {
274  handleError("Current case has been closed",
275  Bundle.PortableCaseReportModule_generateReport_caseClosed(), null, progressPanel); // NON-NLS
276  return;
277  }
278 
279  // If the applciation is included add an extra level to the directory structure
280  if (options.includeApplication()) {
281  outputDir = Paths.get(outputDir.toString(), caseName).toFile();
282  }
283  // Check that there will be something to copy
284  List<TagName> tagNames;
285  if (options.areAllTagsSelected()) {
286  try {
288  } catch (NoCurrentCaseException | TskCoreException ex) {
289  handleError("Unable to get all tags",
290  Bundle.PortableCaseReportModule_generateReport_errorReadingTags(), ex, progressPanel); // NON-NLS
291  return;
292  }
293  } else {
294  tagNames = options.getSelectedTagNames();
295  }
296 
297  List<String> setNames;
298  if (options.areAllSetsSelected()) {
299  try {
300  setNames = getAllInterestingItemsSets();
301  } catch (NoCurrentCaseException | TskCoreException ex) {
302  handleError("Unable to get all interesting items sets",
303  Bundle.PortableCaseReportModule_generateReport_errorReadingSets(), ex, progressPanel); // NON-NLS
304  return;
305  }
306  } else {
307  setNames = options.getSelectedSetNames();
308  }
309 
310  if (tagNames.isEmpty() && setNames.isEmpty()) {
311  handleError("No content to copy",
312  Bundle.PortableCaseReportModule_generateReport_noContentToCopy(), null, progressPanel); // NON-NLS
313  return;
314  }
315 
316  // Create the case.
317  // portableSkCase and caseFolder will be set here.
318  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_creatingCase());
319  createCase(outputDir, progressPanel);
320  if (portableSkCase == null) {
321  // The error has already been handled
322  return;
323  }
324 
325  // Check for cancellation
326  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
327  handleCancellation(progressPanel);
328  return;
329  }
330 
331  // Set up the table for the image tags
332  try {
333  initializeImageTags(progressPanel);
334  } catch (TskCoreException ex) {
335  handleError("Error creating image tag table", Bundle.PortableCaseReportModule_generateReport_errorCreatingImageTagTable(), ex, progressPanel); // NON-NLS
336  return;
337  }
338 
339  // Copy the selected tags
340  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingTags());
341  try {
342  for (TagName tagName : tagNames) {
343  TagName newTagName = portableSkCase.getTaggingManager().addOrUpdateTagName(tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus());
344  oldTagNameToNewTagName.put(tagName, newTagName);
345  }
346  } catch (TskCoreException ex) {
347  handleError("Error copying tags", Bundle.PortableCaseReportModule_generateReport_errorCopyingTags(), ex, progressPanel); // NON-NLS
348  return;
349  }
350 
351  // Set up tracking to support any custom artifact or attribute types
352  for (BlackboardArtifact.ARTIFACT_TYPE type : BlackboardArtifact.ARTIFACT_TYPE.values()) {
353  oldArtTypeIdToNewArtTypeId.put(type.getTypeID(), type.getTypeID());
354  }
355  for (BlackboardAttribute.ATTRIBUTE_TYPE type : BlackboardAttribute.ATTRIBUTE_TYPE.values()) {
356  try {
357  oldAttrTypeIdToNewAttrType.put(type.getTypeID(), portableSkCase.getAttributeType(type.getLabel()));
358  } catch (TskCoreException ex) {
359  handleError("Error looking up attribute name " + type.getLabel(),
360  Bundle.PortableCaseReportModule_generateReport_errorLookingUpAttrType(type.getLabel()),
361  ex, progressPanel); // NON-NLS
362  }
363  }
364 
365  // Copy the tagged files
366  try {
367  for (TagName tagName : tagNames) {
368  // Check for cancellation
369  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
370  handleCancellation(progressPanel);
371  return;
372  }
373  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingFiles(tagName.getDisplayName()));
374  addFilesToPortableCase(tagName, progressPanel);
375 
376  // Check for cancellation
377  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
378  handleCancellation(progressPanel);
379  return;
380  }
381  }
382  } catch (TskCoreException ex) {
383  handleError("Error copying tagged files", Bundle.PortableCaseReportModule_generateReport_errorCopyingFiles(), ex, progressPanel); // NON-NLS
384  return;
385  }
386 
387  // Copy the tagged artifacts and associated files
388  try {
389  for (TagName tagName : tagNames) {
390  // Check for cancellation
391  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
392  handleCancellation(progressPanel);
393  return;
394  }
395  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingArtifacts(tagName.getDisplayName()));
396  addArtifactsToPortableCase(tagName, progressPanel);
397 
398  // Check for cancellation
399  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
400  handleCancellation(progressPanel);
401  return;
402  }
403  }
404  } catch (TskCoreException ex) {
405  handleError("Error copying tagged artifacts", Bundle.PortableCaseReportModule_generateReport_errorCopyingArtifacts(), ex, progressPanel); // NON-NLS
406  return;
407  }
408 
409  // Copy interesting files and results
410  if (!setNames.isEmpty()) {
411  try {
412  List<AnalysisResult> interestingFiles = currentCase.getSleuthkitCase().getBlackboard().getAnalysisResultsByType(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID());
413  for (AnalysisResult art : interestingFiles) {
414  // Check for cancellation
415  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
416  handleCancellation(progressPanel);
417  return;
418  }
419 
420  BlackboardAttribute setAttr = art.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
421  if (setNames.contains(setAttr.getValueString())) {
422  copyContentToPortableCase(art, progressPanel);
423  }
424  }
425  } catch (TskCoreException ex) {
426  handleError("Error copying interesting files", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingFiles(), ex, progressPanel); // NON-NLS
427  return;
428  }
429 
430  try {
431  List<AnalysisResult> interestingResults = currentCase.getSleuthkitCase().getBlackboard().getAnalysisResultsByType(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID());
432  for (AnalysisResult art : interestingResults) {
433  // Check for cancellation
434  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
435  handleCancellation(progressPanel);
436  return;
437  }
438  BlackboardAttribute setAttr = art.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
439  if (setNames.contains(setAttr.getValueString())) {
440  copyContentToPortableCase(art, progressPanel);
441  }
442  }
443  } catch (TskCoreException ex) {
444  handleError("Error copying interesting results", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingResults(), ex, progressPanel); // NON-NLS
445  return;
446  }
447  }
448 
449  // Check for cancellation
450  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
451  handleCancellation(progressPanel);
452  return;
453  }
454 
455  //Attempt to generate and included the CASE-UCO report.
456  generateCaseUcoReport(tagNames, setNames, progressPanel);
457 
458  if (options.includeApplication()) {
459  try {
460  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingAutopsy());
461  copyApplication(getApplicationBasePath(), outputDir.getAbsolutePath());
462  createAppLaunchBatFile(outputDir.getAbsolutePath());
463  } catch (IOException ex) {
464  handleError("Error copying autopsy", Bundle.PortableCaseReportModule_generateReport_errorCopyingAutopsy(), ex, progressPanel); // NON-NLS
465  }
466  }
467 
468  // Compress the case (if desired)
469  if (options.shouldCompress()) {
470  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_compressingCase());
471 
472  if(!compressCase(progressPanel, options.includeApplication() ? outputDir.getAbsolutePath() : caseFolder.getAbsolutePath())){
473  // Errors have been handled already
474  return;
475  }
476 
477  // Check for cancellation
478  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
479  handleCancellation(progressPanel);
480  return;
481  }
482  }
483 
484  // Close the case connections and clear out the maps
485  cleanup();
486 
488 
489  }
490 
502  @NbBundle.Messages({
503  "PortableCaseReportModule.generateCaseUcoReport.errorCreatingReportFolder=Could not make report folder",
504  "PortableCaseReportModule.generateCaseUcoReport.errorGeneratingCaseUcoReport=Problem while generating CASE-UCO report",
505  "PortableCaseReportModule.generateCaseUcoReport.startCaseUcoReportGeneration=Creating a CASE-UCO report of the portable case",
506  "PortableCaseReportModule.generateCaseUcoReport.successCaseUcoReportGeneration=Successfully created a CASE-UCO report of the portable case"
507  })
508  private void generateCaseUcoReport(List<TagName> tagNames, List<String> setNames, ReportProgressPanel progressPanel) {
509  //Create the 'Reports' directory to include a CASE-UCO report.
510  Path reportsDirectory = Paths.get(caseFolder.toString(), "Reports");
511  if (!reportsDirectory.toFile().mkdir()) {
512  logger.log(Level.SEVERE, "Could not make the report folder... skipping "
513  + "CASE-UCO report generation for the portable case");
514  return;
515  }
516 
517  Path reportFile = reportsDirectory.resolve(CASE_UCO_FILE_NAME);
518 
519  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_startCaseUcoReportGeneration());
520  try (OutputStream stream = new FileOutputStream(reportFile.toFile());
521  JsonWriter reportWriter = new JsonWriter(new OutputStreamWriter(stream, "UTF-8"))) {
522  Gson gson = new GsonBuilder().setPrettyPrinting().create();
523  reportWriter.setIndent(" ");
524  reportWriter.beginObject();
525  reportWriter.name("@graph");
526  reportWriter.beginArray();
527 
528  String caseTempDirectory = currentCase.getTempDirectory();
529  SleuthkitCase skCase = currentCase.getSleuthkitCase();
530  TagsManager tagsManager = currentCase.getServices().getTagsManager();
531 
532  //Create temp directory to filter out duplicate files.
533  //Clear out the old directory if it exists.
534  Path tmpDir = Paths.get(caseTempDirectory, CASE_UCO_TMP_DIR);
535  FileUtils.deleteDirectory(tmpDir.toFile());
536  Files.createDirectory(tmpDir);
537 
538  CaseUcoExporter exporter = new CaseUcoExporter(currentCase.getSleuthkitCase());
539  for (JsonElement element : exporter.exportSleuthkitCase()) {
540  gson.toJson(element, reportWriter);
541  }
542 
543  //Load all interesting BlackboardArtifacts that belong to the selected SET_NAMEs
544  //binned by data source id.
545  Multimap<Long, BlackboardArtifact> artifactsWithSetName = getInterestingArtifactsBySetName(skCase, setNames);
546 
547  //Search each data source looking for content tags and interesting
548  //items that match the selected tag names and set names.
549  for (DataSource dataSource : currentCase.getSleuthkitCase().getDataSources()) {
550  // Helper flag to ensure each data source is only written once in
551  // a report.
552  boolean dataSourceHasBeenIncluded = false;
553 
554  //Search content tags and artifact tags that match
555  for (TagName tagName : tagNames) {
556  for (ContentTag ct : tagsManager.getContentTagsByTagName(tagName, dataSource.getId())) {
557  dataSourceHasBeenIncluded |= addUniqueFile(ct.getContent(),
558  dataSource, tmpDir, gson, exporter, reportWriter, dataSourceHasBeenIncluded);
559  }
560  for (BlackboardArtifactTag bat : tagsManager.getBlackboardArtifactTagsByTagName(tagName, dataSource.getId())) {
561  dataSourceHasBeenIncluded |= addUniqueFile(bat.getContent(),
562  dataSource, tmpDir, gson, exporter, reportWriter, dataSourceHasBeenIncluded);
563  }
564  }
565  //Search artifacts that this data source contains
566  for (BlackboardArtifact bArt : artifactsWithSetName.get(dataSource.getId())) {
567  Content sourceContent = bArt.getParent();
568  dataSourceHasBeenIncluded |= addUniqueFile(sourceContent, dataSource,
569  tmpDir, gson, exporter, reportWriter, dataSourceHasBeenIncluded);
570  }
571  }
572 
573  // Finish the report.
574  reportWriter.endArray();
575  reportWriter.endObject();
576  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_successCaseUcoReportGeneration());
577  } catch (IOException | TskCoreException ex) {
578  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_errorGeneratingCaseUcoReport());
579  logger.log(Level.SEVERE, "Error encountered while trying to create "
580  + "CASE-UCO output for portable case.. the portable case will be "
581  + "completed without a CASE-UCO report.", ex);
582  }
583  }
584 
590  private Multimap<Long, BlackboardArtifact> getInterestingArtifactsBySetName(SleuthkitCase skCase, List<String> setNames) throws TskCoreException {
591  Multimap<Long, BlackboardArtifact> artifactsWithSetName = ArrayListMultimap.create();
592  if (!setNames.isEmpty()) {
593  List<BlackboardArtifact> allArtifacts = skCase.getBlackboardArtifacts(
594  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
595  allArtifacts.addAll(skCase.getBlackboardArtifacts(
596  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT));
597 
598  for (BlackboardArtifact bArt : allArtifacts) {
599  BlackboardAttribute setAttr = bArt.getAttribute(
600  new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
601  if (setNames.contains(setAttr.getValueString())) {
602  artifactsWithSetName.put(bArt.getDataSource().getId(), bArt);
603  }
604  }
605  }
606  return artifactsWithSetName;
607  }
608 
629  private boolean addUniqueFile(Content content, DataSource dataSource,
630  Path tmpDir, Gson gson, CaseUcoExporter exporter, JsonWriter reportWriter,
631  boolean dataSourceHasBeenIncluded) throws IOException, TskCoreException {
632  if (content instanceof AbstractFile && !(content instanceof DataSource)) {
633  AbstractFile absFile = (AbstractFile) content;
634  Path filePath = tmpDir.resolve(Long.toString(absFile.getId()));
635  if (!absFile.isDir() && !Files.exists(filePath)) {
636  if (!dataSourceHasBeenIncluded) {
637  for (JsonElement element : exporter.exportDataSource(dataSource)) {
638  gson.toJson(element, reportWriter);
639  }
640  }
641  String subFolder = getExportSubfolder(absFile);
642  String fileName = absFile.getId() + "-" + FileUtil.escapeFileName(absFile.getName());
643  for (JsonElement element : exporter.exportAbstractFile(absFile, Paths.get(FILE_FOLDER_NAME, subFolder, fileName).toString())) {
644  gson.toJson(element, reportWriter);
645  }
646  Files.createFile(filePath);
647  return true;
648  }
649  }
650  return false;
651  }
652 
653  private List<String> getAllInterestingItemsSets() throws NoCurrentCaseException, TskCoreException {
654 
655  // Get the set names in use for the current case.
656  List<String> setNames = new ArrayList<>();
657  Map<String, Long> setCounts;
658 
659  // There may not be a case open when configuring report modules for Command Line execution
660  // Get all SET_NAMEs from interesting item artifacts
661  String innerSelect = "SELECT (value_text) AS set_name FROM blackboard_attributes WHERE (artifact_type_id = '"
662  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + "' OR artifact_type_id = '"
663  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() + "') AND attribute_type_id = '"
664  + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + "'"; // NON-NLS
665 
666  // Get the count of each SET_NAME
667  String query = "set_name, count(1) AS set_count FROM (" + innerSelect + ") set_names GROUP BY set_name"; // NON-NLS
668 
670  Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select(query, callback);
671  setCounts = callback.getSetCountMap();
672  setNames.addAll(setCounts.keySet());
673  return setNames;
674  }
675 
683  @NbBundle.Messages({
684  "# {0} - case folder",
685  "PortableCaseReportModule.createCase.caseDirExists=Case folder {0} already exists",
686  "PortableCaseReportModule.createCase.errorCreatingCase=Error creating case",
687  "# {0} - folder",
688  "PortableCaseReportModule.createCase.errorCreatingFolder=Error creating folder {0}",
689  "PortableCaseReportModule.createCase.errorStoringMaxIds=Error storing maximum database IDs",})
690  private void createCase(File outputDir, ReportProgressPanel progressPanel) {
691 
692  // Create the case folder
693  caseFolder = Paths.get(outputDir.toString(), caseName).toFile();
694 
695  if (caseFolder.exists()) {
696  handleError("Case folder " + caseFolder.toString() + " already exists",
697  Bundle.PortableCaseReportModule_createCase_caseDirExists(caseFolder.toString()), null, progressPanel); // NON-NLS
698  return;
699  }
700 
701  // Create the case
702  try {
703  portableSkCase = currentCase.createPortableCase(caseName, caseFolder);
704  } catch (TskCoreException ex) {
705  handleError("Error creating case " + caseName + " in folder " + caseFolder.toString(),
706  Bundle.PortableCaseReportModule_createCase_errorCreatingCase(), ex, progressPanel); // NON-NLS
707  return;
708  }
709 
710  // Store the highest IDs
711  try {
712  saveHighestIds();
713  } catch (TskCoreException ex) {
714  handleError("Error storing maximum database IDs",
715  Bundle.PortableCaseReportModule_createCase_errorStoringMaxIds(), ex, progressPanel); // NON-NLS
716  return;
717  }
718 
719  // Create the base folder for the copied files
720  copiedFilesFolder = Paths.get(caseFolder.toString(), FILE_FOLDER_NAME).toFile();
721  if (!copiedFilesFolder.mkdir()) {
722  handleError("Error creating folder " + copiedFilesFolder.toString(),
723  Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(copiedFilesFolder.toString()), null, progressPanel); // NON-NLS
724  return;
725  }
726 
727  // Create subfolders for the copied files
728  for (FileTypeCategory cat : FILE_TYPE_CATEGORIES) {
729  File subFolder = Paths.get(copiedFilesFolder.toString(), cat.getDisplayName()).toFile();
730  if (!subFolder.mkdir()) {
731  handleError("Error creating folder " + subFolder.toString(),
732  Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(subFolder.toString()), null, progressPanel); // NON-NLS
733  return;
734  }
735  }
736  File unknownTypeFolder = Paths.get(copiedFilesFolder.toString(), UNKNOWN_FILE_TYPE_FOLDER).toFile();
737  if (!unknownTypeFolder.mkdir()) {
738  handleError("Error creating folder " + unknownTypeFolder.toString(),
739  Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(unknownTypeFolder.toString()), null, progressPanel); // NON-NLS
740  return;
741  }
742 
743  }
744 
750  private void saveHighestIds() throws TskCoreException {
751 
752  CaseDbAccessManager currentCaseDbManager = currentCase.getSleuthkitCase().getCaseDbAccessManager();
753 
754  String tableSchema = "( table_name TEXT PRIMARY KEY, "
755  + " max_id TEXT)"; // NON-NLS
756 
757  portableSkCase.getCaseDbAccessManager().createTable(MAX_ID_TABLE_NAME, tableSchema);
758 
759  currentCaseDbManager.select("max(obj_id) as max_id from tsk_objects", new StoreMaxIdCallback("tsk_objects")); // NON-NLS
760  currentCaseDbManager.select("max(tag_id) as max_id from content_tags", new StoreMaxIdCallback("content_tags")); // NON-NLS
761  currentCaseDbManager.select("max(tag_id) as max_id from blackboard_artifact_tags", new StoreMaxIdCallback("blackboard_artifact_tags")); // NON-NLS
762  currentCaseDbManager.select("max(examiner_id) as max_id from tsk_examiners", new StoreMaxIdCallback("tsk_examiners")); // NON-NLS
763  }
764 
772  private void initializeImageTags(ReportProgressPanel progressPanel) throws TskCoreException {
773 
774  // Create the image tags table in the portable case
775  CaseDbAccessManager portableDbAccessManager = portableSkCase.getCaseDbAccessManager();
776  if (!portableDbAccessManager.tableExists(ContentViewerTagManager.TABLE_NAME)) {
778  }
779  }
780 
789  private void addFilesToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel) throws TskCoreException {
790 
791  // Get all the tags in the current case
792  List<ContentTag> tags = currentCase.getServices().getTagsManager().getContentTagsByTagName(oldTagName);
793 
794  // Copy the files into the portable case and tag
795  for (ContentTag tag : tags) {
796 
797  // Check for cancellation
798  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
799  return;
800  }
801 
802  Content content = tag.getContent();
803  if (content instanceof AbstractFile) {
804 
805  long newFileId = copyContentToPortableCase(content, progressPanel);
806 
807  // Tag the file
808  if (!oldTagNameToNewTagName.containsKey(tag.getName())) {
809  throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS
810  }
811  ContentTagChange newContentTag = portableSkCase.getTaggingManager().addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
812 
813  // Get the image tag data associated with this tag (empty string if there is none)
814  // and save it if present
815  String appData = getImageTagDataForContentTag(tag);
816  if (!appData.isEmpty()) {
817  addImageTagToPortableCase(newContentTag.getAddedTag(), appData);
818  }
819  }
820  }
821  }
822 
833  private String getImageTagDataForContentTag(ContentTag tag) throws TskCoreException {
834 
835  GetImageTagCallback callback = new GetImageTagCallback();
836  String query = "* FROM " + ContentViewerTagManager.TABLE_NAME + " WHERE content_tag_id = " + tag.getId();
837  currentCase.getSleuthkitCase().getCaseDbAccessManager().select(query, callback);
838  return callback.getAppData();
839  }
840 
844  private static class GetImageTagCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
845 
846  private static final Logger logger = Logger.getLogger(PortableCaseReportModule.class.getName());
847  private String appData = "";
848 
849  @Override
850  public void process(ResultSet rs) {
851  try {
852  while (rs.next()) {
853  try {
854  appData = rs.getString("app_data"); // NON-NLS
855  } catch (SQLException ex) {
856  logger.log(Level.WARNING, "Unable to get app_data from result set", ex); // NON-NLS
857  }
858  }
859  } catch (SQLException ex) {
860  logger.log(Level.WARNING, "Failed to get next result for app_data", ex); // NON-NLS
861  }
862  }
863 
869  String getAppData() {
870  return appData;
871  }
872  }
873 
882  private void addImageTagToPortableCase(ContentTag newContentTag, String appData) throws TskCoreException {
883  String insert = "(content_tag_id, app_data) VALUES (" + newContentTag.getId() + ", '" + appData + "')";
884  portableSkCase.getCaseDbAccessManager().insert(ContentViewerTagManager.TABLE_NAME, insert);
885  }
886 
895  private void addArtifactsToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel) throws TskCoreException {
896 
897  List<BlackboardArtifactTag> tags = currentCase.getServices().getTagsManager().getBlackboardArtifactTagsByTagName(oldTagName);
898 
899  // Copy the artifacts into the portable case along with their content and tag
900  for (BlackboardArtifactTag tag : tags) {
901 
902  // Check for cancellation
903  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
904  return;
905  }
906 
907  // Copy the source content
908  Content content = tag.getContent();
909  long newContentId = copyContentToPortableCase(content, progressPanel);
910 
911  // Copy the artifact
912  BlackboardArtifact newArtifact = copyArtifact(newContentId, tag.getArtifact());
913 
914  // Copy any attachments
915  copyAttachments(newArtifact, tag.getArtifact(), portableSkCase.getAbstractFileById(newContentId));
916 
917  // Copy any files associated with this artifact through the TSK_PATH_ID attribute
918  copyPathID(newArtifact, tag.getArtifact());
919 
920  // Tag the artfiact
921  if (!oldTagNameToNewTagName.containsKey(tag.getName())) {
922  throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS
923  }
924  portableSkCase.getTaggingManager().addArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment());
925  }
926  }
927 
940  private BlackboardArtifact copyArtifact(long newContentId, BlackboardArtifact artifactToCopy) throws TskCoreException {
941 
942  if (oldArtifactIdToNewArtifact.containsKey(artifactToCopy.getArtifactID())) {
943  return oldArtifactIdToNewArtifact.get(artifactToCopy.getArtifactID());
944  }
945 
946  // First create the associated artifact (if present)
947  BlackboardAttribute oldAssociatedAttribute = artifactToCopy.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
948  List<BlackboardAttribute> newAttrs = new ArrayList<>();
949  if (oldAssociatedAttribute != null) {
950  BlackboardArtifact oldAssociatedArtifact = currentCase.getSleuthkitCase().getBlackboardArtifact(oldAssociatedAttribute.getValueLong());
951  BlackboardArtifact newAssociatedArtifact = copyArtifact(newContentId, oldAssociatedArtifact);
952  newAttrs.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
953  String.join(",", oldAssociatedAttribute.getSources()), newAssociatedArtifact.getArtifactID()));
954  }
955 
956  List<BlackboardAttribute> oldAttrs = artifactToCopy.getAttributes();
957 
958  // Copy over each attribute, making sure the type is in the new case.
959  for (BlackboardAttribute oldAttr : oldAttrs) {
960 
961  // Skip attributes that are handled elsewhere
962  if (SPECIALLY_HANDLED_ATTRS.contains(oldAttr.getAttributeType().getTypeID())) {
963  continue;
964  }
965 
966  BlackboardAttribute.Type newAttributeType = getNewAttributeType(oldAttr);
967  switch (oldAttr.getValueType()) {
968  case BYTE:
969  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
970  oldAttr.getValueBytes()));
971  break;
972  case DOUBLE:
973  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
974  oldAttr.getValueDouble()));
975  break;
976  case INTEGER:
977  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
978  oldAttr.getValueInt()));
979  break;
980  case DATETIME:
981  case LONG:
982  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
983  oldAttr.getValueLong()));
984  break;
985  case STRING:
986  case JSON:
987  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
988  oldAttr.getValueString()));
989  break;
990  default:
991  throw new TskCoreException("Unexpected attribute value type found: " + oldAttr.getValueType().getLabel()); // NON-NLS
992  }
993  }
994 
995  // Figure out the data source ID. We can't always get it from newContent because it could be null
996  // for OS accounts, which means we also can't assume it's been added to the case already.
997  Long newDataSourceId;
998  if (newIdToContent.get(newContentId).getDataSource() != null) {
999  // We can use the new content to get the id since the data source is in its parent hierarchy.
1000  // Everything would have already been copied to the portable case.
1001  newDataSourceId = newIdToContent.get(newContentId).getDataSource().getId();
1002  } else {
1003  // The newContent has no data source parent, so we'll have to use the old artifact.
1004  if (artifactToCopy.getDataSource() == null) {
1005  // Shouldn't happen with the current code
1006  throw new TskCoreException("Can not copy artifact with ID: " + artifactToCopy.getArtifactID() + " because it is not associated with a data source");
1007  }
1008  newDataSourceId = copyContent(artifactToCopy.getDataSource());
1009  }
1010 
1011 
1012  // Create the new artifact
1013  int newArtifactTypeId = getNewArtifactTypeId(artifactToCopy);
1014  BlackboardArtifact.Type newArtifactType = portableSkCase.getBlackboard().getArtifactType(newArtifactTypeId);
1015  BlackboardArtifact newArtifact;
1016 
1017  // First, check if the artifact being copied is an AnalysisResult or a DataArtifact. If it
1018  // is neither, attempt to reload it as the appropriate subclass.
1019  if (!((artifactToCopy instanceof AnalysisResult) || (artifactToCopy instanceof DataArtifact))) {
1020  try {
1021  if (newArtifactType.getCategory().equals(BlackboardArtifact.Category.ANALYSIS_RESULT)) {
1022  AnalysisResult ar = currentCase.getSleuthkitCase().getBlackboard().getAnalysisResultById(artifactToCopy.getId());
1023  if (ar != null) {
1024  artifactToCopy = ar;
1025  }
1026  } else {
1027  DataArtifact da = currentCase.getSleuthkitCase().getBlackboard().getDataArtifactById(artifactToCopy.getId());
1028  if (da != null) {
1029  artifactToCopy = da;
1030  }
1031  }
1032  } catch (TskCoreException ex) {
1033  // If the lookup failed, just use the orginal BlackboardArtifact
1034  }
1035  }
1036 
1037  try {
1038  if (artifactToCopy instanceof AnalysisResult) {
1039  AnalysisResult analysisResultToCopy = (AnalysisResult) artifactToCopy;
1040  newArtifact = portableSkCase.getBlackboard().newAnalysisResult(newArtifactType, newContentId,
1041  newDataSourceId, analysisResultToCopy.getScore(),
1042  analysisResultToCopy.getConclusion(), analysisResultToCopy.getConfiguration(),
1043  analysisResultToCopy.getJustification(), newAttrs).getAnalysisResult();
1044  } else if (artifactToCopy instanceof DataArtifact) {
1045  DataArtifact dataArtifactToCopy = (DataArtifact) artifactToCopy;
1046  Long newOsAccountId = null;
1047  if (dataArtifactToCopy.getOsAccountObjectId().isPresent()) {
1048  copyOsAccount(dataArtifactToCopy.getOsAccountObjectId().get());
1049  newOsAccountId = oldOsAccountIdToNewOsAccount.get((dataArtifactToCopy.getOsAccountObjectId().get())).getId();
1050  }
1051  newArtifact = portableSkCase.getBlackboard().newDataArtifact(newArtifactType, newContentId,
1052  newDataSourceId,
1053  newAttrs, newOsAccountId);
1054  } else {
1055  if (newArtifactType.getCategory().equals(BlackboardArtifact.Category.ANALYSIS_RESULT)) {
1056  newArtifact = portableSkCase.getBlackboard().newAnalysisResult(newArtifactType, newContentId,
1057  newDataSourceId, Score.SCORE_NONE,
1058  null, null, null, newAttrs).getAnalysisResult();
1059  } else {
1060  newArtifact = portableSkCase.getBlackboard().newDataArtifact(newArtifactType, newContentId,
1061  newDataSourceId,
1062  newAttrs, null);
1063  }
1064  }
1065  } catch (BlackboardException ex) {
1066  throw new TskCoreException("Error copying artifact with ID: " + artifactToCopy.getId());
1067  }
1068 
1069  oldArtifactIdToNewArtifact.put(artifactToCopy.getArtifactID(), newArtifact);
1070  return newArtifact;
1071  }
1072 
1082  private int getNewArtifactTypeId(BlackboardArtifact oldArtifact) throws TskCoreException {
1083  if (oldArtTypeIdToNewArtTypeId.containsKey(oldArtifact.getArtifactTypeID())) {
1084  return oldArtTypeIdToNewArtTypeId.get(oldArtifact.getArtifactTypeID());
1085  }
1086 
1087  BlackboardArtifact.Type oldCustomType = currentCase.getSleuthkitCase().getArtifactType(oldArtifact.getArtifactTypeName());
1088  try {
1089  BlackboardArtifact.Type newCustomType = portableSkCase.getBlackboard().getOrAddArtifactType(oldCustomType.getTypeName(), oldCustomType.getDisplayName());
1090  oldArtTypeIdToNewArtTypeId.put(oldArtifact.getArtifactTypeID(), newCustomType.getTypeID());
1091  return newCustomType.getTypeID();
1092  } catch (BlackboardException ex) {
1093  throw new TskCoreException("Error creating new artifact type " + oldCustomType.getTypeName(), ex); // NON-NLS
1094  }
1095  }
1096 
1106  private BlackboardAttribute.Type getNewAttributeType(BlackboardAttribute oldAttribute) throws TskCoreException {
1107  BlackboardAttribute.Type oldAttrType = oldAttribute.getAttributeType();
1108  if (oldAttrTypeIdToNewAttrType.containsKey(oldAttrType.getTypeID())) {
1109  return oldAttrTypeIdToNewAttrType.get(oldAttrType.getTypeID());
1110  }
1111 
1112  try {
1113  BlackboardAttribute.Type newCustomType = portableSkCase.getBlackboard().getOrAddAttributeType(oldAttrType.getTypeName(),
1114  oldAttrType.getValueType(), oldAttrType.getDisplayName());
1115  oldAttrTypeIdToNewAttrType.put(oldAttribute.getAttributeType().getTypeID(), newCustomType);
1116  return newCustomType;
1117  } catch (BlackboardException ex) {
1118  throw new TskCoreException("Error creating new attribute type " + oldAttrType.getTypeName(), ex); // NON-NLS
1119  }
1120  }
1121 
1132  @NbBundle.Messages({
1133  "# {0} - File name",
1134  "PortableCaseReportModule.copyContentToPortableCase.copyingFile=Copying file {0}",})
1135  private long copyContentToPortableCase(Content content, ReportProgressPanel progressPanel) throws TskCoreException {
1136  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_copyContentToPortableCase_copyingFile(content.getUniquePath()));
1137  return copyContent(content);
1138  }
1139 
1149  private long copyContent(Content content) throws TskCoreException {
1150 
1151  // Check if we've already copied this content
1152  if (oldIdToNewContent.containsKey(content.getId())) {
1153  return oldIdToNewContent.get(content.getId()).getId();
1154  }
1155 
1156  // Otherwise:
1157  // - Make parent of this object (if applicable)
1158  // - Copy this content
1159  long parentId = 0;
1160  if (content.getParent() != null) {
1161  parentId = copyContent(content.getParent());
1162  }
1163 
1164  Content newContent;
1165  if (content instanceof BlackboardArtifact) {
1166  BlackboardArtifact artifactToCopy = (BlackboardArtifact) content;
1167  newContent = copyArtifact(parentId, artifactToCopy);
1168  } else if (content instanceof OsAccount) {
1169  newContent = copyOsAccount(content.getId());
1170  } else {
1171  // Get or create the host (if needed) before beginning transaction.
1172  Host newHost = null;
1173  if (content instanceof DataSource) {
1174  newHost = copyHost(((DataSource)content).getHost());
1175  }
1176 
1177  // Copy the associated OS account (if needed) before beginning transaction.
1178  if (content instanceof AbstractFile) {
1179  AbstractFile file = (AbstractFile) content;
1180  if (file.getOsAccountObjectId().isPresent()) {
1181  copyOsAccount(file.getOsAccountObjectId().get());
1182  }
1183  }
1184 
1185  // Load the hashes if we have an image to avoid getting new connections with an open transaction.
1186  String md5 = "";
1187  String sha1 = "";
1188  String sha256 = "";
1189  if (content instanceof Image) {
1190  md5 = ((Image) content).getMd5();
1191  sha1 = ((Image) content).getSha1();
1192  sha256 = ((Image) content).getSha256();
1193  }
1194 
1195  CaseDbTransaction trans = portableSkCase.beginTransaction();
1196  try {
1197  if (content instanceof Image) {
1198  Image image = (Image) content;
1199  newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(),
1200  new ArrayList<>(), image.getTimeZone(), md5, sha1, sha256, image.getDeviceId(), newHost, trans);
1201  } else if (content instanceof VolumeSystem) {
1202  VolumeSystem vs = (VolumeSystem) content;
1203  newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
1204  } else if (content instanceof Volume) {
1205  Volume vs = (Volume) content;
1206  newContent = portableSkCase.addVolume(parentId, vs.getAddr(), vs.getStart(), vs.getLength(),
1207  vs.getDescription(), vs.getFlags(), trans);
1208  } else if (content instanceof Pool) {
1209  Pool pool = (Pool) content;
1210  newContent = portableSkCase.addPool(parentId, pool.getType(), trans);
1211  } else if (content instanceof FileSystem) {
1212  FileSystem fs = (FileSystem) content;
1213  newContent = portableSkCase.addFileSystem(parentId, fs.getImageOffset(), fs.getFsType(), fs.getBlock_size(),
1214  fs.getBlock_count(), fs.getRoot_inum(), fs.getFirst_inum(), fs.getLastInum(),
1215  fs.getName(), trans);
1216  } else if (content instanceof BlackboardArtifact) {
1217  BlackboardArtifact artifactToCopy = (BlackboardArtifact) content;
1218  newContent = copyArtifact(parentId, artifactToCopy);
1219  } else if (content instanceof AbstractFile) {
1220  AbstractFile abstractFile = (AbstractFile) content;
1221 
1222  if (abstractFile instanceof LocalFilesDataSource) {
1223  LocalFilesDataSource localFilesDS = (LocalFilesDataSource) abstractFile;
1224  newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), newHost, trans);
1225  } else {
1226  if (abstractFile.isDir()) {
1227  newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
1228  } else {
1229  try {
1230  // Copy the file
1231  String fileName = abstractFile.getId() + "-" + FileUtil.escapeFileName(abstractFile.getName());
1232  String exportSubFolder = getExportSubfolder(abstractFile);
1233  File exportFolder = Paths.get(copiedFilesFolder.toString(), exportSubFolder).toFile();
1234  File localFile = new File(exportFolder, fileName);
1235  ContentUtils.writeToFile(abstractFile, localFile);
1236 
1237  // Get the new parent object in the portable case database
1238  Content oldParent = abstractFile.getParent();
1239  if (!oldIdToNewContent.containsKey(oldParent.getId())) {
1240  throw new TskCoreException("Parent of file with ID " + abstractFile.getId() + " has not been created"); // NON-NLS
1241  }
1242  Content newParent = oldIdToNewContent.get(oldParent.getId());
1243 
1244  // Construct the relative path to the copied file
1245  String relativePath = FILE_FOLDER_NAME + File.separator + exportSubFolder + File.separator + fileName;
1246 
1247  Long newOsAccountId = null;
1248  if (abstractFile.getOsAccountObjectId().isPresent()) {
1249  newOsAccountId = oldOsAccountIdToNewOsAccount.get(abstractFile.getOsAccountObjectId().get()).getId();
1250  }
1251 
1252  newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
1253  abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getMtime(),
1254  abstractFile.getMd5Hash(), abstractFile.getSha256Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
1255  true, TskData.EncodingType.NONE,
1256  newOsAccountId, abstractFile.getOwnerUid().orElse(null),
1257  newParent, trans);
1258  } catch (IOException ex) {
1259  throw new TskCoreException("Error copying file " + abstractFile.getName() + " with original obj ID "
1260  + abstractFile.getId(), ex); // NON-NLS
1261  }
1262  }
1263  }
1264  } else {
1265  throw new TskCoreException("Trying to copy unexpected Content type " + content.getClass().getName()); // NON-NLS
1266  }
1267  trans.commit();
1268  } catch (TskCoreException ex) {
1269  trans.rollback();
1270  throw (ex);
1271  }
1272  }
1273 
1274  // Save the new object
1275  oldIdToNewContent.put(content.getId(), newContent);
1276  newIdToContent.put(newContent.getId(), newContent);
1277  return oldIdToNewContent.get(content.getId()).getId();
1278  }
1279 
1288  private Host copyHost(Host oldHost) throws TskCoreException {
1289  Host newHost;
1290  if (oldHostIdToNewHost.containsKey(oldHost.getHostId())) {
1291  newHost = oldHostIdToNewHost.get(oldHost.getHostId());
1292  } else {
1293  newHost = portableSkCase.getHostManager().newHost(oldHost.getName());
1294  oldHostIdToNewHost.put(oldHost.getHostId(), newHost);
1295  }
1296  return newHost;
1297  }
1298 
1305  private OsAccount copyOsAccount(Long oldOsAccountId) throws TskCoreException {
1306  // If it has already been copied, we're done.
1307  if (oldOsAccountIdToNewOsAccount.containsKey(oldOsAccountId)) {
1308  return oldOsAccountIdToNewOsAccount.get(oldOsAccountId);
1309  }
1310 
1311  // Load the OS account from the current case.
1312  OsAccountManager oldOsAcctManager = currentCase.getSleuthkitCase().getOsAccountManager();
1313  OsAccount oldOsAccount = oldOsAcctManager.getOsAccountByObjectId(oldOsAccountId);
1314 
1315  // Load the realm associated with the OS account.
1316  OsAccountRealmManager oldRealmManager = currentCase.getSleuthkitCase().getOsAccountRealmManager();
1317  OsAccountRealm oldRealm = oldRealmManager.getRealmByRealmId(oldOsAccount.getRealmId());
1318 
1319  // Copy the realm to the portable case if necessary.
1320  if (!oldRealmIdToNewRealm.containsKey(oldOsAccount.getRealmId())) {
1321  OsAccountRealmManager newRealmManager = portableSkCase.getOsAccountRealmManager();
1322 
1323  Host newHost = null;
1324  if (oldRealm.getScopeHost().isPresent()) {
1325  Host host = oldRealm.getScopeHost().get();
1326  newHost = copyHost(host);
1327  } else {
1328  if (oldRealm.getScope().equals(OsAccountRealm.RealmScope.DOMAIN)) {
1329  // There is currently no way to get a domain-scoped host in Autopsy. When this changes
1330  // we will need to update this code. This will require a new version of newWindowsRealm() that
1331  // does not require a host, or some other way to create a realm with no host.
1332  throw new TskCoreException("Failed to copy OsAccountRealm with ID=" + oldOsAccount.getRealmId() + " - can not currently handle domain-scoped hosts");
1333  } else {
1334  throw new TskCoreException("Failed to copy OsAccountRealm with ID=" + oldOsAccount.getRealmId() + " because it is non-domain scoped but has no scope host");
1335  }
1336  }
1337 
1338  // We currently only support one realm name.
1339  String realmName = null;
1340  List<String> names = oldRealm.getRealmNames();
1341  if (!names.isEmpty()) {
1342  realmName = names.get(0);
1343  }
1344 
1345  try {
1346  OsAccountRealm newRealm = newRealmManager.newWindowsRealm(oldRealm.getRealmAddr().orElse(null), realmName, newHost, oldRealm.getScope());
1347  oldRealmIdToNewRealm.put(oldOsAccount.getRealmId(), newRealm);
1348  } catch (NotUserSIDException ex) {
1349  throw new TskCoreException("Failed to copy OsAccountRealm with ID=" + oldOsAccount.getRealmId(), ex);
1350  }
1351  }
1352 
1353  OsAccountManager newOsAcctManager = portableSkCase.getOsAccountManager();
1354  try {
1355  OsAccount newOsAccount = newOsAcctManager.newWindowsOsAccount(oldOsAccount.getAddr().orElse(null),
1356  oldOsAccount.getLoginName().orElse(null), oldRealmIdToNewRealm.get(oldOsAccount.getRealmId()));
1357  oldOsAccountIdToNewOsAccount.put(oldOsAccountId, newOsAccount);
1358  return newOsAccount;
1359  } catch (NotUserSIDException ex) {
1360  throw new TskCoreException("Failed to copy OsAccount with ID=" + oldOsAccount.getId(), ex);
1361  }
1362  }
1363 
1372  private void copyPathID(BlackboardArtifact newArtifact, BlackboardArtifact oldArtifact) throws TskCoreException {
1373  // Get the path ID attribute
1374  BlackboardAttribute oldPathIdAttr = oldArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
1375  if (oldPathIdAttr != null) {
1376  // Copy the file and remake the attribute if the path ID is valid
1377  long oldContentId = oldPathIdAttr.getValueLong();
1378  if (oldContentId > 0) {
1379  Content oldContent = currentCase.getSleuthkitCase().getContentById(oldContentId);
1380  long newContentId = copyContent(oldContent);
1381  newArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID,
1382  String.join(",", oldPathIdAttr.getSources()), newContentId));
1383  }
1384  }
1385  }
1386 
1396  private void copyAttachments(BlackboardArtifact newArtifact, BlackboardArtifact oldArtifact, AbstractFile newFile) throws TskCoreException {
1397  // Get the attachments from TSK_ATTACHMENTS attribute.
1398  BlackboardAttribute attachmentsAttr = oldArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS));
1399  if (attachmentsAttr != null) {
1400  try {
1401  MessageAttachments msgAttachments = BlackboardJsonAttrUtil.fromAttribute(attachmentsAttr, MessageAttachments.class);
1402 
1403  Collection<MessageAttachments.FileAttachment> oldFileAttachments = msgAttachments.getFileAttachments();
1404  List<MessageAttachments.FileAttachment> newFileAttachments = new ArrayList<>();
1405  for (MessageAttachments.FileAttachment oldFileAttachment : oldFileAttachments) {
1406  long attachedFileObjId = oldFileAttachment.getObjectId();
1407  if (attachedFileObjId >= 0) {
1408  // Copy the attached file and save to the MessageAttachments object
1409  AbstractFile attachedFile = currentCase.getSleuthkitCase().getAbstractFileById(attachedFileObjId);
1410  if (attachedFile == null) {
1411  throw new TskCoreException("Error loading file with object ID " + attachedFileObjId + " from portable case");
1412  }
1413  long newFileID = copyContent(attachedFile);
1414  newFileAttachments.add(new MessageAttachments.FileAttachment(portableSkCase.getAbstractFileById(newFileID)));
1415  }
1416  }
1417 
1418  // Get the name of the module(s) that created the attachment
1419  String newSourceStr = "";
1420  List<String> oldSources = attachmentsAttr.getSources();
1421  if (! oldSources.isEmpty()) {
1422  newSourceStr = String.join(",", oldSources);
1423  }
1424 
1425  // Add the attachment. The account type specified in the constructor will not be used.
1426  CommunicationArtifactsHelper communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(),
1427  newSourceStr, newFile, Account.Type.EMAIL);
1428  communicationArtifactsHelper.addAttachments(newArtifact, new MessageAttachments(newFileAttachments, msgAttachments.getUrlAttachments()));
1429  }
1430  catch (BlackboardJsonAttrUtil.InvalidJsonException ex) {
1431  throw new TskCoreException(String.format("Unable to parse json for MessageAttachments object in artifact: %s", oldArtifact.getName()), ex);
1432  }
1433  } else { // backward compatibility - email message attachments are derived files, children of the message.
1434  for (Content childContent : oldArtifact.getChildren()) {
1435  if (childContent instanceof AbstractFile) {
1436  copyContent(childContent);
1437  }
1438  }
1439  }
1440  }
1441 
1449  private String getExportSubfolder(AbstractFile abstractFile) {
1450  if (abstractFile.getMIMEType() == null || abstractFile.getMIMEType().isEmpty()) {
1451  return UNKNOWN_FILE_TYPE_FOLDER;
1452  }
1453 
1454  for (FileTypeCategory cat : FILE_TYPE_CATEGORIES) {
1455  if (cat.getMediaTypes().contains(abstractFile.getMIMEType())) {
1456  return cat.getDisplayName();
1457  }
1458  }
1459  return UNKNOWN_FILE_TYPE_FOLDER;
1460  }
1461 
1467  private Path getApplicationBasePath() {
1468  return getAutopsyExePath().getParent().getParent();
1469  }
1470 
1476  private Path getAutopsyExePath() {
1477  // If this is an installed version, there should be an <appName>64.exe file in the bin folder
1478  String exeName = getAutopsyExeName();
1479  String installPath = PlatformUtil.getInstallPath();
1480 
1481  return Paths.get(installPath, "bin", exeName);
1482  }
1483 
1489  private String getAutopsyExeName() {
1490  String appName = UserPreferences.getAppName();
1491  return appName + "64.exe";
1492  }
1493 
1502  private void copyApplication(Path sourceFolder, String destBaseFolder) throws IOException {
1503 
1504  // Create an appName folder in the destination
1505  Path destAppFolder = Paths.get(destBaseFolder, UserPreferences.getAppName());
1506  if (!destAppFolder.toFile().exists() && !destAppFolder.toFile().mkdirs()) {
1507  throw new IOException("Failed to create directory " + destAppFolder.toString());
1508  }
1509 
1510  // Now copy the files
1511  FileUtils.copyDirectory(sourceFolder.toFile(), destAppFolder.toFile());
1512  }
1513 
1521  private void createAppLaunchBatFile(String destBaseFolder) throws IOException {
1522  Path filePath = Paths.get(destBaseFolder, "open.bat");
1523  String appName = UserPreferences.getAppName();
1524  String exePath = "\"%~dp0" + appName + "\\bin\\" + getAutopsyExeName() + "\"";
1525  String casePath = "..\\" + caseName;
1526  try (FileWriter writer = new FileWriter(filePath.toFile())) {
1527  writer.write(exePath + " \"" + casePath + "\"");
1528  }
1529  }
1530 
1534  private void cleanup() {
1535  oldIdToNewContent.clear();
1536  newIdToContent.clear();
1537  oldTagNameToNewTagName.clear();
1538  oldArtTypeIdToNewArtTypeId.clear();
1540  oldArtifactIdToNewArtifact.clear();
1541  oldOsAccountIdToNewOsAccount.clear();
1542  oldRealmIdToNewRealm.clear();
1543  oldHostIdToNewHost.clear();
1544 
1546 
1547  currentCase = null;
1548  caseFolder = null;
1549  copiedFilesFolder = null;
1550  }
1551 
1555  private void closePortableCaseDatabase() {
1556  if (portableSkCase != null) {
1557  portableSkCase.close();
1558  portableSkCase = null;
1559  }
1560  }
1561 
1562  /*
1563  * @Override public JPanel getConfigurationPanel() { configPanel = new
1564  * CreatePortableCasePanel(); return configPanel; }
1565  */
1566  private class StoreMaxIdCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
1567 
1568  private final String tableName;
1569 
1570  StoreMaxIdCallback(String tableName) {
1571  this.tableName = tableName;
1572  }
1573 
1574  @Override
1575  public void process(ResultSet rs) {
1576 
1577  try {
1578  while (rs.next()) {
1579  try {
1580  Long maxId = rs.getLong("max_id"); // NON-NLS
1581  String query = " (table_name, max_id) VALUES ('" + tableName + "', '" + maxId + "')"; // NON-NLS
1582  portableSkCase.getCaseDbAccessManager().insert(MAX_ID_TABLE_NAME, query);
1583 
1584  } catch (SQLException ex) {
1585  logger.log(Level.WARNING, "Unable to get maximum ID from result set", ex); // NON-NLS
1586  } catch (TskCoreException ex) {
1587  logger.log(Level.WARNING, "Unable to save maximum ID from result set", ex); // NON-NLS
1588  }
1589 
1590  }
1591  } catch (SQLException ex) {
1592  logger.log(Level.WARNING, "Failed to get maximum ID from result set", ex); // NON-NLS
1593  }
1594  }
1595  }
1596 
1597  @NbBundle.Messages({
1598  "PortableCaseReportModule.compressCase.errorFinding7zip=Could not locate 7-Zip executable",
1599  "# {0} - Temp folder path",
1600  "PortableCaseReportModule.compressCase.errorCreatingTempFolder=Could not create temporary folder {0}",
1601  "PortableCaseReportModule.compressCase.errorCompressingCase=Error compressing case",
1602  "PortableCaseReportModule.compressCase.canceled=Compression canceled by user",})
1603  private boolean compressCase(ReportProgressPanel progressPanel, String folderToCompress) {
1604 
1606 
1607  // Make a temporary folder for the compressed case
1608  Path dirToCompress = Paths.get(folderToCompress);
1609  File tempZipFolder = Paths.get(dirToCompress.getParent().toString(), "temp", "portableCase" + System.currentTimeMillis()).toFile();
1610  if (!tempZipFolder.mkdirs()) {
1611  handleError("Error creating temporary folder " + tempZipFolder.toString(),
1612  Bundle.PortableCaseReportModule_compressCase_errorCreatingTempFolder(tempZipFolder.toString()), null, progressPanel); // NON-NLS
1613  return false;
1614  }
1615 
1616  // Find 7-Zip
1617  File sevenZipExe = locate7ZipExecutable();
1618  if (sevenZipExe == null) {
1619  handleError("Error finding 7-Zip exectuable", Bundle.PortableCaseReportModule_compressCase_errorFinding7zip(), null, progressPanel); // NON-NLS
1620  return false;
1621  }
1622 
1623  // Create the chunk option
1624  String chunkOption = "";
1626  chunkOption = "-v" + settings.getChunkSize().getSevenZipParam();
1627  }
1628 
1629  File zipFile = Paths.get(tempZipFolder.getAbsolutePath(), caseName + ".zip").toFile(); // NON-NLS
1630  ProcessBuilder procBuilder = new ProcessBuilder();
1631  procBuilder.command(
1632  sevenZipExe.getAbsolutePath(),
1633  "a", // Add to archive
1634  zipFile.getAbsolutePath(),
1635  dirToCompress.toAbsolutePath().toString(),
1636  chunkOption
1637  );
1638 
1639  try {
1640  Process process = procBuilder.start();
1641 
1642  while (process.isAlive()) {
1643  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
1644  process.destroy();
1645  return false;
1646  }
1647  Thread.sleep(200);
1648  }
1649  int exitCode = process.exitValue();
1650  if (exitCode != 0) {
1651  // Save any errors so they can be logged
1652  StringBuilder sb = new StringBuilder();
1653  try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
1654  String line;
1655  while ((line = br.readLine()) != null) {
1656  sb.append(line).append(System.getProperty("line.separator")); // NON-NLS
1657  }
1658  }
1659 
1660  handleError("Error compressing case\n7-Zip output: " + sb.toString(), Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), null, progressPanel); // NON-NLS
1661  return false;
1662  }
1663  } catch (IOException | InterruptedException ex) {
1664  handleError("Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel); // NON-NLS
1665  return false;
1666  }
1667 
1668  // Delete everything in the case folder then copy over the compressed file(s)
1669  try {
1670  FileUtils.cleanDirectory(dirToCompress.toFile());
1671  FileUtils.copyDirectory(tempZipFolder, dirToCompress.toFile());
1672  FileUtils.deleteDirectory(new File(tempZipFolder.getParent()));
1673  } catch (IOException ex) {
1674  handleError("Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel); // NON-NLS
1675  return false;
1676  }
1677 
1678  return true;
1679  }
1680 
1686  private static File locate7ZipExecutable() {
1687  if (!PlatformUtil.isWindowsOS()) {
1688  return null;
1689  }
1690 
1691  String executableToFindName = Paths.get("7-Zip", "7z.exe").toString(); // NON-NLS
1692  File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, PortableCaseReportModule.class.getPackage().getName(), false);
1693  if (null == exeFile) {
1694  return null;
1695  }
1696 
1697  if (!exeFile.canExecute()) {
1698  return null;
1699  }
1700 
1701  return exeFile;
1702  }
1703 
1707  public static class GetInterestingItemSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
1708 
1709  private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(GetInterestingItemSetNamesCallback.class.getName());
1710  private final Map<String, Long> setCounts = new HashMap<>();
1711 
1712  @Override
1713  public void process(ResultSet rs) {
1714  try {
1715  while (rs.next()) {
1716  try {
1717  Long setCount = rs.getLong("set_count"); // NON-NLS
1718  String setName = rs.getString("set_name"); // NON-NLS
1719 
1720  setCounts.put(setName, setCount);
1721 
1722  } catch (SQLException ex) {
1723  logger.log(Level.WARNING, "Unable to get data_source_obj_id or value from result set", ex); // NON-NLS
1724  }
1725  }
1726  } catch (SQLException ex) {
1727  logger.log(Level.WARNING, "Failed to get next result for values by datasource", ex); // NON-NLS
1728  }
1729  }
1730 
1736  public Map<String, Long> getSetCountMap() {
1737  return setCounts;
1738  }
1739  }
1740 }
void handleError(String logWarning, String dialogWarning, Exception ex, ReportProgressPanel progressPanel)
long copyContentToPortableCase(Content content, ReportProgressPanel progressPanel)
void addArtifactsToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel)
void copyAttachments(BlackboardArtifact newArtifact, BlackboardArtifact oldArtifact, AbstractFile newFile)
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 copyPathID(BlackboardArtifact newArtifact, BlackboardArtifact oldArtifact)
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:2503
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-2021 Basis Technology. Generated on: Thu Sep 30 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.