19 package org.sleuthkit.autopsy.report.modules.portablecase;
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;
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;
48 import org.apache.commons.io.FileUtils;
49 import org.openide.modules.InstalledFileLocator;
50 import org.openide.util.NbBundle;
66 import org.
sleuthkit.datamodel.Blackboard.BlackboardException;
81 import org.
sleuthkit.datamodel.OsAccountManager.NotUserSIDException;
87 import org.
sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
89 import org.
sleuthkit.datamodel.TaggingManager.ContentTagChange;
94 import org.
sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
95 import org.
sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
96 import org.
sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
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());
157 "PortableCaseReportModule.getName.name=Portable Case"
161 return Bundle.PortableCaseReportModule_getName_name();
165 "PortableCaseReportModule.getDescription.description=Copies selected items to a new single-user case that can be easily shared"
169 return Bundle.PortableCaseReportModule_getDescription_description();
189 logger.log(Level.INFO,
"Portable case creation canceled by user");
207 logger.log(Level.WARNING, logWarning);
209 logger.log(Level.SEVERE, logWarning, ex);
217 "PortableCaseReportModule.generateReport.verifying=Verifying selected parameters...",
218 "PortableCaseReportModule.generateReport.creatingCase=Creating portable case database...",
219 "PortableCaseReportModule.generateReport.copyingTags=Copying tags...",
221 "PortableCaseReportModule.generateReport.copyingFiles=Copying files tagged as {0}...",
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..."
247 this.settings = options;
249 progressPanel.
start();
250 progressPanel.
updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_verifying());
256 File outputDir =
new File(reportPath);
257 if (!outputDir.exists()) {
258 handleError(
"Output folder " + outputDir.toString() +
" does not exist",
259 Bundle.PortableCaseReportModule_generateReport_outputDirDoesNotExist(outputDir.toString()), null, progressPanel);
263 if (!outputDir.isDirectory()) {
264 handleError(
"Output folder " + outputDir.toString() +
" is not a folder",
265 Bundle.PortableCaseReportModule_generateReport_outputDirIsNotDir(outputDir.toString()), null, progressPanel);
275 Bundle.PortableCaseReportModule_generateReport_caseClosed(), null, progressPanel);
281 outputDir = Paths.get(outputDir.toString(),
caseName).toFile();
284 List<TagName> tagNames;
290 Bundle.PortableCaseReportModule_generateReport_errorReadingTags(), ex, progressPanel);
297 List<String> setNames;
302 handleError(
"Unable to get all interesting items sets",
303 Bundle.PortableCaseReportModule_generateReport_errorReadingSets(), ex, progressPanel);
310 if (tagNames.isEmpty() && setNames.isEmpty()) {
312 Bundle.PortableCaseReportModule_generateReport_noContentToCopy(), null, progressPanel);
318 progressPanel.
updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_creatingCase());
320 if (portableSkCase == null) {
334 }
catch (TskCoreException ex) {
335 handleError(
"Error creating image tag table", Bundle.PortableCaseReportModule_generateReport_errorCreatingImageTagTable(), ex, progressPanel);
340 progressPanel.
updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingTags());
342 for (TagName tagName : tagNames) {
343 TagName newTagName = portableSkCase.getTaggingManager().addOrUpdateTagName(tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus());
344 oldTagNameToNewTagName.put(tagName, newTagName);
346 }
catch (TskCoreException ex) {
347 handleError(
"Error copying tags", Bundle.PortableCaseReportModule_generateReport_errorCopyingTags(), ex, progressPanel);
352 for (BlackboardArtifact.ARTIFACT_TYPE type : BlackboardArtifact.ARTIFACT_TYPE.values()) {
353 oldArtTypeIdToNewArtTypeId.put(type.getTypeID(), type.getTypeID());
355 for (BlackboardAttribute.ATTRIBUTE_TYPE type : BlackboardAttribute.ATTRIBUTE_TYPE.values()) {
358 }
catch (TskCoreException ex) {
359 handleError(
"Error looking up attribute name " + type.getLabel(),
360 Bundle.PortableCaseReportModule_generateReport_errorLookingUpAttrType(type.getLabel()),
367 for (TagName tagName : tagNames) {
373 progressPanel.
updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingFiles(tagName.getDisplayName()));
382 }
catch (TskCoreException ex) {
383 handleError(
"Error copying tagged files", Bundle.PortableCaseReportModule_generateReport_errorCopyingFiles(), ex, progressPanel);
389 for (TagName tagName : tagNames) {
395 progressPanel.
updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingArtifacts(tagName.getDisplayName()));
404 }
catch (TskCoreException ex) {
405 handleError(
"Error copying tagged artifacts", Bundle.PortableCaseReportModule_generateReport_errorCopyingArtifacts(), ex, progressPanel);
410 if (!setNames.isEmpty()) {
412 List<AnalysisResult> interestingFiles = currentCase.
getSleuthkitCase().getBlackboard().getAnalysisResultsByType(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID());
413 for (AnalysisResult art : interestingFiles) {
420 BlackboardAttribute setAttr = art.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
421 if (setNames.contains(setAttr.getValueString())) {
425 }
catch (TskCoreException ex) {
426 handleError(
"Error copying interesting files", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingFiles(), ex, progressPanel);
431 List<AnalysisResult> interestingResults = currentCase.
getSleuthkitCase().getBlackboard().getAnalysisResultsByType(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID());
432 for (AnalysisResult art : interestingResults) {
438 BlackboardAttribute setAttr = art.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
439 if (setNames.contains(setAttr.getValueString())) {
443 }
catch (TskCoreException ex) {
444 handleError(
"Error copying interesting results", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingResults(), ex, progressPanel);
460 progressPanel.
updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingAutopsy());
463 }
catch (IOException ex) {
464 handleError(
"Error copying autopsy", Bundle.PortableCaseReportModule_generateReport_errorCopyingAutopsy(), ex, progressPanel);
470 progressPanel.
updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_compressingCase());
503 "PortableCaseReportModule.generateCaseUcoReport.errorCreatingReportFolder=Could not make report folder",
504 "PortableCaseReportModule.generateCaseUcoReport.errorGeneratingCaseUcoReport=Problem while generating CASE-UCO report",
505 "PortableCaseReportModule.generateCaseUcoReport.startCaseUcoReportGeneration=Creating a CASE-UCO report of the portable case",
506 "PortableCaseReportModule.generateCaseUcoReport.successCaseUcoReportGeneration=Successfully created a CASE-UCO report of the portable case"
510 Path reportsDirectory = Paths.get(caseFolder.toString(),
"Reports");
511 if (!reportsDirectory.toFile().mkdir()) {
512 logger.log(Level.SEVERE,
"Could not make the report folder... skipping "
513 +
"CASE-UCO report generation for the portable case");
517 Path reportFile = reportsDirectory.resolve(CASE_UCO_FILE_NAME);
519 progressPanel.
updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_startCaseUcoReportGeneration());
520 try (OutputStream stream =
new FileOutputStream(reportFile.toFile());
521 JsonWriter reportWriter =
new JsonWriter(
new OutputStreamWriter(stream,
"UTF-8"))) {
522 Gson gson =
new GsonBuilder().setPrettyPrinting().create();
523 reportWriter.setIndent(
" ");
524 reportWriter.beginObject();
525 reportWriter.name(
"@graph");
526 reportWriter.beginArray();
534 Path tmpDir = Paths.get(caseTempDirectory, CASE_UCO_TMP_DIR);
535 FileUtils.deleteDirectory(tmpDir.toFile());
536 Files.createDirectory(tmpDir);
538 CaseUcoExporter exporter =
new CaseUcoExporter(currentCase.
getSleuthkitCase());
539 for (JsonElement element : exporter.exportSleuthkitCase()) {
540 gson.toJson(element, reportWriter);
549 for (DataSource dataSource : currentCase.
getSleuthkitCase().getDataSources()) {
552 boolean dataSourceHasBeenIncluded =
false;
555 for (TagName tagName : tagNames) {
558 dataSource, tmpDir, gson, exporter, reportWriter, dataSourceHasBeenIncluded);
562 dataSource, tmpDir, gson, exporter, reportWriter, dataSourceHasBeenIncluded);
566 for (BlackboardArtifact bArt : artifactsWithSetName.get(dataSource.getId())) {
567 Content sourceContent = bArt.getParent();
568 dataSourceHasBeenIncluded |=
addUniqueFile(sourceContent, dataSource,
569 tmpDir, gson, exporter, reportWriter, dataSourceHasBeenIncluded);
574 reportWriter.endArray();
575 reportWriter.endObject();
576 progressPanel.
updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_successCaseUcoReportGeneration());
577 }
catch (IOException | TskCoreException ex) {
578 progressPanel.
updateStatusLabel(Bundle.PortableCaseReportModule_generateCaseUcoReport_errorGeneratingCaseUcoReport());
579 logger.log(Level.SEVERE,
"Error encountered while trying to create "
580 +
"CASE-UCO output for portable case.. the portable case will be "
581 +
"completed without a CASE-UCO report.", ex);
591 Multimap<Long, BlackboardArtifact> artifactsWithSetName = ArrayListMultimap.create();
592 if (!setNames.isEmpty()) {
593 List<BlackboardArtifact> allArtifacts = skCase.getBlackboardArtifacts(
594 BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
595 allArtifacts.addAll(skCase.getBlackboardArtifacts(
596 BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT));
598 for (BlackboardArtifact bArt : allArtifacts) {
599 BlackboardAttribute setAttr = bArt.getAttribute(
600 new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
601 if (setNames.contains(setAttr.getValueString())) {
602 artifactsWithSetName.put(bArt.getDataSource().getId(), bArt);
606 return artifactsWithSetName;
630 Path tmpDir, Gson gson, CaseUcoExporter exporter, JsonWriter reportWriter,
631 boolean dataSourceHasBeenIncluded)
throws IOException, TskCoreException {
632 if (content instanceof AbstractFile && !(content instanceof DataSource)) {
633 AbstractFile absFile = (AbstractFile) content;
634 Path filePath = tmpDir.resolve(Long.toString(absFile.getId()));
635 if (!absFile.isDir() && !Files.exists(filePath)) {
636 if (!dataSourceHasBeenIncluded) {
637 for (JsonElement element : exporter.exportDataSource(dataSource)) {
638 gson.toJson(element, reportWriter);
643 for (JsonElement element : exporter.exportAbstractFile(absFile, Paths.get(FILE_FOLDER_NAME, subFolder, fileName).toString())) {
644 gson.toJson(element, reportWriter);
646 Files.createFile(filePath);
656 List<String> setNames =
new ArrayList<>();
657 Map<String, Long> setCounts;
661 String innerSelect =
"SELECT (value_text) AS set_name FROM blackboard_attributes WHERE (artifact_type_id = '"
662 + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() +
"' OR artifact_type_id = '"
663 + BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() +
"') AND attribute_type_id = '"
664 + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() +
"'";
667 String query =
"set_name, count(1) AS set_count FROM (" + innerSelect +
") set_names GROUP BY set_name";
672 setNames.addAll(setCounts.keySet());
684 "# {0} - case folder",
685 "PortableCaseReportModule.createCase.caseDirExists=Case folder {0} already exists",
686 "PortableCaseReportModule.createCase.errorCreatingCase=Error creating case",
688 "PortableCaseReportModule.createCase.errorCreatingFolder=Error creating folder {0}",
689 "PortableCaseReportModule.createCase.errorStoringMaxIds=Error storing maximum database IDs",})
693 caseFolder = Paths.get(outputDir.toString(),
caseName).toFile();
695 if (caseFolder.exists()) {
696 handleError(
"Case folder " + caseFolder.toString() +
" already exists",
697 Bundle.PortableCaseReportModule_createCase_caseDirExists(caseFolder.toString()), null, progressPanel);
704 }
catch (TskCoreException ex) {
705 handleError(
"Error creating case " + caseName +
" in folder " + caseFolder.toString(),
706 Bundle.PortableCaseReportModule_createCase_errorCreatingCase(), ex, progressPanel);
713 }
catch (TskCoreException ex) {
715 Bundle.PortableCaseReportModule_createCase_errorStoringMaxIds(), ex, progressPanel);
720 copiedFilesFolder = Paths.get(caseFolder.toString(),
FILE_FOLDER_NAME).toFile();
721 if (!copiedFilesFolder.mkdir()) {
722 handleError(
"Error creating folder " + copiedFilesFolder.toString(),
723 Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(copiedFilesFolder.toString()), null, progressPanel);
729 File subFolder = Paths.get(copiedFilesFolder.toString(), cat.getDisplayName()).toFile();
730 if (!subFolder.mkdir()) {
731 handleError(
"Error creating folder " + subFolder.toString(),
732 Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(subFolder.toString()), null, progressPanel);
737 if (!unknownTypeFolder.mkdir()) {
738 handleError(
"Error creating folder " + unknownTypeFolder.toString(),
739 Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(unknownTypeFolder.toString()), null, progressPanel);
752 CaseDbAccessManager currentCaseDbManager = currentCase.
getSleuthkitCase().getCaseDbAccessManager();
754 String tableSchema =
"( table_name TEXT PRIMARY KEY, "
757 portableSkCase.getCaseDbAccessManager().createTable(MAX_ID_TABLE_NAME, tableSchema);
759 currentCaseDbManager.select(
"max(obj_id) as max_id from tsk_objects",
new StoreMaxIdCallback(
"tsk_objects"));
760 currentCaseDbManager.select(
"max(tag_id) as max_id from content_tags",
new StoreMaxIdCallback(
"content_tags"));
761 currentCaseDbManager.select(
"max(tag_id) as max_id from blackboard_artifact_tags",
new StoreMaxIdCallback(
"blackboard_artifact_tags"));
762 currentCaseDbManager.select(
"max(examiner_id) as max_id from tsk_examiners",
new StoreMaxIdCallback(
"tsk_examiners"));
775 CaseDbAccessManager portableDbAccessManager = portableSkCase.getCaseDbAccessManager();
795 for (ContentTag tag : tags) {
802 Content content = tag.getContent();
803 if (content instanceof AbstractFile) {
808 if (!oldTagNameToNewTagName.containsKey(tag.getName())) {
809 throw new TskCoreException(
"TagName map is missing entry for ID " + tag.getName().getId() +
" with display name " + tag.getName().getDisplayName());
811 ContentTagChange newContentTag = portableSkCase.getTaggingManager().addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
816 if (!appData.isEmpty()) {
837 currentCase.
getSleuthkitCase().getCaseDbAccessManager().select(query, callback);
838 return callback.getAppData();
854 appData = rs.getString(
"app_data");
855 }
catch (SQLException ex) {
856 logger.log(Level.WARNING,
"Unable to get app_data from result set", ex);
859 }
catch (SQLException ex) {
860 logger.log(Level.WARNING,
"Failed to get next result for app_data", ex);
869 String getAppData() {
883 String insert =
"(content_tag_id, app_data) VALUES (" + newContentTag.getId() +
", '" + appData +
"')";
900 for (BlackboardArtifactTag tag : tags) {
908 Content content = tag.getContent();
912 BlackboardArtifact newArtifact =
copyArtifact(newContentId, tag.getArtifact());
915 copyAttachments(newArtifact, tag.getArtifact(), portableSkCase.getAbstractFileById(newContentId));
921 if (!oldTagNameToNewTagName.containsKey(tag.getName())) {
922 throw new TskCoreException(
"TagName map is missing entry for ID " + tag.getName().getId() +
" with display name " + tag.getName().getDisplayName());
924 portableSkCase.getTaggingManager().addArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment());
940 private BlackboardArtifact
copyArtifact(
long newContentId, BlackboardArtifact artifactToCopy)
throws TskCoreException {
942 if (oldArtifactIdToNewArtifact.containsKey(artifactToCopy.getArtifactID())) {
943 return oldArtifactIdToNewArtifact.get(artifactToCopy.getArtifactID());
947 BlackboardAttribute oldAssociatedAttribute = artifactToCopy.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
948 List<BlackboardAttribute> newAttrs =
new ArrayList<>();
949 if (oldAssociatedAttribute != null) {
950 BlackboardArtifact oldAssociatedArtifact = currentCase.
getSleuthkitCase().getBlackboardArtifact(oldAssociatedAttribute.getValueLong());
951 BlackboardArtifact newAssociatedArtifact =
copyArtifact(newContentId, oldAssociatedArtifact);
952 newAttrs.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
953 String.join(
",", oldAssociatedAttribute.getSources()), newAssociatedArtifact.getArtifactID()));
956 List<BlackboardAttribute> oldAttrs = artifactToCopy.getAttributes();
959 for (BlackboardAttribute oldAttr : oldAttrs) {
962 if (SPECIALLY_HANDLED_ATTRS.contains(oldAttr.getAttributeType().getTypeID())) {
967 switch (oldAttr.getValueType()) {
969 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
970 oldAttr.getValueBytes()));
973 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
974 oldAttr.getValueDouble()));
977 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
978 oldAttr.getValueInt()));
982 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
983 oldAttr.getValueLong()));
987 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
988 oldAttr.getValueString()));
991 throw new TskCoreException(
"Unexpected attribute value type found: " + oldAttr.getValueType().getLabel());
997 Long newDataSourceId;
998 if (newIdToContent.get(newContentId).getDataSource() != null) {
1001 newDataSourceId = newIdToContent.get(newContentId).getDataSource().getId();
1004 if (artifactToCopy.getDataSource() == null) {
1006 throw new TskCoreException(
"Can not copy artifact with ID: " + artifactToCopy.getArtifactID() +
" because it is not associated with a data source");
1008 newDataSourceId =
copyContent(artifactToCopy.getDataSource());
1014 BlackboardArtifact.Type newArtifactType = portableSkCase.getBlackboard().getArtifactType(newArtifactTypeId);
1015 BlackboardArtifact newArtifact;
1019 if (!((artifactToCopy instanceof AnalysisResult) || (artifactToCopy instanceof DataArtifact))) {
1021 if (newArtifactType.getCategory().equals(BlackboardArtifact.Category.ANALYSIS_RESULT)) {
1022 AnalysisResult ar = currentCase.
getSleuthkitCase().getBlackboard().getAnalysisResultById(artifactToCopy.getId());
1024 artifactToCopy = ar;
1027 DataArtifact da = currentCase.
getSleuthkitCase().getBlackboard().getDataArtifactById(artifactToCopy.getId());
1029 artifactToCopy = da;
1032 }
catch (TskCoreException ex) {
1038 if (artifactToCopy instanceof AnalysisResult) {
1039 AnalysisResult analysisResultToCopy = (AnalysisResult) artifactToCopy;
1040 newArtifact = portableSkCase.getBlackboard().newAnalysisResult(newArtifactType, newContentId,
1041 newDataSourceId, analysisResultToCopy.getScore(),
1042 analysisResultToCopy.getConclusion(), analysisResultToCopy.getConfiguration(),
1043 analysisResultToCopy.getJustification(), newAttrs).getAnalysisResult();
1044 }
else if (artifactToCopy instanceof DataArtifact) {
1045 DataArtifact dataArtifactToCopy = (DataArtifact) artifactToCopy;
1046 Long newOsAccountId = null;
1047 if (dataArtifactToCopy.getOsAccountObjectId().isPresent()) {
1048 copyOsAccount(dataArtifactToCopy.getOsAccountObjectId().get());
1049 newOsAccountId = oldOsAccountIdToNewOsAccount.get((dataArtifactToCopy.getOsAccountObjectId().get())).getId();
1051 newArtifact = portableSkCase.getBlackboard().newDataArtifact(newArtifactType, newContentId,
1053 newAttrs, newOsAccountId);
1055 if (newArtifactType.getCategory().equals(BlackboardArtifact.Category.ANALYSIS_RESULT)) {
1056 newArtifact = portableSkCase.getBlackboard().newAnalysisResult(newArtifactType, newContentId,
1057 newDataSourceId, Score.SCORE_NONE,
1058 null, null, null, newAttrs).getAnalysisResult();
1060 newArtifact = portableSkCase.getBlackboard().newDataArtifact(newArtifactType, newContentId,
1065 }
catch (BlackboardException ex) {
1066 throw new TskCoreException(
"Error copying artifact with ID: " + artifactToCopy.getId());
1069 oldArtifactIdToNewArtifact.put(artifactToCopy.getArtifactID(), newArtifact);
1083 if (oldArtTypeIdToNewArtTypeId.containsKey(oldArtifact.getArtifactTypeID())) {
1084 return oldArtTypeIdToNewArtTypeId.get(oldArtifact.getArtifactTypeID());
1087 BlackboardArtifact.Type oldCustomType = currentCase.
getSleuthkitCase().getArtifactType(oldArtifact.getArtifactTypeName());
1089 BlackboardArtifact.Type newCustomType = portableSkCase.getBlackboard().getOrAddArtifactType(oldCustomType.getTypeName(), oldCustomType.getDisplayName());
1090 oldArtTypeIdToNewArtTypeId.put(oldArtifact.getArtifactTypeID(), newCustomType.getTypeID());
1091 return newCustomType.getTypeID();
1092 }
catch (BlackboardException ex) {
1093 throw new TskCoreException(
"Error creating new artifact type " + oldCustomType.getTypeName(), ex);
1106 private BlackboardAttribute.Type
getNewAttributeType(BlackboardAttribute oldAttribute)
throws TskCoreException {
1107 BlackboardAttribute.Type oldAttrType = oldAttribute.getAttributeType();
1113 BlackboardAttribute.Type newCustomType = portableSkCase.getBlackboard().getOrAddAttributeType(oldAttrType.getTypeName(),
1114 oldAttrType.getValueType(), oldAttrType.getDisplayName());
1116 return newCustomType;
1117 }
catch (BlackboardException ex) {
1118 throw new TskCoreException(
"Error creating new attribute type " + oldAttrType.getTypeName(), ex);
1132 @NbBundle.Messages({
1133 "# {0} - File name",
1134 "PortableCaseReportModule.copyContentToPortableCase.copyingFile=Copying file {0}",})
1136 progressPanel.
updateStatusLabel(Bundle.PortableCaseReportModule_copyContentToPortableCase_copyingFile(content.getUniquePath()));
1152 if (oldIdToNewContent.containsKey(content.getId())) {
1153 return oldIdToNewContent.get(content.getId()).getId();
1160 if (content.getParent() != null) {
1165 if (content instanceof BlackboardArtifact) {
1166 BlackboardArtifact artifactToCopy = (BlackboardArtifact) content;
1168 }
else if (content instanceof OsAccount) {
1172 Host newHost = null;
1173 if (content instanceof DataSource) {
1174 newHost =
copyHost(((DataSource)content).getHost());
1178 if (content instanceof AbstractFile) {
1179 AbstractFile file = (AbstractFile) content;
1180 if (file.getOsAccountObjectId().isPresent()) {
1189 if (content instanceof Image) {
1190 md5 = ((Image) content).getMd5();
1191 sha1 = ((Image) content).getSha1();
1192 sha256 = ((Image) content).getSha256();
1195 CaseDbTransaction trans = portableSkCase.beginTransaction();
1197 if (content instanceof Image) {
1198 Image image = (Image) content;
1199 newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(),
1200 new ArrayList<>(), image.getTimeZone(), md5, sha1, sha256, image.getDeviceId(), newHost, trans);
1201 }
else if (content instanceof VolumeSystem) {
1202 VolumeSystem vs = (VolumeSystem) content;
1203 newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
1204 }
else if (content instanceof Volume) {
1205 Volume vs = (Volume) content;
1206 newContent = portableSkCase.addVolume(parentId, vs.getAddr(), vs.getStart(), vs.getLength(),
1207 vs.getDescription(), vs.getFlags(), trans);
1208 }
else if (content instanceof Pool) {
1209 Pool pool = (Pool) content;
1210 newContent = portableSkCase.addPool(parentId, pool.getType(), trans);
1211 }
else if (content instanceof FileSystem) {
1212 FileSystem fs = (FileSystem) content;
1213 newContent = portableSkCase.addFileSystem(parentId, fs.getImageOffset(), fs.getFsType(), fs.getBlock_size(),
1214 fs.getBlock_count(), fs.getRoot_inum(), fs.getFirst_inum(), fs.getLastInum(),
1215 fs.getName(), trans);
1216 }
else if (content instanceof BlackboardArtifact) {
1217 BlackboardArtifact artifactToCopy = (BlackboardArtifact) content;
1219 }
else if (content instanceof AbstractFile) {
1220 AbstractFile abstractFile = (AbstractFile) content;
1222 if (abstractFile instanceof LocalFilesDataSource) {
1223 LocalFilesDataSource localFilesDS = (LocalFilesDataSource) abstractFile;
1224 newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), newHost, trans);
1226 if (abstractFile.isDir()) {
1227 newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
1233 File exportFolder = Paths.get(copiedFilesFolder.toString(), exportSubFolder).toFile();
1234 File localFile =
new File(exportFolder, fileName);
1238 Content oldParent = abstractFile.getParent();
1239 if (!oldIdToNewContent.containsKey(oldParent.getId())) {
1240 throw new TskCoreException(
"Parent of file with ID " + abstractFile.getId() +
" has not been created");
1242 Content newParent = oldIdToNewContent.get(oldParent.getId());
1245 String relativePath = FILE_FOLDER_NAME + File.separator + exportSubFolder + File.separator + fileName;
1247 Long newOsAccountId = null;
1248 if (abstractFile.getOsAccountObjectId().isPresent()) {
1249 newOsAccountId = oldOsAccountIdToNewOsAccount.get(abstractFile.getOsAccountObjectId().get()).getId();
1252 newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
1253 abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getMtime(),
1254 abstractFile.getMd5Hash(), abstractFile.getSha256Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
1255 true, TskData.EncodingType.NONE,
1256 newOsAccountId, abstractFile.getOwnerUid().orElse(null),
1258 }
catch (IOException ex) {
1259 throw new TskCoreException(
"Error copying file " + abstractFile.getName() +
" with original obj ID "
1260 + abstractFile.getId(), ex);
1265 throw new TskCoreException(
"Trying to copy unexpected Content type " + content.getClass().getName());
1268 }
catch (TskCoreException ex) {
1275 oldIdToNewContent.put(content.getId(), newContent);
1276 newIdToContent.put(newContent.getId(), newContent);
1277 return oldIdToNewContent.get(content.getId()).getId();
1288 private Host
copyHost(Host oldHost)
throws TskCoreException {
1290 if (oldHostIdToNewHost.containsKey(oldHost.getHostId())) {
1291 newHost = oldHostIdToNewHost.get(oldHost.getHostId());
1293 newHost = portableSkCase.getHostManager().newHost(oldHost.getName());
1294 oldHostIdToNewHost.put(oldHost.getHostId(), newHost);
1307 if (oldOsAccountIdToNewOsAccount.containsKey(oldOsAccountId)) {
1308 return oldOsAccountIdToNewOsAccount.get(oldOsAccountId);
1312 OsAccountManager oldOsAcctManager = currentCase.
getSleuthkitCase().getOsAccountManager();
1313 OsAccount oldOsAccount = oldOsAcctManager.getOsAccountByObjectId(oldOsAccountId);
1316 OsAccountRealmManager oldRealmManager = currentCase.
getSleuthkitCase().getOsAccountRealmManager();
1317 OsAccountRealm oldRealm = oldRealmManager.getRealmByRealmId(oldOsAccount.getRealmId());
1320 if (!oldRealmIdToNewRealm.containsKey(oldOsAccount.getRealmId())) {
1321 OsAccountRealmManager newRealmManager = portableSkCase.getOsAccountRealmManager();
1323 Host newHost = null;
1324 if (oldRealm.getScopeHost().isPresent()) {
1325 Host host = oldRealm.getScopeHost().get();
1328 if (oldRealm.getScope().equals(OsAccountRealm.RealmScope.DOMAIN)) {
1332 throw new TskCoreException(
"Failed to copy OsAccountRealm with ID=" + oldOsAccount.getRealmId() +
" - can not currently handle domain-scoped hosts");
1334 throw new TskCoreException(
"Failed to copy OsAccountRealm with ID=" + oldOsAccount.getRealmId() +
" because it is non-domain scoped but has no scope host");
1339 String realmName = null;
1340 List<String> names = oldRealm.getRealmNames();
1341 if (!names.isEmpty()) {
1342 realmName = names.get(0);
1346 OsAccountRealm newRealm = newRealmManager.newWindowsRealm(oldRealm.getRealmAddr().orElse(null), realmName, newHost, oldRealm.getScope());
1347 oldRealmIdToNewRealm.put(oldOsAccount.getRealmId(), newRealm);
1348 }
catch (NotUserSIDException ex) {
1349 throw new TskCoreException(
"Failed to copy OsAccountRealm with ID=" + oldOsAccount.getRealmId(), ex);
1353 OsAccountManager newOsAcctManager = portableSkCase.getOsAccountManager();
1355 OsAccount newOsAccount = newOsAcctManager.newWindowsOsAccount(oldOsAccount.getAddr().orElse(null),
1356 oldOsAccount.getLoginName().orElse(null), oldRealmIdToNewRealm.get(oldOsAccount.getRealmId()));
1357 oldOsAccountIdToNewOsAccount.put(oldOsAccountId, newOsAccount);
1358 return newOsAccount;
1359 }
catch (NotUserSIDException ex) {
1360 throw new TskCoreException(
"Failed to copy OsAccount with ID=" + oldOsAccount.getId(), ex);
1372 private void copyPathID(BlackboardArtifact newArtifact, BlackboardArtifact oldArtifact)
throws TskCoreException {
1374 BlackboardAttribute oldPathIdAttr = oldArtifact.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
1375 if (oldPathIdAttr != null) {
1377 long oldContentId = oldPathIdAttr.getValueLong();
1378 if (oldContentId > 0) {
1379 Content oldContent = currentCase.
getSleuthkitCase().getContentById(oldContentId);
1381 newArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID,
1382 String.join(
",", oldPathIdAttr.getSources()), newContentId));
1396 private void copyAttachments(BlackboardArtifact newArtifact, BlackboardArtifact oldArtifact, AbstractFile newFile)
throws TskCoreException {
1398 BlackboardAttribute attachmentsAttr = oldArtifact.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS));
1399 if (attachmentsAttr != null) {
1401 MessageAttachments msgAttachments = BlackboardJsonAttrUtil.fromAttribute(attachmentsAttr, MessageAttachments.class);
1403 Collection<MessageAttachments.FileAttachment> oldFileAttachments = msgAttachments.getFileAttachments();
1404 List<MessageAttachments.FileAttachment> newFileAttachments =
new ArrayList<>();
1405 for (MessageAttachments.FileAttachment oldFileAttachment : oldFileAttachments) {
1406 long attachedFileObjId = oldFileAttachment.getObjectId();
1407 if (attachedFileObjId >= 0) {
1409 AbstractFile attachedFile = currentCase.
getSleuthkitCase().getAbstractFileById(attachedFileObjId);
1410 if (attachedFile == null) {
1411 throw new TskCoreException(
"Error loading file with object ID " + attachedFileObjId +
" from portable case");
1414 newFileAttachments.add(
new MessageAttachments.FileAttachment(portableSkCase.getAbstractFileById(newFileID)));
1419 String newSourceStr =
"";
1420 List<String> oldSources = attachmentsAttr.getSources();
1421 if (! oldSources.isEmpty()) {
1422 newSourceStr = String.join(
",", oldSources);
1426 CommunicationArtifactsHelper communicationArtifactsHelper =
new CommunicationArtifactsHelper(currentCase.
getSleuthkitCase(),
1427 newSourceStr, newFile, Account.Type.EMAIL);
1428 communicationArtifactsHelper.addAttachments(newArtifact,
new MessageAttachments(newFileAttachments, msgAttachments.getUrlAttachments()));
1430 catch (BlackboardJsonAttrUtil.InvalidJsonException ex) {
1431 throw new TskCoreException(String.format(
"Unable to parse json for MessageAttachments object in artifact: %s", oldArtifact.getName()), ex);
1434 for (Content childContent : oldArtifact.getChildren()) {
1435 if (childContent instanceof AbstractFile) {
1450 if (abstractFile.getMIMEType() == null || abstractFile.getMIMEType().isEmpty()) {
1455 if (cat.getMediaTypes().contains(abstractFile.getMIMEType())) {
1456 return cat.getDisplayName();
1481 return Paths.get(installPath,
"bin", exeName);
1491 return appName +
"64.exe";
1502 private void copyApplication(Path sourceFolder, String destBaseFolder)
throws IOException {
1506 if (!destAppFolder.toFile().exists() && !destAppFolder.toFile().mkdirs()) {
1507 throw new IOException(
"Failed to create directory " + destAppFolder.toString());
1511 FileUtils.copyDirectory(sourceFolder.toFile(), destAppFolder.toFile());
1522 Path filePath = Paths.get(destBaseFolder,
"open.bat");
1525 String casePath =
"..\\" +
caseName;
1526 try (FileWriter writer =
new FileWriter(filePath.toFile())) {
1527 writer.write(exePath +
" \"" + casePath +
"\"");
1535 oldIdToNewContent.clear();
1536 newIdToContent.clear();
1537 oldTagNameToNewTagName.clear();
1538 oldArtTypeIdToNewArtTypeId.clear();
1540 oldArtifactIdToNewArtifact.clear();
1541 oldOsAccountIdToNewOsAccount.clear();
1542 oldRealmIdToNewRealm.clear();
1543 oldHostIdToNewHost.clear();
1549 copiedFilesFolder = null;
1556 if (portableSkCase != null) {
1557 portableSkCase.close();
1558 portableSkCase = null;
1580 Long maxId = rs.getLong(
"max_id");
1581 String query =
" (table_name, max_id) VALUES ('" + tableName +
"', '" + maxId +
"')";
1582 portableSkCase.getCaseDbAccessManager().insert(MAX_ID_TABLE_NAME, query);
1584 }
catch (SQLException ex) {
1585 logger.log(Level.WARNING,
"Unable to get maximum ID from result set", ex);
1586 }
catch (TskCoreException ex) {
1587 logger.log(Level.WARNING,
"Unable to save maximum ID from result set", ex);
1591 }
catch (SQLException ex) {
1592 logger.log(Level.WARNING,
"Failed to get maximum ID from result set", ex);
1597 @NbBundle.Messages({
1598 "PortableCaseReportModule.compressCase.errorFinding7zip=Could not locate 7-Zip executable",
1599 "# {0} - Temp folder path",
1600 "PortableCaseReportModule.compressCase.errorCreatingTempFolder=Could not create temporary folder {0}",
1601 "PortableCaseReportModule.compressCase.errorCompressingCase=Error compressing case",
1602 "PortableCaseReportModule.compressCase.canceled=Compression canceled by user",})
1608 Path dirToCompress = Paths.get(folderToCompress);
1609 File tempZipFolder = Paths.get(dirToCompress.getParent().toString(),
"temp",
"portableCase" + System.currentTimeMillis()).toFile();
1610 if (!tempZipFolder.mkdirs()) {
1611 handleError(
"Error creating temporary folder " + tempZipFolder.toString(),
1612 Bundle.PortableCaseReportModule_compressCase_errorCreatingTempFolder(tempZipFolder.toString()), null, progressPanel);
1618 if (sevenZipExe == null) {
1619 handleError(
"Error finding 7-Zip exectuable", Bundle.PortableCaseReportModule_compressCase_errorFinding7zip(), null, progressPanel);
1624 String chunkOption =
"";
1629 File zipFile = Paths.get(tempZipFolder.getAbsolutePath(), caseName +
".zip").toFile();
1630 ProcessBuilder procBuilder =
new ProcessBuilder();
1631 procBuilder.command(
1632 sevenZipExe.getAbsolutePath(),
1634 zipFile.getAbsolutePath(),
1635 dirToCompress.toAbsolutePath().toString(),
1640 Process process = procBuilder.start();
1642 while (process.isAlive()) {
1649 int exitCode = process.exitValue();
1650 if (exitCode != 0) {
1652 StringBuilder sb =
new StringBuilder();
1653 try (BufferedReader br =
new BufferedReader(
new InputStreamReader(process.getErrorStream()))) {
1655 while ((line = br.readLine()) != null) {
1656 sb.append(line).append(System.getProperty(
"line.separator"));
1660 handleError(
"Error compressing case\n7-Zip output: " + sb.toString(), Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), null, progressPanel);
1663 }
catch (IOException | InterruptedException ex) {
1664 handleError(
"Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel);
1670 FileUtils.cleanDirectory(dirToCompress.toFile());
1671 FileUtils.copyDirectory(tempZipFolder, dirToCompress.toFile());
1672 FileUtils.deleteDirectory(
new File(tempZipFolder.getParent()));
1673 }
catch (IOException ex) {
1674 handleError(
"Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel);
1691 String executableToFindName = Paths.get(
"7-Zip",
"7z.exe").toString();
1693 if (null == exeFile) {
1697 if (!exeFile.canExecute()) {
1710 private final Map<String, Long>
setCounts =
new HashMap<>();
1717 Long setCount = rs.getLong(
"set_count");
1718 String setName = rs.getString(
"set_name");
1720 setCounts.put(setName, setCount);
1722 }
catch (SQLException ex) {
1723 logger.log(Level.WARNING,
"Unable to get data_source_obj_id or value from result set", ex);
1726 }
catch (SQLException ex) {
1727 logger.log(Level.WARNING,
"Failed to get next result for values by datasource", ex);
static final List< Integer > SPECIALLY_HANDLED_ATTRS
boolean includeApplication()
final Map< Integer, Integer > oldArtTypeIdToNewArtTypeId
void handleError(String logWarning, String dialogWarning, Exception ex, ReportProgressPanel progressPanel)
long copyContent(Content content)
static final Logger logger
Host copyHost(Host oldHost)
int getNewArtifactTypeId(BlackboardArtifact oldArtifact)
static final List< FileTypeCategory > FILE_TYPE_CATEGORIES
static final String MAX_ID_TABLE_NAME
void process(ResultSet rs)
final Map< TagName, TagName > oldTagNameToNewTagName
OsAccount copyOsAccount(Long oldOsAccountId)
final Map< Long, Content > oldIdToNewContent
final Map< Long, BlackboardArtifact > oldArtifactIdToNewArtifact
PortableCaseReportModule()
final Map< Long, OsAccount > oldOsAccountIdToNewOsAccount
String getTempDirectory()
String getAutopsyExeName()
long copyContentToPortableCase(Content content, ReportProgressPanel progressPanel)
void addArtifactsToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel)
static final String FILE_FOLDER_NAME
final Map< Integer, BlackboardAttribute.Type > oldAttrTypeIdToNewAttrType
static String getAppName()
void process(ResultSet rs)
void complete(ReportStatus reportStatus)
List< TagName > getSelectedTagNames()
Map< String, Long > getSetCountMap()
static File locate7ZipExecutable()
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)
void closePortableCaseDatabase()
List< String > getAllInterestingItemsSets()
void createAppLaunchBatFile(String destBaseFolder)
final Map< Long, OsAccountRealm > oldRealmIdToNewRealm
static final String UNKNOWN_FILE_TYPE_FOLDER
Logger(String name, String resourceBundleName)
void setIndeterminate(boolean indeterminate)
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)
String getRelativeFilePath()
TagsManager getTagsManager()
static final String CASE_UCO_TMP_DIR
void initializeImageTags(ReportProgressPanel progressPanel)
String getExportSubfolder(AbstractFile abstractFile)
final Map< Long, Host > oldHostIdToNewHost
static final String TABLE_SCHEMA_SQLITE
final Map< Long, Content > newIdToContent
SleuthkitCase portableSkCase
static final String TABLE_NAME
final Map< String, Long > setCounts
SleuthkitCase getSleuthkitCase()
List< String > getSelectedSetNames()
BlackboardArtifact copyArtifact(long newContentId, BlackboardArtifact artifactToCopy)
void generateCaseUcoReport(List< TagName > tagNames, List< String > setNames, ReportProgressPanel progressPanel)
void copyApplication(Path sourceFolder, String destBaseFolder)
SleuthkitCase createPortableCase(String caseName, File portableCaseFolder)
boolean areAllTagsSelected()
boolean addUniqueFile(Content content, DataSource dataSource, Path tmpDir, Gson gson, CaseUcoExporter exporter, JsonWriter reportWriter, boolean dataSourceHasBeenIncluded)
static String escapeFileName(String fileName)
boolean compressCase(ReportProgressPanel progressPanel, String folderToCompress)
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
void updateStatusLabel(String statusMessage)
String getImageTagDataForContentTag(ContentTag tag)
static final String CASE_UCO_FILE_NAME
String getSevenZipParam()
boolean areAllSetsSelected()
void addImageTagToPortableCase(ContentTag newContentTag, String appData)
void handleCancellation(ReportProgressPanel progressPanel)
Path getApplicationBasePath()
void createCase(File outputDir, ReportProgressPanel progressPanel)
void process(ResultSet rs)
PortableCaseReportModuleSettings settings