19 package org.sleuthkit.autopsy.report;
21 import java.util.logging.Level;
22 import java.io.BufferedReader;
24 import java.io.InputStreamReader;
25 import java.io.IOException;
26 import java.nio.file.Paths;
27 import java.sql.ResultSet;
28 import java.sql.SQLException;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.HashMap;
32 import java.util.List;
34 import org.apache.commons.io.FileUtils;
35 import org.openide.modules.InstalledFileLocator;
36 import org.openide.util.NbBundle;
56 import org.
sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
67 class PortableCaseReportModule
implements ReportModule {
68 private static final Logger logger = Logger.getLogger(PortableCaseReportModule.class.getName());
69 private static final String FILE_FOLDER_NAME =
"PortableCaseFiles";
70 private static final String UNKNOWN_FILE_TYPE_FOLDER =
"Other";
71 private static final String MAX_ID_TABLE_NAME =
"portable_case_max_ids";
72 private PortableCaseOptions options;
75 private static final List<FileTypeCategory> FILE_TYPE_CATEGORIES = Arrays.asList(FileTypeCategory.AUDIO, FileTypeCategory.DOCUMENTS,
76 FileTypeCategory.EXECUTABLE, FileTypeCategory.IMAGE, FileTypeCategory.VIDEO);
78 private Case currentCase = null;
79 private SleuthkitCase portableSkCase = null;
80 private final String caseName;
81 private File caseFolder = null;
82 private File copiedFilesFolder = null;
85 private final Map<Long, Content> oldIdToNewContent =
new HashMap<>();
88 private final Map<Long, Content> newIdToContent =
new HashMap<>();
91 private final Map<TagName, TagName> oldTagNameToNewTagName =
new HashMap<>();
94 private final Map<Integer, Integer> oldArtTypeIdToNewArtTypeId =
new HashMap<>();
97 private final Map<Integer, BlackboardAttribute.Type> oldAttrTypeIdToNewAttrType =
new HashMap<>();
100 private final Map<Long, BlackboardArtifact> oldArtifactIdToNewArtifact =
new HashMap<>();
102 PortableCaseReportModule() {
103 caseName = Case.getCurrentCase().getDisplayName() +
" (Portable)";
107 "PortableCaseReportModule.getName.name=Portable Case"
110 public String getName() {
111 return Bundle.PortableCaseReportModule_getName_name();
115 "PortableCaseReportModule.getDescription.description=Copies selected items to a new single-user case that can be easily shared"
118 public String getDescription() {
119 return Bundle.PortableCaseReportModule_getDescription_description();
123 public String getRelativeFilePath() {
132 private void handleCancellation(ReportProgressPanel progressPanel) {
133 logger.log(Level.INFO,
"Portable case creation canceled by user");
134 progressPanel.setIndeterminate(
false);
135 progressPanel.complete(ReportProgressPanel.ReportStatus.CANCELED);
149 private void handleError(String logWarning, String dialogWarning, Exception ex, ReportProgressPanel progressPanel) {
151 logger.log(Level.WARNING, logWarning);
153 logger.log(Level.SEVERE, logWarning, ex);
155 MessageNotifyUtil.Message.error(dialogWarning);
156 progressPanel.setIndeterminate(
false);
157 progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
162 "PortableCaseReportModule.generateReport.verifying=Verifying selected parameters...",
163 "PortableCaseReportModule.generateReport.creatingCase=Creating portable case database...",
164 "PortableCaseReportModule.generateReport.copyingTags=Copying tags...",
166 "PortableCaseReportModule.generateReport.copyingFiles=Copying files tagged as {0}...",
168 "PortableCaseReportModule.generateReport.copyingArtifacts=Copying artifacts tagged as {0}...",
169 "# {0} - output folder",
170 "PortableCaseReportModule.generateReport.outputDirDoesNotExist=Output folder {0} does not exist",
171 "# {0} - output folder",
172 "PortableCaseReportModule.generateReport.outputDirIsNotDir=Output folder {0} is not a folder",
173 "PortableCaseReportModule.generateReport.caseClosed=Current case has been closed",
174 "PortableCaseReportModule.generateReport.interestingItemError=Error loading intersting items",
175 "PortableCaseReportModule.generateReport.noContentToCopy=No interesting files, results, or tagged items to copy",
176 "PortableCaseReportModule.generateReport.errorCopyingTags=Error copying tags",
177 "PortableCaseReportModule.generateReport.errorCopyingFiles=Error copying tagged files",
178 "PortableCaseReportModule.generateReport.errorCopyingArtifacts=Error copying tagged artifacts",
179 "PortableCaseReportModule.generateReport.errorCopyingInterestingFiles=Error copying interesting files",
180 "PortableCaseReportModule.generateReport.errorCopyingInterestingResults=Error copying interesting results",
181 "# {0} - attribute type name",
182 "PortableCaseReportModule.generateReport.errorLookingUpAttrType=Error looking up attribute type {0}",
183 "PortableCaseReportModule.generateReport.compressingCase=Compressing case...",
186 void generateReport(String reportPath, PortableCaseOptions options, ReportProgressPanel progressPanel) {
187 this.options = options;
188 progressPanel.setIndeterminate(
true);
189 progressPanel.start();
190 progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_verifying());
196 File outputDir =
new File(reportPath);
197 if (! outputDir.exists()) {
198 handleError(
"Output folder " + outputDir.toString() +
" does not exist",
199 Bundle.PortableCaseReportModule_generateReport_outputDirDoesNotExist(outputDir.toString()), null, progressPanel);
203 if (! outputDir.isDirectory()) {
204 handleError(
"Output folder " + outputDir.toString() +
" is not a folder",
205 Bundle.PortableCaseReportModule_generateReport_outputDirIsNotDir(outputDir.toString()), null, progressPanel);
211 currentCase = Case.getCurrentCaseThrows();
212 }
catch (NoCurrentCaseException ex) {
213 handleError(
"Current case has been closed",
214 Bundle.PortableCaseReportModule_generateReport_caseClosed(), null, progressPanel);
219 List<TagName> tagNames = options.getSelectedTagNames();
220 List<String> setNames = options.getSelectedSetNames();
221 if (tagNames.isEmpty() && setNames.isEmpty()) {
222 handleError(
"No content to copy",
223 Bundle.PortableCaseReportModule_generateReport_noContentToCopy(), null, progressPanel);
229 progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_creatingCase());
230 createCase(outputDir, progressPanel);
231 if (portableSkCase == null) {
237 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
238 handleCancellation(progressPanel);
243 progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingTags());
245 for(TagName tagName:tagNames) {
246 TagName newTagName = portableSkCase.addOrUpdateTagName(tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus());
247 oldTagNameToNewTagName.put(tagName, newTagName);
249 }
catch (TskCoreException ex) {
250 handleError(
"Error copying tags", Bundle.PortableCaseReportModule_generateReport_errorCopyingTags(), ex, progressPanel);
255 for (BlackboardArtifact.ARTIFACT_TYPE type:BlackboardArtifact.ARTIFACT_TYPE.values()) {
256 oldArtTypeIdToNewArtTypeId.put(type.getTypeID(), type.getTypeID());
258 for (BlackboardAttribute.ATTRIBUTE_TYPE type:BlackboardAttribute.ATTRIBUTE_TYPE.values()) {
260 oldAttrTypeIdToNewAttrType.put(type.getTypeID(), portableSkCase.getAttributeType(type.getLabel()));
261 }
catch (TskCoreException ex) {
262 handleError(
"Error looking up attribute name " + type.getLabel(),
263 Bundle.PortableCaseReportModule_generateReport_errorLookingUpAttrType(type.getLabel()),
270 for(TagName tagName:tagNames) {
272 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
273 handleCancellation(progressPanel);
276 progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingFiles(tagName.getDisplayName()));
277 addFilesToPortableCase(tagName, progressPanel);
280 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
281 handleCancellation(progressPanel);
285 }
catch (TskCoreException ex) {
286 handleError(
"Error copying tagged files", Bundle.PortableCaseReportModule_generateReport_errorCopyingFiles(), ex, progressPanel);
292 for(TagName tagName:tagNames) {
294 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
295 handleCancellation(progressPanel);
298 progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingArtifacts(tagName.getDisplayName()));
299 addArtifactsToPortableCase(tagName, progressPanel);
302 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
303 handleCancellation(progressPanel);
307 }
catch (TskCoreException ex) {
308 handleError(
"Error copying tagged artifacts", Bundle.PortableCaseReportModule_generateReport_errorCopyingArtifacts(), ex, progressPanel);
313 if (! setNames.isEmpty()) {
315 List<BlackboardArtifact> interestingFiles = currentCase.getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
316 for (BlackboardArtifact art:interestingFiles) {
318 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
319 handleCancellation(progressPanel);
323 BlackboardAttribute setAttr = art.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
324 if (setNames.contains(setAttr.getValueString())) {
325 copyContentToPortableCase(art, progressPanel);
328 }
catch (TskCoreException ex) {
329 handleError(
"Error copying interesting files", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingFiles(), ex, progressPanel);
334 List<BlackboardArtifact> interestingResults = currentCase.getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT);
335 for (BlackboardArtifact art:interestingResults) {
337 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
338 handleCancellation(progressPanel);
341 BlackboardAttribute setAttr = art.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
342 if (setNames.contains(setAttr.getValueString())) {
343 copyContentToPortableCase(art, progressPanel);
346 }
catch (TskCoreException ex) {
347 handleError(
"Error copying interesting results", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingResults(), ex, progressPanel);
353 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
354 handleCancellation(progressPanel);
359 if (options.shouldCompress()) {
360 progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_compressingCase());
362 boolean success = compressCase(progressPanel);
365 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
366 handleCancellation(progressPanel);
379 progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE);
391 "# {0} - case folder",
392 "PortableCaseReportModule.createCase.caseDirExists=Case folder {0} already exists",
393 "PortableCaseReportModule.createCase.errorCreatingCase=Error creating case",
395 "PortableCaseReportModule.createCase.errorCreatingFolder=Error creating folder {0}",
396 "PortableCaseReportModule.createCase.errorStoringMaxIds=Error storing maximum database IDs",
398 private void createCase(File outputDir, ReportProgressPanel progressPanel) {
401 caseFolder = Paths.get(outputDir.toString(), caseName).toFile();
403 if (caseFolder.exists()) {
404 handleError(
"Case folder " + caseFolder.toString() +
" already exists",
405 Bundle.PortableCaseReportModule_createCase_caseDirExists(caseFolder.toString()), null, progressPanel);
411 portableSkCase = currentCase.createPortableCase(caseName, caseFolder);
412 }
catch (TskCoreException ex) {
413 handleError(
"Error creating case " + caseName +
" in folder " + caseFolder.toString(),
414 Bundle.PortableCaseReportModule_createCase_errorCreatingCase(), ex, progressPanel);
421 }
catch (TskCoreException ex) {
422 handleError(
"Error storing maximum database IDs",
423 Bundle.PortableCaseReportModule_createCase_errorStoringMaxIds(), ex, progressPanel);
428 copiedFilesFolder = Paths.get(caseFolder.toString(), FILE_FOLDER_NAME).toFile();
429 if (! copiedFilesFolder.mkdir()) {
430 handleError(
"Error creating folder " + copiedFilesFolder.toString(),
431 Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(copiedFilesFolder.toString()), null, progressPanel);
436 for (FileTypeCategory cat:FILE_TYPE_CATEGORIES) {
437 File subFolder = Paths.get(copiedFilesFolder.toString(), cat.getDisplayName()).toFile();
438 if (! subFolder.mkdir()) {
439 handleError(
"Error creating folder " + subFolder.toString(),
440 Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(subFolder.toString()), null, progressPanel);
444 File unknownTypeFolder = Paths.get(copiedFilesFolder.toString(), UNKNOWN_FILE_TYPE_FOLDER).toFile();
445 if (! unknownTypeFolder.mkdir()) {
446 handleError(
"Error creating folder " + unknownTypeFolder.toString(),
447 Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(unknownTypeFolder.toString()), null, progressPanel);
458 private void saveHighestIds() throws TskCoreException {
460 CaseDbAccessManager currentCaseDbManager = currentCase.getSleuthkitCase().getCaseDbAccessManager();
462 String tableSchema =
"( table_name TEXT PRIMARY KEY, "
465 portableSkCase.getCaseDbAccessManager().createTable(MAX_ID_TABLE_NAME, tableSchema);
467 currentCaseDbManager.select(
"max(obj_id) as max_id from tsk_objects",
new StoreMaxIdCallback(
"tsk_objects"));
468 currentCaseDbManager.select(
"max(tag_id) as max_id from content_tags",
new StoreMaxIdCallback(
"content_tags"));
469 currentCaseDbManager.select(
"max(tag_id) as max_id from blackboard_artifact_tags",
new StoreMaxIdCallback(
"blackboard_artifact_tags"));
470 currentCaseDbManager.select(
"max(examiner_id) as max_id from tsk_examiners",
new StoreMaxIdCallback(
"tsk_examiners"));
481 private void addFilesToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel)
throws TskCoreException {
484 List<ContentTag> tags = currentCase.getServices().getTagsManager().getContentTagsByTagName(oldTagName);
487 for (ContentTag tag : tags) {
490 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
494 Content content = tag.getContent();
495 if (content instanceof AbstractFile) {
496 long newFileId = copyContentToPortableCase(content, progressPanel);
499 if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
500 throw new TskCoreException(
"TagName map is missing entry for ID " + tag.getName().getId() +
" with display name " + tag.getName().getDisplayName());
502 portableSkCase.addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
515 private void addArtifactsToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel)
throws TskCoreException {
517 List<BlackboardArtifactTag> tags = currentCase.getServices().getTagsManager().getBlackboardArtifactTagsByTagName(oldTagName);
520 for (BlackboardArtifactTag tag : tags) {
523 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
528 Content content = tag.getContent();
529 long newContentId = copyContentToPortableCase(content, progressPanel);
532 BlackboardArtifact newArtifact = copyArtifact(newContentId, tag.getArtifact());
535 if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
536 throw new TskCoreException(
"TagName map is missing entry for ID " + tag.getName().getId() +
" with display name " + tag.getName().getDisplayName());
538 portableSkCase.addBlackboardArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment());
552 private BlackboardArtifact copyArtifact(
long newContentId, BlackboardArtifact artifactToCopy)
throws TskCoreException {
554 if (oldArtifactIdToNewArtifact.containsKey(artifactToCopy.getArtifactID())) {
555 return oldArtifactIdToNewArtifact.get(artifactToCopy.getArtifactID());
559 BlackboardAttribute oldAssociatedAttribute = artifactToCopy.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
560 List<BlackboardAttribute> newAttrs =
new ArrayList<>();
561 if (oldAssociatedAttribute != null) {
562 BlackboardArtifact oldAssociatedArtifact = currentCase.getSleuthkitCase().getBlackboardArtifact(oldAssociatedAttribute.getValueLong());
563 BlackboardArtifact newAssociatedArtifact = copyArtifact(newContentId, oldAssociatedArtifact);
564 newAttrs.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
565 String.join(
",", oldAssociatedAttribute.getSources()), newAssociatedArtifact.getArtifactID()));
569 int newArtifactTypeId = getNewArtifactTypeId(artifactToCopy);
570 BlackboardArtifact newArtifact = portableSkCase.newBlackboardArtifact(newArtifactTypeId, newContentId);
571 List<BlackboardAttribute> oldAttrs = artifactToCopy.getAttributes();
574 for (BlackboardAttribute oldAttr:oldAttrs) {
577 if (oldAttr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
581 BlackboardAttribute.Type newAttributeType = getNewAttributeType(oldAttr);
582 switch (oldAttr.getValueType()) {
584 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
585 oldAttr.getValueBytes()));
588 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
589 oldAttr.getValueDouble()));
592 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
593 oldAttr.getValueInt()));
597 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
598 oldAttr.getValueLong()));
601 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
602 oldAttr.getValueString()));
605 throw new TskCoreException(
"Unexpected attribute value type found: " + oldAttr.getValueType().getLabel());
609 newArtifact.addAttributes(newAttrs);
611 oldArtifactIdToNewArtifact.put(artifactToCopy.getArtifactID(), newArtifact);
623 private int getNewArtifactTypeId(BlackboardArtifact oldArtifact)
throws TskCoreException {
624 if (oldArtTypeIdToNewArtTypeId.containsKey(oldArtifact.getArtifactTypeID())) {
625 return oldArtTypeIdToNewArtTypeId.get(oldArtifact.getArtifactTypeID());
628 BlackboardArtifact.Type oldCustomType = currentCase.getSleuthkitCase().getArtifactType(oldArtifact.getArtifactTypeName());
630 BlackboardArtifact.Type newCustomType = portableSkCase.addBlackboardArtifactType(oldCustomType.getTypeName(), oldCustomType.getDisplayName());
631 oldArtTypeIdToNewArtTypeId.put(oldArtifact.getArtifactTypeID(), newCustomType.getTypeID());
632 return newCustomType.getTypeID();
633 }
catch (TskDataException ex) {
634 throw new TskCoreException(
"Error creating new artifact type " + oldCustomType.getTypeName(), ex);
646 private BlackboardAttribute.Type getNewAttributeType(BlackboardAttribute oldAttribute)
throws TskCoreException {
647 BlackboardAttribute.Type oldAttrType = oldAttribute.getAttributeType();
648 if (oldAttrTypeIdToNewAttrType.containsKey(oldAttrType.getTypeID())) {
649 return oldAttrTypeIdToNewAttrType.get(oldAttrType.getTypeID());
653 BlackboardAttribute.Type newCustomType = portableSkCase.addArtifactAttributeType(oldAttrType.getTypeName(),
654 oldAttrType.getValueType(), oldAttrType.getDisplayName());
655 oldAttrTypeIdToNewAttrType.put(oldAttribute.getAttributeType().getTypeID(), newCustomType);
656 return newCustomType;
657 }
catch (TskDataException ex) {
658 throw new TskCoreException(
"Error creating new attribute type " + oldAttrType.getTypeName(), ex);
674 "PortableCaseReportModule.copyContentToPortableCase.copyingFile=Copying file {0}",
676 private long copyContentToPortableCase(Content content, ReportProgressPanel progressPanel)
throws TskCoreException {
677 progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_copyContentToPortableCase_copyingFile(content.getUniquePath()));
678 return copyContent(content);
690 private long copyContent(Content content)
throws TskCoreException {
693 if (oldIdToNewContent.containsKey(content.getId())) {
694 return oldIdToNewContent.get(content.getId()).getId();
701 if (content.getParent() != null) {
702 parentId = copyContent(content.getParent());
706 if (content instanceof BlackboardArtifact) {
707 BlackboardArtifact artifactToCopy = (BlackboardArtifact)content;
708 newContent = copyArtifact(parentId, artifactToCopy);
710 CaseDbTransaction trans = portableSkCase.beginTransaction();
712 if (content instanceof Image) {
713 Image image = (Image)content;
714 newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(),
715 new ArrayList<>(), image.getTimeZone(), image.getMd5(), image.getSha1(), image.getSha256(), image.getDeviceId(), trans);
716 }
else if (content instanceof VolumeSystem) {
717 VolumeSystem vs = (VolumeSystem)content;
718 newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
719 }
else if (content instanceof Volume) {
720 Volume vs = (Volume)content;
721 newContent = portableSkCase.addVolume(parentId, vs.getAddr(), vs.getStart(), vs.getLength(),
722 vs.getDescription(), vs.getFlags(), trans);
723 }
else if (content instanceof FileSystem) {
724 FileSystem fs = (FileSystem)content;
725 newContent = portableSkCase.addFileSystem(parentId, fs.getImageOffset(), fs.getFsType(), fs.getBlock_size(),
726 fs.getBlock_count(), fs.getRoot_inum(), fs.getFirst_inum(), fs.getLastInum(),
727 fs.getName(), trans);
728 }
else if (content instanceof BlackboardArtifact) {
729 BlackboardArtifact artifactToCopy = (BlackboardArtifact)content;
730 newContent = copyArtifact(parentId, artifactToCopy);
731 }
else if (content instanceof AbstractFile) {
732 AbstractFile abstractFile = (AbstractFile)content;
734 if (abstractFile instanceof LocalFilesDataSource) {
735 LocalFilesDataSource localFilesDS = (LocalFilesDataSource)abstractFile;
736 newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), trans);
738 if (abstractFile.isDir()) {
739 newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
743 String fileName = abstractFile.getId() +
"-" + FileUtil.escapeFileName(abstractFile.getName());
744 String exportSubFolder = getExportSubfolder(abstractFile);
745 File exportFolder = Paths.get(copiedFilesFolder.toString(), exportSubFolder).toFile();
746 File localFile =
new File(exportFolder, fileName);
747 ContentUtils.writeToFile(abstractFile, localFile);
750 Content oldParent = abstractFile.getParent();
751 if (! oldIdToNewContent.containsKey(oldParent.getId())) {
752 throw new TskCoreException(
"Parent of file with ID " + abstractFile.getId() +
" has not been created");
754 Content newParent = oldIdToNewContent.get(oldParent.getId());
757 String relativePath = FILE_FOLDER_NAME + File.separator + exportSubFolder + File.separator + fileName;
759 newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
760 abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getMtime(),
761 abstractFile.getMd5Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
762 true, TskData.EncodingType.NONE,
764 }
catch (IOException ex) {
765 throw new TskCoreException(
"Error copying file " + abstractFile.getName() +
" with original obj ID "
766 + abstractFile.getId(), ex);
771 throw new TskCoreException(
"Trying to copy unexpected Content type " + content.getClass().getName());
774 }
catch (TskCoreException ex) {
781 oldIdToNewContent.put(content.getId(), newContent);
782 newIdToContent.put(newContent.getId(), newContent);
783 return oldIdToNewContent.get(content.getId()).getId();
793 private String getExportSubfolder(AbstractFile abstractFile) {
794 if (abstractFile.getMIMEType() == null || abstractFile.getMIMEType().isEmpty()) {
795 return UNKNOWN_FILE_TYPE_FOLDER;
798 for (FileTypeCategory cat:FILE_TYPE_CATEGORIES) {
799 if (cat.getMediaTypes().contains(abstractFile.getMIMEType())) {
800 return cat.getDisplayName();
803 return UNKNOWN_FILE_TYPE_FOLDER;
809 private void cleanup() {
810 oldIdToNewContent.clear();
811 newIdToContent.clear();
812 oldTagNameToNewTagName.clear();
813 oldArtTypeIdToNewArtTypeId.clear();
814 oldAttrTypeIdToNewAttrType.clear();
815 oldArtifactIdToNewArtifact.clear();
817 closePortableCaseDatabase();
821 copiedFilesFolder = null;
827 private void closePortableCaseDatabase() {
828 if (portableSkCase != null) {
829 portableSkCase.close();
830 portableSkCase = null;
854 Long maxId = rs.getLong(
"max_id");
855 String query =
" (table_name, max_id) VALUES ('" + tableName +
"', '" + maxId +
"')";
856 portableSkCase.getCaseDbAccessManager().insert(MAX_ID_TABLE_NAME, query);
858 }
catch (SQLException ex) {
859 logger.log(Level.WARNING,
"Unable to get maximum ID from result set", ex);
860 }
catch (TskCoreException ex) {
861 logger.log(Level.WARNING,
"Unable to save maximum ID from result set", ex);
865 }
catch (SQLException ex) {
866 logger.log(Level.WARNING,
"Failed to get maximum ID from result set", ex);
872 "PortableCaseReportModule.compressCase.errorFinding7zip=Could not locate 7-Zip executable",
873 "# {0} - Temp folder path",
874 "PortableCaseReportModule.compressCase.errorCreatingTempFolder=Could not create temporary folder {0}",
875 "PortableCaseReportModule.compressCase.errorCompressingCase=Error compressing case",
876 "PortableCaseReportModule.compressCase.canceled=Compression canceled by user",
878 private boolean compressCase(ReportProgressPanel progressPanel) {
881 closePortableCaseDatabase();
884 File tempZipFolder = Paths.get(currentCase.getTempDirectory(),
"portableCase" + System.currentTimeMillis()).toFile();
885 if (! tempZipFolder.mkdir()) {
886 handleError(
"Error creating temporary folder " + tempZipFolder.toString(),
887 Bundle.PortableCaseReportModule_compressCase_errorCreatingTempFolder(tempZipFolder.toString()), null, progressPanel);
892 File sevenZipExe = locate7ZipExecutable();
893 if (sevenZipExe == null) {
894 handleError(
"Error finding 7-Zip exectuable", Bundle.PortableCaseReportModule_compressCase_errorFinding7zip(), null, progressPanel);
899 String chunkOption =
"";
900 if (options.getChunkSize() != ChunkSize.NONE) {
901 chunkOption =
"-v" + options.getChunkSize().getSevenZipParam();
904 File zipFile = Paths.get(tempZipFolder.getAbsolutePath(), caseName +
".zip").toFile();
905 ProcessBuilder procBuilder =
new ProcessBuilder();
907 sevenZipExe.getAbsolutePath(),
909 zipFile.getAbsolutePath(),
910 caseFolder.getAbsolutePath(),
915 Process process = procBuilder.start();
917 while (process.isAlive()) {
918 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
924 int exitCode = process.exitValue();
927 StringBuilder sb =
new StringBuilder();
928 try (BufferedReader br =
new BufferedReader(
new InputStreamReader(process.getErrorStream()))) {
930 while ((line = br.readLine()) != null) {
931 sb.append(line).append(System.getProperty(
"line.separator"));
935 handleError(
"Error compressing case\n7-Zip output: " + sb.toString(), Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), null, progressPanel);
938 }
catch (IOException | InterruptedException ex) {
939 handleError(
"Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel);
945 FileUtils.cleanDirectory(caseFolder);
946 FileUtils.copyDirectory(tempZipFolder, caseFolder);
947 FileUtils.deleteDirectory(tempZipFolder);
948 }
catch (IOException ex) {
949 handleError(
"Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel);
961 private static File locate7ZipExecutable() {
962 if (!PlatformUtil.isWindowsOS()) {
966 String executableToFindName = Paths.get(
"7-Zip",
"7z.exe").toString();
967 File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, PortableCaseReportModule.class.getPackage().getName(),
false);
968 if (null == exeFile) {
972 if (!exeFile.canExecute()) {
985 NONE(
"Do not split",
""),
986 DVD(
"4.5 GB (DVD)",
"4500m");
988 private final String displayName;
989 private final String sevenZipParam;
997 private ChunkSize(String displayName, String sevenZipParam) {
998 this.displayName = displayName;
999 this.sevenZipParam = sevenZipParam;
1002 String getDisplayName() {
1006 String getSevenZipParam() {
1007 return sevenZipParam;
1011 public String toString() {
1019 static class PortableCaseOptions {
1021 private final List<TagName> tagNames =
new ArrayList<>();
1022 private final List<String> setNames =
new ArrayList<>();
1023 private boolean compress;
1024 private ChunkSize chunkSize;
1026 PortableCaseOptions(List<String> setNames, List<TagName> tagNames,
1027 boolean compress, ChunkSize chunkSize) {
1028 this.setNames.addAll(setNames);
1029 this.tagNames.addAll(tagNames);
1030 this.compress = compress;
1031 this.chunkSize = chunkSize;
1034 PortableCaseOptions() {
1035 this.compress =
false;
1036 this.chunkSize = ChunkSize.NONE;
1039 void updateSetNames(List<String> setNames) {
1040 this.setNames.clear();
1041 this.setNames.addAll(setNames);
1044 void updateTagNames(List<TagName> tagNames) {
1045 this.tagNames.clear();
1046 this.tagNames.addAll(tagNames);
1049 void updateCompression(
boolean compress, ChunkSize chunkSize) {
1050 this.compress = compress;
1051 this.chunkSize = chunkSize;
1055 return (( !setNames.isEmpty()) || ( ! tagNames.isEmpty()));
1058 List<String> getSelectedSetNames() {
1059 return new ArrayList<>(setNames);
1062 List<TagName> getSelectedTagNames() {
1063 return new ArrayList<>(tagNames);
1066 boolean shouldCompress() {
1070 ChunkSize getChunkSize() {
void process(ResultSet rs)