19 package org.sleuthkit.autopsy.report;
21 import org.openide.util.lookup.ServiceProvider;
22 import javax.swing.JPanel;
23 import java.util.logging.Level;
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.openide.util.NbBundle;
53 import org.
sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
64 @ServiceProvider(service = GeneralReportModule.class)
67 private static final String FILE_FOLDER_NAME =
"PortableCaseFiles";
68 private static final String UNKNOWN_FILE_TYPE_FOLDER =
"Other";
69 private static final String MAX_ID_TABLE_NAME =
"portable_case_max_ids";
76 private Case currentCase = null;
77 private SleuthkitCase portableSkCase = null;
78 private File caseFolder = null;
79 private File copiedFilesFolder = null;
82 private final Map<Long, Content> oldIdToNewContent =
new HashMap<>();
85 private final Map<Long, Content> newIdToContent =
new HashMap<>();
88 private final Map<TagName, TagName> oldTagNameToNewTagName =
new HashMap<>();
91 private final Map<Integer, Integer> oldArtTypeIdToNewArtTypeId =
new HashMap<>();
94 private final Map<Integer, BlackboardAttribute.Type> oldAttrTypeIdToNewAttrType =
new HashMap<>();
97 private final Map<Long, BlackboardArtifact> oldArtifactIdToNewArtifact =
new HashMap<>();
104 "CreatePortableCaseModule.getName.name=Portable Case"
108 return Bundle.CreatePortableCaseModule_getName_name();
112 "CreatePortableCaseModule.getDescription.description=Copies selected tagged items to a new single-user case that will work anywhere"
116 return Bundle.CreatePortableCaseModule_getDescription_description();
136 logger.log(Level.WARNING, logWarning);
138 logger.log(Level.SEVERE, logWarning, ex);
147 "CreatePortableCaseModule.generateReport.verifying=Verifying selected parameters...",
148 "CreatePortableCaseModule.generateReport.creatingCase=Creating portable case database...",
149 "CreatePortableCaseModule.generateReport.copyingTags=Copying tags...",
151 "CreatePortableCaseModule.generateReport.copyingFiles=Copying files tagged as {0}...",
153 "CreatePortableCaseModule.generateReport.copyingArtifacts=Copying artifacts tagged as {0}...",
154 "# {0} - output folder",
155 "CreatePortableCaseModule.generateReport.outputDirDoesNotExist=Output folder {0} does not exist",
156 "# {0} - output folder",
157 "CreatePortableCaseModule.generateReport.outputDirIsNotDir=Output folder {0} is not a folder",
158 "CreatePortableCaseModule.generateReport.noTagsSelected=No tags selected for export.",
159 "CreatePortableCaseModule.generateReport.caseClosed=Current case has been closed",
160 "CreatePortableCaseModule.generateReport.errorCopyingTags=Error copying tags",
161 "CreatePortableCaseModule.generateReport.errorCopyingFiles=Error copying tagged files",
162 "CreatePortableCaseModule.generateReport.errorCopyingArtifacts=Error copying tagged artifacts",
163 "# {0} - attribute type name",
164 "CreatePortableCaseModule.generateReport.errorLookingUpAttrType=Error looking up attribute type {0}",
169 progressPanel.
start();
170 progressPanel.
updateStatusLabel(Bundle.CreatePortableCaseModule_generateReport_verifying());
176 File outputDir =
new File(configPanel.getOutputFolder());
177 if (! outputDir.exists()) {
178 handleError(
"Output folder " + outputDir.toString() +
" does not exist",
179 Bundle.CreatePortableCaseModule_generateReport_outputDirDoesNotExist(outputDir.toString()), null, progressPanel);
183 if (! outputDir.isDirectory()) {
184 handleError(
"Output folder " + outputDir.toString() +
" is not a folder",
185 Bundle.CreatePortableCaseModule_generateReport_outputDirIsNotDir(outputDir.toString()), null, progressPanel);
189 List<TagName> tagNames = configPanel.getSelectedTagNames();
190 if (tagNames.isEmpty()) {
191 handleError(
"No tags selected for export",
192 Bundle.CreatePortableCaseModule_generateReport_noTagsSelected(), null, progressPanel);
200 handleError(
"Current case has been closed",
201 Bundle.CreatePortableCaseModule_generateReport_caseClosed(), null, progressPanel);
208 progressPanel.
updateStatusLabel(Bundle.CreatePortableCaseModule_generateReport_creatingCase());
209 createCase(outputDir, progressPanel);
210 if (portableSkCase == null) {
222 progressPanel.
updateStatusLabel(Bundle.CreatePortableCaseModule_generateReport_copyingTags());
224 for(TagName tagName:tagNames) {
225 TagName newTagName = portableSkCase.addOrUpdateTagName(tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus());
226 oldTagNameToNewTagName.put(tagName, newTagName);
228 }
catch (TskCoreException ex) {
229 handleError(
"Error copying tags", Bundle.CreatePortableCaseModule_generateReport_errorCopyingTags(), ex, progressPanel);
235 for(TagName tagName:tagNames) {
240 progressPanel.
updateStatusLabel(Bundle.CreatePortableCaseModule_generateReport_copyingFiles(tagName.getDisplayName()));
241 addFilesToPortableCase(tagName, progressPanel);
243 }
catch (TskCoreException ex) {
244 handleError(
"Error copying tagged files", Bundle.CreatePortableCaseModule_generateReport_errorCopyingFiles(), ex, progressPanel);
249 for (BlackboardArtifact.ARTIFACT_TYPE type:BlackboardArtifact.ARTIFACT_TYPE.values()) {
250 oldArtTypeIdToNewArtTypeId.put(type.getTypeID(), type.getTypeID());
252 for (BlackboardAttribute.ATTRIBUTE_TYPE type:BlackboardAttribute.ATTRIBUTE_TYPE.values()) {
254 oldAttrTypeIdToNewAttrType.put(type.getTypeID(), portableSkCase.getAttributeType(type.getLabel()));
255 }
catch (TskCoreException ex) {
256 handleError(
"Error looking up attribute name " + type.getLabel(),
257 Bundle.CreatePortableCaseModule_generateReport_errorLookingUpAttrType(type.getLabel()),
264 for(TagName tagName:tagNames) {
269 progressPanel.
updateStatusLabel(Bundle.CreatePortableCaseModule_generateReport_copyingArtifacts(tagName.getDisplayName()));
270 addArtifactsToPortableCase(tagName, progressPanel);
272 }
catch (TskCoreException ex) {
273 handleError(
"Error copying tagged artifacts", Bundle.CreatePortableCaseModule_generateReport_errorCopyingArtifacts(), ex, progressPanel);
292 "# {0} - case folder",
293 "CreatePortableCaseModule.createCase.caseDirExists=Case folder {0} already exists",
294 "CreatePortableCaseModule.createCase.errorCreatingCase=Error creating case",
296 "CreatePortableCaseModule.createCase.errorCreatingFolder=Error creating folder {0}",
297 "CreatePortableCaseModule.createCase.errorStoringMaxIds=Error storing maximum database IDs",
303 caseFolder = Paths.get(outputDir.toString(), caseName).toFile();
305 if (caseFolder.exists()) {
306 handleError(
"Case folder " + caseFolder.toString() +
" already exists",
307 Bundle.CreatePortableCaseModule_createCase_caseDirExists(caseFolder.toString()), null, progressPanel);
314 }
catch (TskCoreException ex) {
315 handleError(
"Error creating case " + caseName +
" in folder " + caseFolder.toString(),
316 Bundle.CreatePortableCaseModule_createCase_errorCreatingCase(), ex, progressPanel);
323 }
catch (TskCoreException ex) {
324 handleError(
"Error storing maximum database IDs",
325 Bundle.CreatePortableCaseModule_createCase_errorStoringMaxIds(), ex, progressPanel);
330 copiedFilesFolder = Paths.get(caseFolder.toString(), FILE_FOLDER_NAME).toFile();
331 if (! copiedFilesFolder.mkdir()) {
332 handleError(
"Error creating folder " + copiedFilesFolder.toString(),
333 Bundle.CreatePortableCaseModule_createCase_errorCreatingFolder(copiedFilesFolder.toString()), null, progressPanel);
339 File subFolder = Paths.get(copiedFilesFolder.toString(), cat.getDisplayName()).toFile();
340 if (! subFolder.mkdir()) {
341 handleError(
"Error creating folder " + subFolder.toString(),
342 Bundle.CreatePortableCaseModule_createCase_errorCreatingFolder(subFolder.toString()), null, progressPanel);
346 File unknownTypeFolder = Paths.get(copiedFilesFolder.toString(), UNKNOWN_FILE_TYPE_FOLDER).toFile();
347 if (! unknownTypeFolder.mkdir()) {
348 handleError(
"Error creating folder " + unknownTypeFolder.toString(),
349 Bundle.CreatePortableCaseModule_createCase_errorCreatingFolder(unknownTypeFolder.toString()), null, progressPanel);
362 CaseDbAccessManager currentCaseDbManager = currentCase.
getSleuthkitCase().getCaseDbAccessManager();
364 String tableSchema =
"( table_name TEXT PRIMARY KEY, "
367 portableSkCase.getCaseDbAccessManager().createTable(MAX_ID_TABLE_NAME, tableSchema);
369 currentCaseDbManager.select(
"max(obj_id) as max_id from tsk_objects",
new StoreMaxIdCallback(
"tsk_objects"));
370 currentCaseDbManager.select(
"max(tag_id) as max_id from content_tags",
new StoreMaxIdCallback(
"content_tags"));
371 currentCaseDbManager.select(
"max(tag_id) as max_id from blackboard_artifact_tags",
new StoreMaxIdCallback(
"blackboard_artifact_tags"));
372 currentCaseDbManager.select(
"max(examiner_id) as max_id from tsk_examiners",
new StoreMaxIdCallback(
"tsk_examiners"));
389 for (ContentTag tag : tags) {
396 Content content = tag.getContent();
397 if (content instanceof AbstractFile) {
398 long newFileId = copyContentToPortableCase(content, progressPanel);
401 if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
402 throw new TskCoreException(
"TagName map is missing entry for ID " + tag.getName().getId() +
" with display name " + tag.getName().getDisplayName());
404 portableSkCase.addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
422 for (BlackboardArtifactTag tag : tags) {
430 Content content = tag.getContent();
431 long newContentId = copyContentToPortableCase(content, progressPanel);
434 BlackboardArtifact newArtifact = copyArtifact(newContentId, tag.getArtifact());
437 if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
438 throw new TskCoreException(
"TagName map is missing entry for ID " + tag.getName().getId() +
" with display name " + tag.getName().getDisplayName());
440 portableSkCase.addBlackboardArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment());
454 private BlackboardArtifact
copyArtifact(
long newContentId, BlackboardArtifact artifactToCopy)
throws TskCoreException {
456 if (oldArtifactIdToNewArtifact.containsKey(artifactToCopy.getArtifactID())) {
457 return oldArtifactIdToNewArtifact.get(artifactToCopy.getArtifactID());
461 BlackboardAttribute oldAssociatedAttribute = artifactToCopy.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
462 List<BlackboardAttribute> newAttrs =
new ArrayList<>();
463 if (oldAssociatedAttribute != null) {
464 BlackboardArtifact oldAssociatedArtifact = currentCase.
getSleuthkitCase().getBlackboardArtifact(oldAssociatedAttribute.getValueLong());
465 BlackboardArtifact newAssociatedArtifact = copyArtifact(newContentId, oldAssociatedArtifact);
466 newAttrs.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
467 String.join(
",", oldAssociatedAttribute.getSources()), newAssociatedArtifact.getArtifactID()));
471 int newArtifactTypeId = getNewArtifactTypeId(artifactToCopy);
472 BlackboardArtifact newArtifact = portableSkCase.newBlackboardArtifact(newArtifactTypeId, newContentId);
473 List<BlackboardAttribute> oldAttrs = artifactToCopy.getAttributes();
476 for (BlackboardAttribute oldAttr:oldAttrs) {
479 if (oldAttr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
483 BlackboardAttribute.Type newAttributeType = getNewAttributeType(oldAttr);
484 switch (oldAttr.getValueType()) {
486 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
487 oldAttr.getValueBytes()));
490 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
491 oldAttr.getValueDouble()));
494 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
495 oldAttr.getValueInt()));
499 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
500 oldAttr.getValueLong()));
503 newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
504 oldAttr.getValueString()));
507 throw new TskCoreException(
"Unexpected attribute value type found: " + oldAttr.getValueType().getLabel());
511 newArtifact.addAttributes(newAttrs);
513 oldArtifactIdToNewArtifact.put(artifactToCopy.getArtifactID(), newArtifact);
526 if (oldArtTypeIdToNewArtTypeId.containsKey(oldArtifact.getArtifactTypeID())) {
527 return oldArtTypeIdToNewArtTypeId.get(oldArtifact.getArtifactTypeID());
530 BlackboardArtifact.Type oldCustomType = currentCase.
getSleuthkitCase().getArtifactType(oldArtifact.getArtifactTypeName());
532 BlackboardArtifact.Type newCustomType = portableSkCase.addBlackboardArtifactType(oldCustomType.getTypeName(), oldCustomType.getDisplayName());
533 oldArtTypeIdToNewArtTypeId.put(oldArtifact.getArtifactTypeID(), newCustomType.getTypeID());
534 return newCustomType.getTypeID();
535 }
catch (TskDataException ex) {
536 throw new TskCoreException(
"Error creating new artifact type " + oldCustomType.getTypeName(), ex);
548 private BlackboardAttribute.Type
getNewAttributeType(BlackboardAttribute oldAttribute)
throws TskCoreException {
549 BlackboardAttribute.Type oldAttrType = oldAttribute.getAttributeType();
550 if (oldAttrTypeIdToNewAttrType.containsKey(oldAttrType.getTypeID())) {
551 return oldAttrTypeIdToNewAttrType.get(oldAttrType.getTypeID());
555 BlackboardAttribute.Type newCustomType = portableSkCase.addArtifactAttributeType(oldAttrType.getTypeName(),
556 oldAttrType.getValueType(), oldAttrType.getDisplayName());
557 oldAttrTypeIdToNewAttrType.put(oldAttribute.getAttributeType().getTypeID(), newCustomType);
558 return newCustomType;
559 }
catch (TskDataException ex) {
560 throw new TskCoreException(
"Error creating new attribute type " + oldAttrType.getTypeName(), ex);
576 "CreatePortableCaseModule.copyContentToPortableCase.copyingFile=Copying file {0}",
579 progressPanel.
updateStatusLabel(Bundle.CreatePortableCaseModule_copyContentToPortableCase_copyingFile(content.getUniquePath()));
582 CaseDbTransaction trans = portableSkCase.beginTransaction();
584 newFileId = copyContent(content, trans);
587 }
catch (TskCoreException ex) {
603 private long copyContent(Content content, CaseDbTransaction trans)
throws TskCoreException {
606 if (oldIdToNewContent.containsKey(content.getId())) {
607 return oldIdToNewContent.get(content.getId()).getId();
614 if (content.getParent() != null) {
615 parentId = copyContent(content.getParent(), trans);
619 if (content instanceof Image) {
620 Image image = (Image)content;
621 newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(),
622 new ArrayList<>(), image.getTimeZone(), image.getMd5(), image.getSha1(), image.getSha256(), image.getDeviceId(), trans);
623 }
else if (content instanceof VolumeSystem) {
624 VolumeSystem vs = (VolumeSystem)content;
625 newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
626 }
else if (content instanceof Volume) {
627 Volume vs = (Volume)content;
628 newContent = portableSkCase.addVolume(parentId, vs.getAddr(), vs.getStart(), vs.getLength(),
629 vs.getDescription(), vs.getFlags(), trans);
630 }
else if (content instanceof FileSystem) {
631 FileSystem fs = (FileSystem)content;
632 newContent = portableSkCase.addFileSystem(parentId, fs.getImageOffset(), fs.getFsType(), fs.getBlock_size(),
633 fs.getBlock_count(), fs.getRoot_inum(), fs.getFirst_inum(), fs.getLastInum(),
634 fs.getName(), trans);
635 }
else if (content instanceof AbstractFile) {
636 AbstractFile abstractFile = (AbstractFile)content;
638 if (abstractFile instanceof LocalFilesDataSource) {
639 LocalFilesDataSource localFilesDS = (LocalFilesDataSource)abstractFile;
640 newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), trans);
642 if (abstractFile.isDir()) {
643 newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
648 String exportSubFolder = getExportSubfolder(abstractFile);
649 File exportFolder = Paths.get(copiedFilesFolder.toString(), exportSubFolder).toFile();
650 File localFile =
new File(exportFolder, fileName);
654 Content oldParent = abstractFile.getParent();
655 if (! oldIdToNewContent.containsKey(oldParent.getId())) {
656 throw new TskCoreException(
"Parent of file with ID " + abstractFile.getId() +
" has not been created");
658 Content newParent = oldIdToNewContent.get(oldParent.getId());
661 String relativePath = FILE_FOLDER_NAME + File.separator + exportSubFolder + File.separator + fileName;
663 newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
664 abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getMtime(),
665 abstractFile.getMd5Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
666 true, TskData.EncodingType.NONE,
668 }
catch (IOException ex) {
669 throw new TskCoreException(
"Error copying file " + abstractFile.getName() +
" with original obj ID "
670 + abstractFile.getId(), ex);
675 throw new TskCoreException(
"Trying to copy unexpected Content type " + content.getClass().getName());
679 oldIdToNewContent.put(content.getId(), newContent);
680 newIdToContent.put(newContent.getId(), newContent);
681 return oldIdToNewContent.get(content.getId()).getId();
692 if (abstractFile.getMIMEType() == null || abstractFile.getMIMEType().isEmpty()) {
693 return UNKNOWN_FILE_TYPE_FOLDER;
697 if (cat.getMediaTypes().contains(abstractFile.getMIMEType())) {
698 return cat.getDisplayName();
701 return UNKNOWN_FILE_TYPE_FOLDER;
708 oldIdToNewContent.clear();
709 newIdToContent.clear();
710 oldTagNameToNewTagName.clear();
711 oldArtTypeIdToNewArtTypeId.clear();
712 oldAttrTypeIdToNewAttrType.clear();
713 oldArtifactIdToNewArtifact.clear();
716 if (portableSkCase != null) {
717 portableSkCase.
close();
718 portableSkCase = null;
721 copiedFilesFolder = null;
726 configPanel =
new CreatePortableCasePanel();
735 this.tableName = tableName;
744 Long maxId = rs.getLong(
"max_id");
745 String query =
" (table_name, max_id) VALUES ('" + tableName +
"', '" + maxId +
"')";
746 portableSkCase.getCaseDbAccessManager().insert(MAX_ID_TABLE_NAME, query);
748 }
catch (SQLException ex) {
749 logger.log(Level.WARNING,
"Unable to get maximum ID from result set", ex);
750 }
catch (TskCoreException ex) {
751 logger.log(Level.WARNING,
"Unable to save maximum ID from result set", ex);
755 }
catch (SQLException ex) {
756 logger.log(Level.WARNING,
"Failed to get maximum ID from result set", ex);
int getNewArtifactTypeId(BlackboardArtifact oldArtifact)
BlackboardArtifact copyArtifact(long newContentId, BlackboardArtifact artifactToCopy)
void process(ResultSet rs)
void addFilesToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel)
void complete(ReportStatus reportStatus)
CreatePortableCasePanel configPanel
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
void handleError(String logWarning, String dialogWarning, Exception ex, ReportProgressPanel progressPanel)
CreatePortableCaseModule()
void setIndeterminate(boolean indeterminate)
TagsManager getTagsManager()
void generateReport(String reportPath, ReportProgressPanel progressPanel)
void createCase(File outputDir, ReportProgressPanel progressPanel)
String getRelativeFilePath()
SleuthkitCase getSleuthkitCase()
String getExportSubfolder(AbstractFile abstractFile)
SleuthkitCase createPortableCase(String caseName, File portableCaseFolder)
static String escapeFileName(String fileName)
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
void updateStatusLabel(String statusMessage)
long copyContentToPortableCase(Content content, ReportProgressPanel progressPanel)
JPanel getConfigurationPanel()
long copyContent(Content content, CaseDbTransaction trans)
BlackboardAttribute.Type getNewAttributeType(BlackboardAttribute oldAttribute)
static void error(String message)
void addArtifactsToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel)