Autopsy 4.22.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-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 */
19package org.sleuthkit.autopsy.report.modules.portablecase;
20
21import com.google.common.collect.ArrayListMultimap;
22import com.google.common.collect.Multimap;
23import com.google.gson.Gson;
24import com.google.gson.GsonBuilder;
25import com.google.gson.JsonElement;
26import com.google.gson.stream.JsonWriter;
27import org.sleuthkit.autopsy.report.ReportModule;
28import java.util.logging.Level;
29import java.io.BufferedReader;
30import java.io.File;
31import java.io.FileOutputStream;
32import java.io.FileWriter;
33import java.io.InputStreamReader;
34import java.io.IOException;
35import java.io.OutputStream;
36import java.io.OutputStreamWriter;
37import java.nio.file.Files;
38import java.nio.file.Path;
39import java.nio.file.Paths;
40import java.sql.ResultSet;
41import java.sql.SQLException;
42import java.util.ArrayList;
43import java.util.Arrays;
44import java.util.Collection;
45import java.util.HashMap;
46import java.util.List;
47import java.util.Map;
48import org.apache.commons.io.FileUtils;
49import org.openide.modules.InstalledFileLocator;
50import org.openide.util.NbBundle;
51import org.sleuthkit.autopsy.casemodule.Case;
52import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
53import org.sleuthkit.autopsy.casemodule.services.TagsManager;
54import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
55import org.sleuthkit.autopsy.core.UserPreferences;
56import org.sleuthkit.autopsy.coreutils.FileUtil;
57import org.sleuthkit.autopsy.coreutils.Logger;
58import org.sleuthkit.autopsy.coreutils.PlatformUtil;
59import org.sleuthkit.autopsy.datamodel.ContentUtils;
60import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory;
61import org.sleuthkit.autopsy.report.ReportProgressPanel;
62import org.sleuthkit.caseuco.CaseUcoExporter;
63import org.sleuthkit.datamodel.AbstractFile;
64import org.sleuthkit.datamodel.Account;
65import org.sleuthkit.datamodel.AnalysisResult;
66import org.sleuthkit.datamodel.Blackboard.BlackboardException;
67import org.sleuthkit.datamodel.BlackboardArtifact;
68import org.sleuthkit.datamodel.BlackboardArtifactTag;
69import org.sleuthkit.datamodel.BlackboardAttribute;
70import org.sleuthkit.datamodel.CaseDbAccessManager;
71import org.sleuthkit.datamodel.Content;
72import org.sleuthkit.datamodel.ContentTag;
73import org.sleuthkit.datamodel.DataArtifact;
74import org.sleuthkit.datamodel.DataSource;
75import org.sleuthkit.datamodel.FileSystem;
76import org.sleuthkit.datamodel.Host;
77import org.sleuthkit.datamodel.Image;
78import org.sleuthkit.datamodel.LocalFilesDataSource;
79import org.sleuthkit.datamodel.OsAccount;
80import org.sleuthkit.datamodel.OsAccountManager;
81import org.sleuthkit.datamodel.OsAccountManager.NotUserSIDException;
82import org.sleuthkit.datamodel.OsAccountRealm;
83import org.sleuthkit.datamodel.OsAccountRealmManager;
84import org.sleuthkit.datamodel.Pool;
85import org.sleuthkit.datamodel.Score;
86import org.sleuthkit.datamodel.SleuthkitCase;
87import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
88import org.sleuthkit.datamodel.TagName;
89import org.sleuthkit.datamodel.TaggingManager.ContentTagChange;
90import org.sleuthkit.datamodel.TskCoreException;
91import org.sleuthkit.datamodel.TskData;
92import org.sleuthkit.datamodel.Volume;
93import org.sleuthkit.datamodel.VolumeSystem;
94import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
95import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
96import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
97
101public class PortableCaseReportModule implements ReportModule {
102
103 private static final Logger logger = Logger.getLogger(PortableCaseReportModule.class.getName());
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 {
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.getTagType());
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 {
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
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
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()) {
1489 }
1490
1492 if (cat.getMediaTypes().contains(abstractFile.getMIMEType())) {
1493 return cat.getDisplayName();
1494 }
1495 }
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();
1579 oldRealmIdToNewRealm.clear();
1580 oldHostIdToNewHost.clear();
1581
1583
1584 currentCase = null;
1585 caseFolder = null;
1586 copiedFilesFolder = null;
1587 }
1588
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}
List< ContentTag > getContentTagsByTagName(TagName tagName)
List< BlackboardArtifactTag > getBlackboardArtifactTagsByTagName(TagName tagName)
static String escapeFileName(String fileName)
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
BlackboardArtifact copyArtifact(long newContentId, BlackboardArtifact artifactToCopy)
Multimap< Long, BlackboardArtifact > getInterestingArtifactsBySetName(SleuthkitCase skCase, List< String > setNames)
void generateCaseUcoReport(List< TagName > tagNames, List< String > setNames, ReportProgressPanel progressPanel)
boolean compressCase(ReportProgressPanel progressPanel, String folderToCompress)
void copyAttachments(BlackboardArtifact newArtifact, BlackboardArtifact oldArtifact, AbstractFile newFile)
void handleError(String logWarning, String dialogWarning, Exception ex, ReportProgressPanel progressPanel)
void generateReport(String reportPath, PortableCaseReportModuleSettings options, ReportProgressPanel progressPanel)
BlackboardAttribute.Type getNewAttributeType(BlackboardAttribute oldAttribute)
void addFilesToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel)
void addArtifactsToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel)
void copyPathID(BlackboardArtifact newArtifact, BlackboardArtifact oldArtifact)
boolean addUniqueFile(Content content, DataSource dataSource, Path tmpDir, Gson gson, CaseUcoExporter exporter, JsonWriter reportWriter, boolean dataSourceHasBeenIncluded)
long copyContentToPortableCase(Content content, ReportProgressPanel progressPanel)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.