Autopsy  4.19.3
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-2021 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  })
249  @SuppressWarnings("deprecation")
250  public void generateReport(String reportPath, PortableCaseReportModuleSettings options, ReportProgressPanel progressPanel) {
251  this.settings = options;
252  progressPanel.setIndeterminate(true);
253  progressPanel.start();
254  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_verifying());
255 
256  // Clear out any old values
257  cleanup();
258 
259  // Validate the input parameters
260  File outputDir = new File(reportPath);
261  if (!outputDir.exists()) {
262  handleError("Output folder " + outputDir.toString() + " does not exist",
263  Bundle.PortableCaseReportModule_generateReport_outputDirDoesNotExist(outputDir.toString()), null, progressPanel); // NON-NLS
264  return;
265  }
266 
267  if (!outputDir.isDirectory()) {
268  handleError("Output folder " + outputDir.toString() + " is not a folder",
269  Bundle.PortableCaseReportModule_generateReport_outputDirIsNotDir(outputDir.toString()), null, progressPanel); // NON-NLS
270  return;
271  }
272 
273  // Save the current case object
274  try {
275  currentCase = Case.getCurrentCaseThrows();
276  caseName = currentCase.getDisplayName() + " (Portable)"; // NON-NLS
277  } catch (NoCurrentCaseException ex) {
278  handleError("Current case has been closed",
279  Bundle.PortableCaseReportModule_generateReport_caseClosed(), null, progressPanel); // NON-NLS
280  return;
281  }
282 
283  // If the applciation is included add an extra level to the directory structure
284  if (options.includeApplication()) {
285  outputDir = Paths.get(outputDir.toString(), caseName).toFile();
286  }
287  // Check that there will be something to copy
288  List<TagName> tagNames;
289  if (options.areAllTagsSelected()) {
290  try {
292  } catch (NoCurrentCaseException | TskCoreException ex) {
293  handleError("Unable to get all tags",
294  Bundle.PortableCaseReportModule_generateReport_errorReadingTags(), ex, progressPanel); // NON-NLS
295  return;
296  }
297  } else {
298  tagNames = options.getSelectedTagNames();
299  }
300 
301  List<String> setNames;
302  if (options.areAllSetsSelected()) {
303  try {
304  setNames = getAllInterestingItemsSets();
305  } catch (NoCurrentCaseException | TskCoreException ex) {
306  handleError("Unable to get all interesting items sets",
307  Bundle.PortableCaseReportModule_generateReport_errorReadingSets(), ex, progressPanel); // NON-NLS
308  return;
309  }
310  } else {
311  setNames = options.getSelectedSetNames();
312  }
313 
314  if (tagNames.isEmpty() && setNames.isEmpty()) {
315  handleError("No content to copy",
316  Bundle.PortableCaseReportModule_generateReport_noContentToCopy(), null, progressPanel); // NON-NLS
317  return;
318  }
319 
320  // Create the case.
321  // portableSkCase and caseFolder will be set here.
322  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_creatingCase());
323  createCase(outputDir, progressPanel);
324  if (portableSkCase == null) {
325  // The error has already been handled
326  return;
327  }
328 
329  // Check for cancellation
330  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
331  handleCancellation(progressPanel);
332  return;
333  }
334 
335  // Set up the table for the image tags
336  try {
337  initializeImageTags(progressPanel);
338  } catch (TskCoreException ex) {
339  handleError("Error creating image tag table", Bundle.PortableCaseReportModule_generateReport_errorCreatingImageTagTable(), ex, progressPanel); // NON-NLS
340  return;
341  }
342 
343  // Copy the selected tags
344  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingTags());
345  try {
346  for (TagName tagName : tagNames) {
347  TagName newTagName = portableSkCase.getTaggingManager().addOrUpdateTagName(tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus());
348  oldTagNameToNewTagName.put(tagName, newTagName);
349  }
350  } catch (TskCoreException ex) {
351  handleError("Error copying tags", Bundle.PortableCaseReportModule_generateReport_errorCopyingTags(), ex, progressPanel); // NON-NLS
352  return;
353  }
354 
355  // Set up tracking to support any custom artifact or attribute types
356  for (BlackboardArtifact.ARTIFACT_TYPE type : BlackboardArtifact.ARTIFACT_TYPE.values()) {
357  oldArtTypeIdToNewArtTypeId.put(type.getTypeID(), type.getTypeID());
358  }
359  for (BlackboardAttribute.ATTRIBUTE_TYPE type : BlackboardAttribute.ATTRIBUTE_TYPE.values()) {
360  try {
361  oldAttrTypeIdToNewAttrType.put(type.getTypeID(), portableSkCase.getBlackboard().getAttributeType(type.getLabel()));
362  } catch (TskCoreException ex) {
363  handleError("Error looking up attribute name " + type.getLabel(),
364  Bundle.PortableCaseReportModule_generateReport_errorLookingUpAttrType(type.getLabel()),
365  ex, progressPanel); // NON-NLS
366  }
367  }
368 
369  // Copy the tagged files
370  try {
371  for (TagName tagName : tagNames) {
372  // Check for cancellation
373  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
374  handleCancellation(progressPanel);
375  return;
376  }
377  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingFiles(tagName.getDisplayName()));
378  addFilesToPortableCase(tagName, progressPanel);
379 
380  // Check for cancellation
381  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
382  handleCancellation(progressPanel);
383  return;
384  }
385  }
386  } catch (TskCoreException ex) {
387  handleError("Error copying tagged files", Bundle.PortableCaseReportModule_generateReport_errorCopyingFiles(), ex, progressPanel); // NON-NLS
388  return;
389  }
390 
391  // Copy the tagged artifacts and associated files
392  try {
393  for (TagName tagName : tagNames) {
394  // Check for cancellation
395  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
396  handleCancellation(progressPanel);
397  return;
398  }
399  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingArtifacts(tagName.getDisplayName()));
400  addArtifactsToPortableCase(tagName, progressPanel);
401 
402  // Check for cancellation
403  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
404  handleCancellation(progressPanel);
405  return;
406  }
407  }
408  } catch (TskCoreException ex) {
409  handleError("Error copying tagged artifacts", Bundle.PortableCaseReportModule_generateReport_errorCopyingArtifacts(), ex, progressPanel); // NON-NLS
410  return;
411  }
412 
413  // Copy interesting files and results
414  if (!setNames.isEmpty()) {
415  try {
416  List<AnalysisResult> interestingFiles = currentCase.getSleuthkitCase().getBlackboard().getAnalysisResultsByType(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID());
417  for (AnalysisResult art : interestingFiles) {
418  // Check for cancellation
419  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
420  handleCancellation(progressPanel);
421  return;
422  }
423 
424  BlackboardAttribute setAttr = art.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
425  if (setNames.contains(setAttr.getValueString())) {
426  copyContentToPortableCase(art, progressPanel);
427  }
428  }
429  } catch (TskCoreException ex) {
430  handleError("Error copying interesting files", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingFiles(), ex, progressPanel); // NON-NLS
431  return;
432  }
433 
434  try {
435  List<AnalysisResult> interestingResults = currentCase.getSleuthkitCase().getBlackboard().getAnalysisResultsByType(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID());
436  for (AnalysisResult art : interestingResults) {
437  // Check for cancellation
438  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
439  handleCancellation(progressPanel);
440  return;
441  }
442  BlackboardAttribute setAttr = art.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
443  if (setNames.contains(setAttr.getValueString())) {
444  copyContentToPortableCase(art, progressPanel);
445  }
446  }
447  } catch (TskCoreException ex) {
448  handleError("Error copying interesting results", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingResults(), ex, progressPanel); // NON-NLS
449  return;
450  }
451 
452  try {
453  List<AnalysisResult> interestingResults = currentCase.getSleuthkitCase().getBlackboard().getAnalysisResultsByType(BlackboardArtifact.Type.TSK_INTERESTING_ITEM.getTypeID());
454  for (AnalysisResult art : interestingResults) {
455  // Check for cancellation
456  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
457  handleCancellation(progressPanel);
458  return;
459  }
460  BlackboardAttribute setAttr = art.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
461  if (setNames.contains(setAttr.getValueString())) {
462  copyContentToPortableCase(art, progressPanel);
463  }
464  }
465  } catch (TskCoreException ex) {
466  handleError("Error copying interesting items", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingResults(), ex, progressPanel); // NON-NLS
467  return;
468  }
469  }
470 
471  // Check for cancellation
472  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
473  handleCancellation(progressPanel);
474  return;
475  }
476 
477  //Attempt to generate and included the CASE-UCO report.
478  generateCaseUcoReport(tagNames, setNames, progressPanel);
479 
480  if (options.includeApplication()) {
481  try {
482  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingAutopsy());
483  copyApplication(getApplicationBasePath(), outputDir.getAbsolutePath());
484  createAppLaunchBatFile(outputDir.getAbsolutePath());
485  } catch (IOException ex) {
486  handleError("Error copying autopsy", Bundle.PortableCaseReportModule_generateReport_errorCopyingAutopsy(), ex, progressPanel); // NON-NLS
487  }
488  }
489 
490  // Compress the case (if desired)
491  if (options.shouldCompress()) {
492  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_compressingCase());
493 
494  if (!compressCase(progressPanel, options.includeApplication() ? outputDir.getAbsolutePath() : caseFolder.getAbsolutePath())) {
495  // Errors have been handled already
496  return;
497  }
498 
499  // Check for cancellation
500  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
501  handleCancellation(progressPanel);
502  return;
503  }
504  }
505 
506  // Close the case connections and clear out the maps
507  cleanup();
508 
510 
511  }
512 
524  @NbBundle.Messages({
525  "PortableCaseReportModule.generateCaseUcoReport.errorCreatingReportFolder=Could not make report folder",
526  "PortableCaseReportModule.generateCaseUcoReport.errorGeneratingCaseUcoReport=Problem while generating CASE-UCO report",
527  "PortableCaseReportModule.generateCaseUcoReport.startCaseUcoReportGeneration=Creating a CASE-UCO report of the portable case",
528  "PortableCaseReportModule.generateCaseUcoReport.successCaseUcoReportGeneration=Successfully created a CASE-UCO report of the portable case"
529  })
530  private void generateCaseUcoReport(List<TagName> tagNames, List<String> setNames, ReportProgressPanel progressPanel) {
531  //Create the 'Reports' directory to include a CASE-UCO report.
532  Path reportsDirectory = Paths.get(caseFolder.toString(), "Reports");
533  if (!reportsDirectory.toFile().mkdir()) {
534  logger.log(Level.SEVERE, "Could not make the report folder... skipping "
535  + "CASE-UCO report generation for the portable case");
536  return;
537  }
538 
539  Path reportFile = reportsDirectory.resolve(CASE_UCO_FILE_NAME);
540 
541  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_startCaseUcoReportGeneration());
542  try (OutputStream stream = new FileOutputStream(reportFile.toFile());
543  JsonWriter reportWriter = new JsonWriter(new OutputStreamWriter(stream, "UTF-8"))) {
544  Gson gson = new GsonBuilder().setPrettyPrinting().create();
545  reportWriter.setIndent(" ");
546  reportWriter.beginObject();
547  reportWriter.name("@graph");
548  reportWriter.beginArray();
549 
550  String caseTempDirectory = currentCase.getTempDirectory();
551  SleuthkitCase skCase = currentCase.getSleuthkitCase();
552  TagsManager tagsManager = currentCase.getServices().getTagsManager();
553 
554  //Create temp directory to filter out duplicate files.
555  //Clear out the old directory if it exists.
556  Path tmpDir = Paths.get(caseTempDirectory, CASE_UCO_TMP_DIR);
557  FileUtils.deleteDirectory(tmpDir.toFile());
558  Files.createDirectory(tmpDir);
559 
560  CaseUcoExporter exporter = new CaseUcoExporter(currentCase.getSleuthkitCase());
561  for (JsonElement element : exporter.exportSleuthkitCase()) {
562  gson.toJson(element, reportWriter);
563  }
564 
565  //Load all interesting BlackboardArtifacts that belong to the selected SET_NAMEs
566  //binned by data source id.
567  Multimap<Long, BlackboardArtifact> artifactsWithSetName = getInterestingArtifactsBySetName(skCase, setNames);
568 
569  //Search each data source looking for content tags and interesting
570  //items that match the selected tag names and set names.
571  for (DataSource dataSource : currentCase.getSleuthkitCase().getDataSources()) {
572  // Helper flag to ensure each data source is only written once in
573  // a report.
574  boolean dataSourceHasBeenIncluded = false;
575 
576  //Search content tags and artifact tags that match
577  for (TagName tagName : tagNames) {
578  for (ContentTag ct : tagsManager.getContentTagsByTagName(tagName, dataSource.getId())) {
579  dataSourceHasBeenIncluded |= addUniqueFile(ct.getContent(),
580  dataSource, tmpDir, gson, exporter, reportWriter, dataSourceHasBeenIncluded);
581  }
582  for (BlackboardArtifactTag bat : tagsManager.getBlackboardArtifactTagsByTagName(tagName, dataSource.getId())) {
583  dataSourceHasBeenIncluded |= addUniqueFile(bat.getContent(),
584  dataSource, tmpDir, gson, exporter, reportWriter, dataSourceHasBeenIncluded);
585  }
586  }
587  //Search artifacts that this data source contains
588  for (BlackboardArtifact bArt : artifactsWithSetName.get(dataSource.getId())) {
589  Content sourceContent = bArt.getParent();
590  dataSourceHasBeenIncluded |= addUniqueFile(sourceContent, dataSource,
591  tmpDir, gson, exporter, reportWriter, dataSourceHasBeenIncluded);
592  }
593  }
594 
595  // Finish the report.
596  reportWriter.endArray();
597  reportWriter.endObject();
598  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_successCaseUcoReportGeneration());
599  } catch (IOException | TskCoreException ex) {
600  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_errorGeneratingCaseUcoReport());
601  logger.log(Level.SEVERE, "Error encountered while trying to create "
602  + "CASE-UCO output for portable case.. the portable case will be "
603  + "completed without a CASE-UCO report.", ex);
604  }
605  }
606 
615  @SuppressWarnings("deprecation")
616  private Multimap<Long, BlackboardArtifact> getInterestingArtifactsBySetName(SleuthkitCase skCase, List<String> setNames) throws TskCoreException {
617  Multimap<Long, BlackboardArtifact> artifactsWithSetName = ArrayListMultimap.create();
618  if (!setNames.isEmpty()) {
619  List<BlackboardArtifact> allArtifacts = skCase.getBlackboardArtifacts(
620  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
621  allArtifacts.addAll(skCase.getBlackboardArtifacts(
622  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT));
623  allArtifacts.addAll(skCase.getBlackboardArtifacts(
624  BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ITEM));
625 
626  for (BlackboardArtifact bArt : allArtifacts) {
627  BlackboardAttribute setAttr = bArt.getAttribute(
628  new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
629  if (setNames.contains(setAttr.getValueString())) {
630  artifactsWithSetName.put(bArt.getDataSource().getId(), bArt);
631  }
632  }
633  }
634  return artifactsWithSetName;
635  }
636 
657  private boolean addUniqueFile(Content content, DataSource dataSource,
658  Path tmpDir, Gson gson, CaseUcoExporter exporter, JsonWriter reportWriter,
659  boolean dataSourceHasBeenIncluded) throws IOException, TskCoreException {
660  if (content instanceof AbstractFile && !(content instanceof DataSource)) {
661  AbstractFile absFile = (AbstractFile) content;
662  Path filePath = tmpDir.resolve(Long.toString(absFile.getId()));
663  if (!absFile.isDir() && !Files.exists(filePath)) {
664  if (!dataSourceHasBeenIncluded) {
665  for (JsonElement element : exporter.exportDataSource(dataSource)) {
666  gson.toJson(element, reportWriter);
667  }
668  }
669  String subFolder = getExportSubfolder(absFile);
670  String fileName = absFile.getId() + "-" + FileUtil.escapeFileName(absFile.getName());
671  for (JsonElement element : exporter.exportAbstractFile(absFile, Paths.get(FILE_FOLDER_NAME, subFolder, fileName).toString())) {
672  gson.toJson(element, reportWriter);
673  }
674  Files.createFile(filePath);
675  return true;
676  }
677  }
678  return false;
679  }
680 
685  @SuppressWarnings("deprecation")
686  private List<String> getAllInterestingItemsSets() throws NoCurrentCaseException, TskCoreException {
687 
688  // Get the set names in use for the current case.
689  List<String> setNames = new ArrayList<>();
690  Map<String, Long> setCounts;
691 
692  // There may not be a case open when configuring report modules for Command Line execution
693  // Get all SET_NAMEs from interesting item artifacts
694  String innerSelect = "SELECT (value_text) AS set_name FROM blackboard_attributes WHERE (artifact_type_id = '"
695  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + "' OR artifact_type_id = '"
696  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ITEM.getTypeID() + "' OR artifact_type_id = '"
697  + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() + "') AND attribute_type_id = '"
698  + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + "'"; // NON-NLS
699 
700  // Get the count of each SET_NAME
701  String query = "set_name, count(1) AS set_count FROM (" + innerSelect + ") set_names GROUP BY set_name"; // NON-NLS
702 
704  Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select(query, callback);
705  setCounts = callback.getSetCountMap();
706  setNames.addAll(setCounts.keySet());
707  return setNames;
708  }
709 
717  @NbBundle.Messages({
718  "# {0} - case folder",
719  "PortableCaseReportModule.createCase.caseDirExists=Case folder {0} already exists",
720  "PortableCaseReportModule.createCase.errorCreatingCase=Error creating case",
721  "# {0} - folder",
722  "PortableCaseReportModule.createCase.errorCreatingFolder=Error creating folder {0}",
723  "PortableCaseReportModule.createCase.errorStoringMaxIds=Error storing maximum database IDs",})
724  private void createCase(File outputDir, ReportProgressPanel progressPanel) {
725 
726  // Create the case folder
727  caseFolder = Paths.get(outputDir.toString(), caseName).toFile();
728 
729  if (caseFolder.exists()) {
730  handleError("Case folder " + caseFolder.toString() + " already exists",
731  Bundle.PortableCaseReportModule_createCase_caseDirExists(caseFolder.toString()), null, progressPanel); // NON-NLS
732  return;
733  }
734 
735  // Create the case
736  try {
737  portableSkCase = currentCase.createPortableCase(caseName, caseFolder);
738  } catch (TskCoreException ex) {
739  handleError("Error creating case " + caseName + " in folder " + caseFolder.toString(),
740  Bundle.PortableCaseReportModule_createCase_errorCreatingCase(), ex, progressPanel); // NON-NLS
741  return;
742  }
743 
744  // Store the highest IDs
745  try {
746  saveHighestIds();
747  } catch (TskCoreException ex) {
748  handleError("Error storing maximum database IDs",
749  Bundle.PortableCaseReportModule_createCase_errorStoringMaxIds(), ex, progressPanel); // NON-NLS
750  return;
751  }
752 
753  // Create the base folder for the copied files
754  copiedFilesFolder = Paths.get(caseFolder.toString(), FILE_FOLDER_NAME).toFile();
755  if (!copiedFilesFolder.mkdir()) {
756  handleError("Error creating folder " + copiedFilesFolder.toString(),
757  Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(copiedFilesFolder.toString()), null, progressPanel); // NON-NLS
758  return;
759  }
760 
761  // Create subfolders for the copied files
762  for (FileTypeCategory cat : FILE_TYPE_CATEGORIES) {
763  File subFolder = Paths.get(copiedFilesFolder.toString(), cat.getDisplayName()).toFile();
764  if (!subFolder.mkdir()) {
765  handleError("Error creating folder " + subFolder.toString(),
766  Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(subFolder.toString()), null, progressPanel); // NON-NLS
767  return;
768  }
769  }
770  File unknownTypeFolder = Paths.get(copiedFilesFolder.toString(), UNKNOWN_FILE_TYPE_FOLDER).toFile();
771  if (!unknownTypeFolder.mkdir()) {
772  handleError("Error creating folder " + unknownTypeFolder.toString(),
773  Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(unknownTypeFolder.toString()), null, progressPanel); // NON-NLS
774  return;
775  }
776 
777  }
778 
784  private void saveHighestIds() throws TskCoreException {
785 
786  CaseDbAccessManager currentCaseDbManager = currentCase.getSleuthkitCase().getCaseDbAccessManager();
787 
788  String tableSchema = "( table_name TEXT PRIMARY KEY, "
789  + " max_id TEXT)"; // NON-NLS
790 
791  portableSkCase.getCaseDbAccessManager().createTable(MAX_ID_TABLE_NAME, tableSchema);
792 
793  currentCaseDbManager.select("max(obj_id) as max_id from tsk_objects", new StoreMaxIdCallback("tsk_objects")); // NON-NLS
794  currentCaseDbManager.select("max(tag_id) as max_id from content_tags", new StoreMaxIdCallback("content_tags")); // NON-NLS
795  currentCaseDbManager.select("max(tag_id) as max_id from blackboard_artifact_tags", new StoreMaxIdCallback("blackboard_artifact_tags")); // NON-NLS
796  currentCaseDbManager.select("max(examiner_id) as max_id from tsk_examiners", new StoreMaxIdCallback("tsk_examiners")); // NON-NLS
797  }
798 
806  private void initializeImageTags(ReportProgressPanel progressPanel) throws TskCoreException {
807 
808  // Create the image tags table in the portable case
809  CaseDbAccessManager portableDbAccessManager = portableSkCase.getCaseDbAccessManager();
810  if (!portableDbAccessManager.tableExists(ContentViewerTagManager.TABLE_NAME)) {
812  }
813  }
814 
823  private void addFilesToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel) throws TskCoreException {
824 
825  // Get all the tags in the current case
826  List<ContentTag> tags = currentCase.getServices().getTagsManager().getContentTagsByTagName(oldTagName);
827 
828  // Copy the files into the portable case and tag
829  for (ContentTag tag : tags) {
830 
831  // Check for cancellation
832  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
833  return;
834  }
835 
836  Content content = tag.getContent();
837  if (content instanceof AbstractFile) {
838 
839  long newFileId = copyContentToPortableCase(content, progressPanel);
840 
841  // Tag the file
842  if (!oldTagNameToNewTagName.containsKey(tag.getName())) {
843  throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS
844  }
845  ContentTagChange newContentTag = portableSkCase.getTaggingManager().addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
846 
847  // Get the image tag data associated with this tag (empty string if there is none)
848  // and save it if present
849  String appData = getImageTagDataForContentTag(tag);
850  if (!appData.isEmpty()) {
851  addImageTagToPortableCase(newContentTag.getAddedTag(), appData);
852  }
853  }
854  }
855  }
856 
867  private String getImageTagDataForContentTag(ContentTag tag) throws TskCoreException {
868 
869  GetImageTagCallback callback = new GetImageTagCallback();
870  String query = "* FROM " + ContentViewerTagManager.TABLE_NAME + " WHERE content_tag_id = " + tag.getId();
871  currentCase.getSleuthkitCase().getCaseDbAccessManager().select(query, callback);
872  return callback.getAppData();
873  }
874 
878  private static class GetImageTagCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
879 
880  private static final Logger logger = Logger.getLogger(PortableCaseReportModule.class.getName());
881  private String appData = "";
882 
883  @Override
884  public void process(ResultSet rs) {
885  try {
886  while (rs.next()) {
887  try {
888  appData = rs.getString("app_data"); // NON-NLS
889  } catch (SQLException ex) {
890  logger.log(Level.WARNING, "Unable to get app_data from result set", ex); // NON-NLS
891  }
892  }
893  } catch (SQLException ex) {
894  logger.log(Level.WARNING, "Failed to get next result for app_data", ex); // NON-NLS
895  }
896  }
897 
903  String getAppData() {
904  return appData;
905  }
906  }
907 
916  private void addImageTagToPortableCase(ContentTag newContentTag, String appData) throws TskCoreException {
917  String insert = "(content_tag_id, app_data) VALUES (" + newContentTag.getId() + ", '" + appData + "')";
918  portableSkCase.getCaseDbAccessManager().insert(ContentViewerTagManager.TABLE_NAME, insert);
919  }
920 
929  private void addArtifactsToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel) throws TskCoreException {
930 
931  List<BlackboardArtifactTag> tags = currentCase.getServices().getTagsManager().getBlackboardArtifactTagsByTagName(oldTagName);
932 
933  // Copy the artifacts into the portable case along with their content and tag
934  for (BlackboardArtifactTag tag : tags) {
935 
936  // Check for cancellation
937  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
938  return;
939  }
940 
941  // Copy the source content
942  Content content = tag.getContent();
943  long newContentId = copyContentToPortableCase(content, progressPanel);
944 
945  // Copy the artifact
946  BlackboardArtifact newArtifact = copyArtifact(newContentId, tag.getArtifact());
947 
948  // Copy any attachments
949  copyAttachments(newArtifact, tag.getArtifact(), portableSkCase.getAbstractFileById(newContentId));
950 
951  // Copy any files associated with this artifact through the TSK_PATH_ID attribute
952  copyPathID(newArtifact, tag.getArtifact());
953 
954  // Tag the artfiact
955  if (!oldTagNameToNewTagName.containsKey(tag.getName())) {
956  throw new TskCoreException("TagName map is missing entry for ID " + tag.getName().getId() + " with display name " + tag.getName().getDisplayName()); // NON-NLS
957  }
958  portableSkCase.getTaggingManager().addArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment());
959  }
960  }
961 
974  private BlackboardArtifact copyArtifact(long newContentId, BlackboardArtifact artifactToCopy) throws TskCoreException {
975 
976  if (oldArtifactIdToNewArtifact.containsKey(artifactToCopy.getArtifactID())) {
977  return oldArtifactIdToNewArtifact.get(artifactToCopy.getArtifactID());
978  }
979 
980  // First create the associated artifact (if present)
981  BlackboardAttribute oldAssociatedAttribute = artifactToCopy.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
982  List<BlackboardAttribute> newAttrs = new ArrayList<>();
983  if (oldAssociatedAttribute != null) {
984  BlackboardArtifact oldAssociatedArtifact = currentCase.getSleuthkitCase().getBlackboardArtifact(oldAssociatedAttribute.getValueLong());
985  BlackboardArtifact newAssociatedArtifact = copyArtifact(newContentId, oldAssociatedArtifact);
986  newAttrs.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
987  String.join(",", oldAssociatedAttribute.getSources()), newAssociatedArtifact.getArtifactID()));
988  }
989 
990  List<BlackboardAttribute> oldAttrs = artifactToCopy.getAttributes();
991 
992  // Copy over each attribute, making sure the type is in the new case.
993  for (BlackboardAttribute oldAttr : oldAttrs) {
994 
995  // Skip attributes that are handled elsewhere
996  if (SPECIALLY_HANDLED_ATTRS.contains(oldAttr.getAttributeType().getTypeID())) {
997  continue;
998  }
999 
1000  BlackboardAttribute.Type newAttributeType = getNewAttributeType(oldAttr);
1001  switch (oldAttr.getValueType()) {
1002  case BYTE:
1003  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
1004  oldAttr.getValueBytes()));
1005  break;
1006  case DOUBLE:
1007  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
1008  oldAttr.getValueDouble()));
1009  break;
1010  case INTEGER:
1011  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
1012  oldAttr.getValueInt()));
1013  break;
1014  case DATETIME:
1015  case LONG:
1016  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
1017  oldAttr.getValueLong()));
1018  break;
1019  case STRING:
1020  case JSON:
1021  newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()),
1022  oldAttr.getValueString()));
1023  break;
1024  default:
1025  throw new TskCoreException("Unexpected attribute value type found: " + oldAttr.getValueType().getLabel()); // NON-NLS
1026  }
1027  }
1028 
1029  // Figure out the data source ID. We can't always get it from newContent because it could be null
1030  // for OS accounts, which means we also can't assume it's been added to the case already.
1031  Long newDataSourceId;
1032  if (newIdToContent.get(newContentId).getDataSource() != null) {
1033  // We can use the new content to get the id since the data source is in its parent hierarchy.
1034  // Everything would have already been copied to the portable case.
1035  newDataSourceId = newIdToContent.get(newContentId).getDataSource().getId();
1036  } else {
1037  // The newContent has no data source parent, so we'll have to use the old artifact.
1038  if (artifactToCopy.getDataSource() == null) {
1039  // Shouldn't happen with the current code
1040  throw new TskCoreException("Can not copy artifact with ID: " + artifactToCopy.getArtifactID() + " because it is not associated with a data source");
1041  }
1042  newDataSourceId = copyContent(artifactToCopy.getDataSource());
1043  }
1044 
1045  // Create the new artifact
1046  int newArtifactTypeId = getNewArtifactTypeId(artifactToCopy);
1047  BlackboardArtifact.Type newArtifactType = portableSkCase.getBlackboard().getArtifactType(newArtifactTypeId);
1048  BlackboardArtifact newArtifact;
1049 
1050  // First, check if the artifact being copied is an AnalysisResult or a DataArtifact. If it
1051  // is neither, attempt to reload it as the appropriate subclass.
1052  if (!((artifactToCopy instanceof AnalysisResult) || (artifactToCopy instanceof DataArtifact))) {
1053  try {
1054  if (newArtifactType.getCategory().equals(BlackboardArtifact.Category.ANALYSIS_RESULT)) {
1055  AnalysisResult ar = currentCase.getSleuthkitCase().getBlackboard().getAnalysisResultById(artifactToCopy.getId());
1056  if (ar != null) {
1057  artifactToCopy = ar;
1058  }
1059  } else {
1060  DataArtifact da = currentCase.getSleuthkitCase().getBlackboard().getDataArtifactById(artifactToCopy.getId());
1061  if (da != null) {
1062  artifactToCopy = da;
1063  }
1064  }
1065  } catch (TskCoreException ex) {
1066  // If the lookup failed, just use the orginal BlackboardArtifact
1067  }
1068  }
1069 
1070  try {
1071  if (artifactToCopy instanceof AnalysisResult) {
1072  AnalysisResult analysisResultToCopy = (AnalysisResult) artifactToCopy;
1073  newArtifact = portableSkCase.getBlackboard().newAnalysisResult(newArtifactType, newContentId,
1074  newDataSourceId, analysisResultToCopy.getScore(),
1075  analysisResultToCopy.getConclusion(), analysisResultToCopy.getConfiguration(),
1076  analysisResultToCopy.getJustification(), newAttrs).getAnalysisResult();
1077  } else if (artifactToCopy instanceof DataArtifact) {
1078  DataArtifact dataArtifactToCopy = (DataArtifact) artifactToCopy;
1079  Long newOsAccountId = null;
1080  if (dataArtifactToCopy.getOsAccountObjectId().isPresent()) {
1081  copyOsAccount(dataArtifactToCopy.getOsAccountObjectId().get());
1082  newOsAccountId = oldOsAccountIdToNewOsAccount.get((dataArtifactToCopy.getOsAccountObjectId().get())).getId();
1083  }
1084  newArtifact = portableSkCase.getBlackboard().newDataArtifact(newArtifactType, newContentId,
1085  newDataSourceId,
1086  newAttrs, newOsAccountId);
1087  } else {
1088  if (newArtifactType.getCategory().equals(BlackboardArtifact.Category.ANALYSIS_RESULT)) {
1089  newArtifact = portableSkCase.getBlackboard().newAnalysisResult(newArtifactType, newContentId,
1090  newDataSourceId, Score.SCORE_NONE,
1091  null, null, null, newAttrs).getAnalysisResult();
1092  } else {
1093  newArtifact = portableSkCase.getBlackboard().newDataArtifact(newArtifactType, newContentId,
1094  newDataSourceId,
1095  newAttrs, null);
1096  }
1097  }
1098  } catch (BlackboardException ex) {
1099  throw new TskCoreException("Error copying artifact with ID: " + artifactToCopy.getId());
1100  }
1101 
1102  oldArtifactIdToNewArtifact.put(artifactToCopy.getArtifactID(), newArtifact);
1103  return newArtifact;
1104  }
1105 
1115  private int getNewArtifactTypeId(BlackboardArtifact oldArtifact) throws TskCoreException {
1116  if (oldArtTypeIdToNewArtTypeId.containsKey(oldArtifact.getArtifactTypeID())) {
1117  return oldArtTypeIdToNewArtTypeId.get(oldArtifact.getArtifactTypeID());
1118  }
1119 
1120  BlackboardArtifact.Type oldCustomType = currentCase.getSleuthkitCase().getBlackboard().getArtifactType(oldArtifact.getArtifactTypeName());
1121  try {
1122  BlackboardArtifact.Type newCustomType = portableSkCase.getBlackboard().getOrAddArtifactType(oldCustomType.getTypeName(), oldCustomType.getDisplayName());
1123  oldArtTypeIdToNewArtTypeId.put(oldArtifact.getArtifactTypeID(), newCustomType.getTypeID());
1124  return newCustomType.getTypeID();
1125  } catch (BlackboardException ex) {
1126  throw new TskCoreException("Error creating new artifact type " + oldCustomType.getTypeName(), ex); // NON-NLS
1127  }
1128  }
1129 
1139  private BlackboardAttribute.Type getNewAttributeType(BlackboardAttribute oldAttribute) throws TskCoreException {
1140  BlackboardAttribute.Type oldAttrType = oldAttribute.getAttributeType();
1141  if (oldAttrTypeIdToNewAttrType.containsKey(oldAttrType.getTypeID())) {
1142  return oldAttrTypeIdToNewAttrType.get(oldAttrType.getTypeID());
1143  }
1144 
1145  try {
1146  BlackboardAttribute.Type newCustomType = portableSkCase.getBlackboard().getOrAddAttributeType(oldAttrType.getTypeName(),
1147  oldAttrType.getValueType(), oldAttrType.getDisplayName());
1148  oldAttrTypeIdToNewAttrType.put(oldAttribute.getAttributeType().getTypeID(), newCustomType);
1149  return newCustomType;
1150  } catch (BlackboardException ex) {
1151  throw new TskCoreException("Error creating new attribute type " + oldAttrType.getTypeName(), ex); // NON-NLS
1152  }
1153  }
1154 
1165  @NbBundle.Messages({
1166  "# {0} - File name",
1167  "PortableCaseReportModule.copyContentToPortableCase.copyingFile=Copying file {0}",})
1168  private long copyContentToPortableCase(Content content, ReportProgressPanel progressPanel) throws TskCoreException {
1169  progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_copyContentToPortableCase_copyingFile(content.getUniquePath()));
1170  return copyContent(content);
1171  }
1172 
1182  private long copyContent(Content content) throws TskCoreException {
1183 
1184  // Check if we've already copied this content
1185  if (oldIdToNewContent.containsKey(content.getId())) {
1186  return oldIdToNewContent.get(content.getId()).getId();
1187  }
1188 
1189  // Otherwise:
1190  // - Make parent of this object (if applicable)
1191  // - Copy this content
1192  long parentId = 0;
1193  if (content.getParent() != null) {
1194  parentId = copyContent(content.getParent());
1195  }
1196 
1197  Content newContent;
1198  if (content instanceof BlackboardArtifact) {
1199  BlackboardArtifact artifactToCopy = (BlackboardArtifact) content;
1200  newContent = copyArtifact(parentId, artifactToCopy);
1201  } else if (content instanceof OsAccount) {
1202  newContent = copyOsAccount(content.getId());
1203  } else {
1204  // Get or create the host (if needed) before beginning transaction.
1205  Host newHost = null;
1206  if (content instanceof DataSource) {
1207  newHost = copyHost(((DataSource) content).getHost());
1208  }
1209 
1210  // Copy the associated OS account (if needed) before beginning transaction.
1211  if (content instanceof AbstractFile) {
1212  AbstractFile file = (AbstractFile) content;
1213  if (file.getOsAccountObjectId().isPresent()) {
1214  copyOsAccount(file.getOsAccountObjectId().get());
1215  }
1216  }
1217 
1218  // Load the hashes if we have an image to avoid getting new connections with an open transaction.
1219  String md5 = "";
1220  String sha1 = "";
1221  String sha256 = "";
1222  if (content instanceof Image) {
1223  md5 = ((Image) content).getMd5();
1224  sha1 = ((Image) content).getSha1();
1225  sha256 = ((Image) content).getSha256();
1226  }
1227 
1228  CaseDbTransaction trans = portableSkCase.beginTransaction();
1229  try {
1230  if (content instanceof Image) {
1231  Image image = (Image) content;
1232  newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(),
1233  new ArrayList<>(), image.getTimeZone(), md5, sha1, sha256, image.getDeviceId(), newHost, trans);
1234  } else if (content instanceof VolumeSystem) {
1235  VolumeSystem vs = (VolumeSystem) content;
1236  newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
1237  } else if (content instanceof Volume) {
1238  Volume vs = (Volume) content;
1239  newContent = portableSkCase.addVolume(parentId, vs.getAddr(), vs.getStart(), vs.getLength(),
1240  vs.getDescription(), vs.getFlags(), trans);
1241  } else if (content instanceof Pool) {
1242  Pool pool = (Pool) content;
1243  newContent = portableSkCase.addPool(parentId, pool.getType(), trans);
1244  } else if (content instanceof FileSystem) {
1245  FileSystem fs = (FileSystem) content;
1246  newContent = portableSkCase.addFileSystem(parentId, fs.getImageOffset(), fs.getFsType(), fs.getBlock_size(),
1247  fs.getBlock_count(), fs.getRoot_inum(), fs.getFirst_inum(), fs.getLastInum(),
1248  fs.getName(), trans);
1249  } else if (content instanceof BlackboardArtifact) {
1250  BlackboardArtifact artifactToCopy = (BlackboardArtifact) content;
1251  newContent = copyArtifact(parentId, artifactToCopy);
1252  } else if (content instanceof AbstractFile) {
1253  AbstractFile abstractFile = (AbstractFile) content;
1254 
1255  if (abstractFile instanceof LocalFilesDataSource) {
1256  LocalFilesDataSource localFilesDS = (LocalFilesDataSource) abstractFile;
1257  newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), newHost, trans);
1258  } else {
1259  if (abstractFile.isDir()) {
1260  newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
1261  } else {
1262  try {
1263  // Copy the file
1264  String fileName = abstractFile.getId() + "-" + FileUtil.escapeFileName(abstractFile.getName());
1265  String exportSubFolder = getExportSubfolder(abstractFile);
1266  File exportFolder = Paths.get(copiedFilesFolder.toString(), exportSubFolder).toFile();
1267  File localFile = new File(exportFolder, fileName);
1268  ContentUtils.writeToFile(abstractFile, localFile);
1269 
1270  // Get the new parent object in the portable case database
1271  Content oldParent = abstractFile.getParent();
1272  if (!oldIdToNewContent.containsKey(oldParent.getId())) {
1273  throw new TskCoreException("Parent of file with ID " + abstractFile.getId() + " has not been created"); // NON-NLS
1274  }
1275  Content newParent = oldIdToNewContent.get(oldParent.getId());
1276 
1277  // Construct the relative path to the copied file
1278  String relativePath = FILE_FOLDER_NAME + File.separator + exportSubFolder + File.separator + fileName;
1279 
1280  Long newOsAccountId = null;
1281  if (abstractFile.getOsAccountObjectId().isPresent()) {
1282  newOsAccountId = oldOsAccountIdToNewOsAccount.get(abstractFile.getOsAccountObjectId().get()).getId();
1283  }
1284 
1285  newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
1286  abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getMtime(),
1287  abstractFile.getMd5Hash(), abstractFile.getSha256Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
1288  true, TskData.EncodingType.NONE,
1289  newOsAccountId, abstractFile.getOwnerUid().orElse(null),
1290  newParent, trans);
1291  } catch (IOException ex) {
1292  throw new TskCoreException("Error copying file " + abstractFile.getName() + " with original obj ID "
1293  + abstractFile.getId(), ex); // NON-NLS
1294  }
1295  }
1296  }
1297  } else {
1298  throw new TskCoreException("Trying to copy unexpected Content type " + content.getClass().getName()); // NON-NLS
1299  }
1300  trans.commit();
1301  } catch (TskCoreException ex) {
1302  trans.rollback();
1303  throw (ex);
1304  }
1305  }
1306 
1307  // Save the new object
1308  oldIdToNewContent.put(content.getId(), newContent);
1309  newIdToContent.put(newContent.getId(), newContent);
1310  return oldIdToNewContent.get(content.getId()).getId();
1311  }
1312 
1323  private Host copyHost(Host oldHost) throws TskCoreException {
1324  Host newHost;
1325  if (oldHostIdToNewHost.containsKey(oldHost.getHostId())) {
1326  newHost = oldHostIdToNewHost.get(oldHost.getHostId());
1327  } else {
1328  newHost = portableSkCase.getHostManager().newHost(oldHost.getName());
1329  oldHostIdToNewHost.put(oldHost.getHostId(), newHost);
1330  }
1331  return newHost;
1332  }
1333 
1340  private OsAccount copyOsAccount(Long oldOsAccountId) throws TskCoreException {
1341  // If it has already been copied, we're done.
1342  if (oldOsAccountIdToNewOsAccount.containsKey(oldOsAccountId)) {
1343  return oldOsAccountIdToNewOsAccount.get(oldOsAccountId);
1344  }
1345 
1346  // Load the OS account from the current case.
1347  OsAccountManager oldOsAcctManager = currentCase.getSleuthkitCase().getOsAccountManager();
1348  OsAccount oldOsAccount = oldOsAcctManager.getOsAccountByObjectId(oldOsAccountId);
1349 
1350  // Load the realm associated with the OS account.
1351  OsAccountRealmManager oldRealmManager = currentCase.getSleuthkitCase().getOsAccountRealmManager();
1352  OsAccountRealm oldRealm = oldRealmManager.getRealmByRealmId(oldOsAccount.getRealmId());
1353 
1354  // Copy the realm to the portable case if necessary.
1355  if (!oldRealmIdToNewRealm.containsKey(oldOsAccount.getRealmId())) {
1356  OsAccountRealmManager newRealmManager = portableSkCase.getOsAccountRealmManager();
1357 
1358  Host newHost = null;
1359  if (oldRealm.getScopeHost().isPresent()) {
1360  Host host = oldRealm.getScopeHost().get();
1361  newHost = copyHost(host);
1362  } else {
1363  if (oldRealm.getScope().equals(OsAccountRealm.RealmScope.DOMAIN)) {
1364  // There is currently no way to get a domain-scoped host in Autopsy. When this changes
1365  // we will need to update this code. This will require a new version of newWindowsRealm() that
1366  // does not require a host, or some other way to create a realm with no host.
1367  throw new TskCoreException("Failed to copy OsAccountRealm with ID=" + oldOsAccount.getRealmId() + " - can not currently handle domain-scoped hosts");
1368  } else {
1369  throw new TskCoreException("Failed to copy OsAccountRealm with ID=" + oldOsAccount.getRealmId() + " because it is non-domain scoped but has no scope host");
1370  }
1371  }
1372 
1373  // We currently only support one realm name.
1374  String realmName = null;
1375  List<String> names = oldRealm.getRealmNames();
1376  if (!names.isEmpty()) {
1377  realmName = names.get(0);
1378  }
1379 
1380  try {
1381  OsAccountRealm newRealm = newRealmManager.newWindowsRealm(oldOsAccount.getAddr().orElse(null), realmName, newHost, oldRealm.getScope());
1382  oldRealmIdToNewRealm.put(oldOsAccount.getRealmId(), newRealm);
1383  } catch (NotUserSIDException ex) {
1384  throw new TskCoreException("Failed to copy OsAccountRealm with ID=" + oldOsAccount.getRealmId(), ex);
1385  }
1386  }
1387 
1388  OsAccountManager newOsAcctManager = portableSkCase.getOsAccountManager();
1389  try {
1390  OsAccount newOsAccount = newOsAcctManager.newWindowsOsAccount(oldOsAccount.getAddr().orElse(null),
1391  oldOsAccount.getLoginName().orElse(null), oldRealmIdToNewRealm.get(oldOsAccount.getRealmId()));
1392  oldOsAccountIdToNewOsAccount.put(oldOsAccountId, newOsAccount);
1393  return newOsAccount;
1394  } catch (NotUserSIDException ex) {
1395  throw new TskCoreException("Failed to copy OsAccount with ID=" + oldOsAccount.getId(), ex);
1396  }
1397  }
1398 
1408  private void copyPathID(BlackboardArtifact newArtifact, BlackboardArtifact oldArtifact) throws TskCoreException {
1409  // Get the path ID attribute
1410  BlackboardAttribute oldPathIdAttr = oldArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
1411  if (oldPathIdAttr != null) {
1412  // Copy the file and remake the attribute if the path ID is valid
1413  long oldContentId = oldPathIdAttr.getValueLong();
1414  if (oldContentId > 0) {
1415  Content oldContent = currentCase.getSleuthkitCase().getContentById(oldContentId);
1416  long newContentId = copyContent(oldContent);
1417  newArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID,
1418  String.join(",", oldPathIdAttr.getSources()), newContentId));
1419  }
1420  }
1421  }
1422 
1434  private void copyAttachments(BlackboardArtifact newArtifact, BlackboardArtifact oldArtifact, AbstractFile newFile) throws TskCoreException {
1435  // Get the attachments from TSK_ATTACHMENTS attribute.
1436  BlackboardAttribute attachmentsAttr = oldArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS));
1437  if (attachmentsAttr != null) {
1438  try {
1439  MessageAttachments msgAttachments = BlackboardJsonAttrUtil.fromAttribute(attachmentsAttr, MessageAttachments.class);
1440 
1441  Collection<MessageAttachments.FileAttachment> oldFileAttachments = msgAttachments.getFileAttachments();
1442  List<MessageAttachments.FileAttachment> newFileAttachments = new ArrayList<>();
1443  for (MessageAttachments.FileAttachment oldFileAttachment : oldFileAttachments) {
1444  long attachedFileObjId = oldFileAttachment.getObjectId();
1445  if (attachedFileObjId >= 0) {
1446  // Copy the attached file and save to the MessageAttachments object
1447  AbstractFile attachedFile = currentCase.getSleuthkitCase().getAbstractFileById(attachedFileObjId);
1448  if (attachedFile == null) {
1449  throw new TskCoreException("Error loading file with object ID " + attachedFileObjId + " from portable case");
1450  }
1451  long newFileID = copyContent(attachedFile);
1452  newFileAttachments.add(new MessageAttachments.FileAttachment(portableSkCase.getAbstractFileById(newFileID)));
1453  }
1454  }
1455 
1456  // Get the name of the module(s) that created the attachment
1457  String newSourceStr = "";
1458  List<String> oldSources = attachmentsAttr.getSources();
1459  if (!oldSources.isEmpty()) {
1460  newSourceStr = String.join(",", oldSources);
1461  }
1462 
1463  // Add the attachment. The account type specified in the constructor will not be used.
1464  CommunicationArtifactsHelper communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(),
1465  newSourceStr, newFile, Account.Type.EMAIL, null);
1466  communicationArtifactsHelper.addAttachments(newArtifact, new MessageAttachments(newFileAttachments, msgAttachments.getUrlAttachments()));
1467  } catch (BlackboardJsonAttrUtil.InvalidJsonException ex) {
1468  throw new TskCoreException(String.format("Unable to parse json for MessageAttachments object in artifact: %s", oldArtifact.getName()), ex);
1469  }
1470  } else { // backward compatibility - email message attachments are derived files, children of the message.
1471  for (Content childContent : oldArtifact.getChildren()) {
1472  if (childContent instanceof AbstractFile) {
1473  copyContent(childContent);
1474  }
1475  }
1476  }
1477  }
1478 
1486  private String getExportSubfolder(AbstractFile abstractFile) {
1487  if (abstractFile.getMIMEType() == null || abstractFile.getMIMEType().isEmpty()) {
1488  return UNKNOWN_FILE_TYPE_FOLDER;
1489  }
1490 
1491  for (FileTypeCategory cat : FILE_TYPE_CATEGORIES) {
1492  if (cat.getMediaTypes().contains(abstractFile.getMIMEType())) {
1493  return cat.getDisplayName();
1494  }
1495  }
1496  return UNKNOWN_FILE_TYPE_FOLDER;
1497  }
1498 
1504  private Path getApplicationBasePath() {
1505  return getAutopsyExePath().getParent().getParent();
1506  }
1507 
1513  private Path getAutopsyExePath() {
1514  // If this is an installed version, there should be an <appName>64.exe file in the bin folder
1515  String exeName = getAutopsyExeName();
1516  String installPath = PlatformUtil.getInstallPath();
1517 
1518  return Paths.get(installPath, "bin", exeName);
1519  }
1520 
1526  private String getAutopsyExeName() {
1527  String appName = UserPreferences.getAppName();
1528  return appName + "64.exe";
1529  }
1530 
1539  private void copyApplication(Path sourceFolder, String destBaseFolder) throws IOException {
1540 
1541  // Create an appName folder in the destination
1542  Path destAppFolder = Paths.get(destBaseFolder, UserPreferences.getAppName());
1543  if (!destAppFolder.toFile().exists() && !destAppFolder.toFile().mkdirs()) {
1544  throw new IOException("Failed to create directory " + destAppFolder.toString());
1545  }
1546 
1547  // Now copy the files
1548  FileUtils.copyDirectory(sourceFolder.toFile(), destAppFolder.toFile());
1549  }
1550 
1558  private void createAppLaunchBatFile(String destBaseFolder) throws IOException {
1559  Path filePath = Paths.get(destBaseFolder, "open.bat");
1560  String appName = UserPreferences.getAppName();
1561  String exePath = "\"%~dp0" + appName + "\\bin\\" + getAutopsyExeName() + "\"";
1562  String casePath = "..\\" + caseName;
1563  try (FileWriter writer = new FileWriter(filePath.toFile())) {
1564  writer.write(exePath + " \"" + casePath + "\"");
1565  }
1566  }
1567 
1571  private void cleanup() {
1572  oldIdToNewContent.clear();
1573  newIdToContent.clear();
1574  oldTagNameToNewTagName.clear();
1575  oldArtTypeIdToNewArtTypeId.clear();
1577  oldArtifactIdToNewArtifact.clear();
1578  oldOsAccountIdToNewOsAccount.clear();
1579  oldRealmIdToNewRealm.clear();
1580  oldHostIdToNewHost.clear();
1581 
1583 
1584  currentCase = null;
1585  caseFolder = null;
1586  copiedFilesFolder = null;
1587  }
1588 
1592  private void closePortableCaseDatabase() {
1593  if (portableSkCase != null) {
1594  portableSkCase.close();
1595  portableSkCase = null;
1596  }
1597  }
1598 
1599  /*
1600  * @Override public JPanel getConfigurationPanel() { configPanel = new
1601  * CreatePortableCasePanel(); return configPanel; }
1602  */
1603  private class StoreMaxIdCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
1604 
1605  private final String tableName;
1606 
1607  StoreMaxIdCallback(String tableName) {
1608  this.tableName = tableName;
1609  }
1610 
1611  @Override
1612  public void process(ResultSet rs) {
1613 
1614  try {
1615  while (rs.next()) {
1616  try {
1617  Long maxId = rs.getLong("max_id"); // NON-NLS
1618  String query = " (table_name, max_id) VALUES ('" + tableName + "', '" + maxId + "')"; // NON-NLS
1619  portableSkCase.getCaseDbAccessManager().insert(MAX_ID_TABLE_NAME, query);
1620 
1621  } catch (SQLException ex) {
1622  logger.log(Level.WARNING, "Unable to get maximum ID from result set", ex); // NON-NLS
1623  } catch (TskCoreException ex) {
1624  logger.log(Level.WARNING, "Unable to save maximum ID from result set", ex); // NON-NLS
1625  }
1626 
1627  }
1628  } catch (SQLException ex) {
1629  logger.log(Level.WARNING, "Failed to get maximum ID from result set", ex); // NON-NLS
1630  }
1631  }
1632  }
1633 
1634  @NbBundle.Messages({
1635  "PortableCaseReportModule.compressCase.errorFinding7zip=Could not locate 7-Zip executable",
1636  "# {0} - Temp folder path",
1637  "PortableCaseReportModule.compressCase.errorCreatingTempFolder=Could not create temporary folder {0}",
1638  "PortableCaseReportModule.compressCase.errorCompressingCase=Error compressing case",
1639  "PortableCaseReportModule.compressCase.canceled=Compression canceled by user",})
1640  private boolean compressCase(ReportProgressPanel progressPanel, String folderToCompress) {
1641 
1643 
1644  // Make a temporary folder for the compressed case
1645  Path dirToCompress = Paths.get(folderToCompress);
1646  File tempZipFolder = Paths.get(dirToCompress.getParent().toString(), "temp", "portableCase" + System.currentTimeMillis()).toFile();
1647  if (!tempZipFolder.mkdirs()) {
1648  handleError("Error creating temporary folder " + tempZipFolder.toString(),
1649  Bundle.PortableCaseReportModule_compressCase_errorCreatingTempFolder(tempZipFolder.toString()), null, progressPanel); // NON-NLS
1650  return false;
1651  }
1652 
1653  // Find 7-Zip
1654  File sevenZipExe = locate7ZipExecutable();
1655  if (sevenZipExe == null) {
1656  handleError("Error finding 7-Zip exectuable", Bundle.PortableCaseReportModule_compressCase_errorFinding7zip(), null, progressPanel); // NON-NLS
1657  return false;
1658  }
1659 
1660  // Create the chunk option
1661  String chunkOption = "";
1663  chunkOption = "-v" + settings.getChunkSize().getSevenZipParam();
1664  }
1665 
1666  File zipFile = Paths.get(tempZipFolder.getAbsolutePath(), caseName + ".zip").toFile(); // NON-NLS
1667  ProcessBuilder procBuilder = new ProcessBuilder();
1668  procBuilder.command(
1669  sevenZipExe.getAbsolutePath(),
1670  "a", // Add to archive
1671  zipFile.getAbsolutePath(),
1672  dirToCompress.toAbsolutePath().toString(),
1673  chunkOption
1674  );
1675 
1676  try {
1677  Process process = procBuilder.start();
1678 
1679  while (process.isAlive()) {
1680  if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
1681  process.destroy();
1682  return false;
1683  }
1684  Thread.sleep(200);
1685  }
1686  int exitCode = process.exitValue();
1687  if (exitCode != 0) {
1688  // Save any errors so they can be logged
1689  StringBuilder sb = new StringBuilder();
1690  try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
1691  String line;
1692  while ((line = br.readLine()) != null) {
1693  sb.append(line).append(System.getProperty("line.separator")); // NON-NLS
1694  }
1695  }
1696 
1697  handleError("Error compressing case\n7-Zip output: " + sb.toString(), Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), null, progressPanel); // NON-NLS
1698  return false;
1699  }
1700  } catch (IOException | InterruptedException ex) {
1701  handleError("Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel); // NON-NLS
1702  return false;
1703  }
1704 
1705  // Delete everything in the case folder then copy over the compressed file(s)
1706  try {
1707  FileUtils.cleanDirectory(dirToCompress.toFile());
1708  FileUtils.copyDirectory(tempZipFolder, dirToCompress.toFile());
1709  FileUtils.deleteDirectory(new File(tempZipFolder.getParent()));
1710  } catch (IOException ex) {
1711  handleError("Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel); // NON-NLS
1712  return false;
1713  }
1714 
1715  return true;
1716  }
1717 
1723  private static File locate7ZipExecutable() {
1724  if (!PlatformUtil.isWindowsOS()) {
1725  return null;
1726  }
1727 
1728  String executableToFindName = Paths.get("7-Zip", "7z.exe").toString(); // NON-NLS
1729  File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, PortableCaseReportModule.class.getPackage().getName(), false);
1730  if (null == exeFile) {
1731  return null;
1732  }
1733 
1734  if (!exeFile.canExecute()) {
1735  return null;
1736  }
1737 
1738  return exeFile;
1739  }
1740 
1744  public static class GetInterestingItemSetNamesCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
1745 
1746  private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(GetInterestingItemSetNamesCallback.class.getName());
1747  private final Map<String, Long> setCounts = new HashMap<>();
1748 
1749  @Override
1750  public void process(ResultSet rs) {
1751  try {
1752  while (rs.next()) {
1753  try {
1754  Long setCount = rs.getLong("set_count"); // NON-NLS
1755  String setName = rs.getString("set_name"); // NON-NLS
1756 
1757  setCounts.put(setName, setCount);
1758 
1759  } catch (SQLException ex) {
1760  logger.log(Level.WARNING, "Unable to get data_source_obj_id or value from result set", ex); // NON-NLS
1761  }
1762  }
1763  } catch (SQLException ex) {
1764  logger.log(Level.WARNING, "Failed to get next result for values by datasource", ex); // NON-NLS
1765  }
1766  }
1767 
1773  public Map<String, Long> getSetCountMap() {
1774  return setCounts;
1775  }
1776  }
1777 }
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-2022 Basis Technology. Generated on: Tue Jun 27 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.