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;
 
   58 import org.
sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
 
   69 class PortableCaseReportModule 
implements ReportModule {
 
   70     private static final Logger logger = Logger.getLogger(PortableCaseReportModule.class.getName());
 
   71     private static final String FILE_FOLDER_NAME = 
"PortableCaseFiles";  
 
   72     private static final String UNKNOWN_FILE_TYPE_FOLDER = 
"Other";  
 
   73     private static final String MAX_ID_TABLE_NAME = 
"portable_case_max_ids";  
 
   74     private PortableCaseOptions options;
 
   77     private static final List<FileTypeCategory> FILE_TYPE_CATEGORIES = Arrays.asList(FileTypeCategory.AUDIO, FileTypeCategory.DOCUMENTS,
 
   78             FileTypeCategory.EXECUTABLE, FileTypeCategory.IMAGE, FileTypeCategory.VIDEO);
 
   80     private Case currentCase = null;
 
   81     private SleuthkitCase portableSkCase = null;
 
   82     private final String caseName;
 
   83     private File caseFolder = null;
 
   84     private File copiedFilesFolder = null;
 
   87     private final Map<Long, Content> oldIdToNewContent = 
new HashMap<>();
 
   90     private final Map<Long, Content> newIdToContent = 
new HashMap<>();
 
   93     private final Map<TagName, TagName> oldTagNameToNewTagName = 
new HashMap<>();
 
   96     private final Map<Integer, Integer> oldArtTypeIdToNewArtTypeId = 
new HashMap<>();
 
   99     private final Map<Integer, BlackboardAttribute.Type> oldAttrTypeIdToNewAttrType = 
new HashMap<>();
 
  102     private final Map<Long, BlackboardArtifact> oldArtifactIdToNewArtifact = 
new HashMap<>();
 
  104     PortableCaseReportModule() {
 
  105         caseName = Case.getCurrentCase().getDisplayName() + 
" (Portable)"; 
 
  109         "PortableCaseReportModule.getName.name=Portable Case" 
  112     public String getName() {
 
  113         return Bundle.PortableCaseReportModule_getName_name();
 
  117         "PortableCaseReportModule.getDescription.description=Copies selected items to a new single-user case that can be easily shared" 
  120     public String getDescription() {
 
  121         return Bundle.PortableCaseReportModule_getDescription_description();
 
  125     public String getRelativeFilePath() {
 
  134     private void handleCancellation(ReportProgressPanel progressPanel) {
 
  135         logger.log(Level.INFO, 
"Portable case creation canceled by user"); 
 
  136         progressPanel.setIndeterminate(
false);
 
  137         progressPanel.complete(ReportProgressPanel.ReportStatus.CANCELED);
 
  151     private void handleError(String logWarning, String dialogWarning, Exception ex, ReportProgressPanel progressPanel) {
 
  153             logger.log(Level.WARNING, logWarning);
 
  155             logger.log(Level.SEVERE, logWarning, ex);
 
  157         MessageNotifyUtil.Message.error(dialogWarning);
 
  158         progressPanel.setIndeterminate(
false);
 
  159         progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
 
  164         "PortableCaseReportModule.generateReport.verifying=Verifying selected parameters...",
 
  165         "PortableCaseReportModule.generateReport.creatingCase=Creating portable case database...",
 
  166         "PortableCaseReportModule.generateReport.copyingTags=Copying tags...",
 
  168         "PortableCaseReportModule.generateReport.copyingFiles=Copying files tagged as {0}...",
 
  170         "PortableCaseReportModule.generateReport.copyingArtifacts=Copying artifacts tagged as {0}...",
 
  171         "# {0} - output folder",
 
  172         "PortableCaseReportModule.generateReport.outputDirDoesNotExist=Output folder {0} does not exist",
 
  173         "# {0} - output folder",
 
  174         "PortableCaseReportModule.generateReport.outputDirIsNotDir=Output folder {0} is not a folder",
 
  175         "PortableCaseReportModule.generateReport.caseClosed=Current case has been closed",
 
  176         "PortableCaseReportModule.generateReport.interestingItemError=Error loading intersting items",
 
  177         "PortableCaseReportModule.generateReport.noContentToCopy=No interesting files, results, or tagged items to copy",
 
  178         "PortableCaseReportModule.generateReport.errorCopyingTags=Error copying tags",
 
  179         "PortableCaseReportModule.generateReport.errorCopyingFiles=Error copying tagged files",
 
  180         "PortableCaseReportModule.generateReport.errorCopyingArtifacts=Error copying tagged artifacts",
 
  181         "PortableCaseReportModule.generateReport.errorCopyingInterestingFiles=Error copying interesting files",
 
  182         "PortableCaseReportModule.generateReport.errorCopyingInterestingResults=Error copying interesting results",
 
  183         "PortableCaseReportModule.generateReport.errorCreatingImageTagTable=Error creating image tags table",
 
  184         "# {0} - attribute type name",
 
  185         "PortableCaseReportModule.generateReport.errorLookingUpAttrType=Error looking up attribute type {0}",
 
  186         "PortableCaseReportModule.generateReport.compressingCase=Compressing case...",
 
  187         "PortableCaseReportModule.generateReport.errorCreatingReportFolder=Could not make report folder",
 
  188         "PortableCaseReportModule.generateReport.errorGeneratingUCOreport=Problem while generating CASE-UCO report" 
  191     void generateReport(String reportPath, PortableCaseOptions options, ReportProgressPanel progressPanel) {
 
  192         this.options = options;
 
  193         progressPanel.setIndeterminate(
true);
 
  194         progressPanel.start();
 
  195         progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_verifying());
 
  201         File outputDir = 
new File(reportPath);
 
  202         if (! outputDir.exists()) {
 
  203             handleError(
"Output folder " + outputDir.toString() + 
" does not exist",
 
  204                     Bundle.PortableCaseReportModule_generateReport_outputDirDoesNotExist(outputDir.toString()), null, progressPanel); 
 
  208         if (! outputDir.isDirectory()) {
 
  209             handleError(
"Output folder " + outputDir.toString() + 
" is not a folder",
 
  210                     Bundle.PortableCaseReportModule_generateReport_outputDirIsNotDir(outputDir.toString()), null, progressPanel); 
 
  216             currentCase = Case.getCurrentCaseThrows();
 
  217         } 
catch (NoCurrentCaseException ex) {
 
  218             handleError(
"Current case has been closed",
 
  219                     Bundle.PortableCaseReportModule_generateReport_caseClosed(), null, progressPanel); 
 
  224         List<TagName> tagNames = options.getSelectedTagNames();
 
  225         List<String> setNames = options.getSelectedSetNames();
 
  226         if (tagNames.isEmpty() && setNames.isEmpty()) {  
 
  227             handleError(
"No content to copy", 
 
  228                     Bundle.PortableCaseReportModule_generateReport_noContentToCopy(), null, progressPanel); 
 
  234         progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_creatingCase());
 
  235         createCase(outputDir, progressPanel);
 
  236         if (portableSkCase == null) {
 
  242         if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
 
  243             handleCancellation(progressPanel);
 
  249             initializeImageTags(progressPanel);
 
  250         } 
catch (TskCoreException ex) {
 
  251             handleError(
"Error creating image tag table", Bundle.PortableCaseReportModule_generateReport_errorCreatingImageTagTable(), ex, progressPanel); 
 
  256         progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingTags());
 
  258             for(TagName tagName:tagNames) {
 
  259                 TagName newTagName = portableSkCase.addOrUpdateTagName(tagName.getDisplayName(), tagName.getDescription(), tagName.getColor(), tagName.getKnownStatus());
 
  260                 oldTagNameToNewTagName.put(tagName, newTagName);
 
  262         } 
catch (TskCoreException ex) {
 
  263             handleError(
"Error copying tags", Bundle.PortableCaseReportModule_generateReport_errorCopyingTags(), ex, progressPanel); 
 
  268         for (BlackboardArtifact.ARTIFACT_TYPE type:BlackboardArtifact.ARTIFACT_TYPE.values()) {
 
  269             oldArtTypeIdToNewArtTypeId.put(type.getTypeID(), type.getTypeID());
 
  271         for (BlackboardAttribute.ATTRIBUTE_TYPE type:BlackboardAttribute.ATTRIBUTE_TYPE.values()) {
 
  273                 oldAttrTypeIdToNewAttrType.put(type.getTypeID(), portableSkCase.getAttributeType(type.getLabel()));
 
  274             } 
catch (TskCoreException ex) {
 
  275                 handleError(
"Error looking up attribute name " + type.getLabel(),
 
  276                         Bundle.PortableCaseReportModule_generateReport_errorLookingUpAttrType(type.getLabel()),
 
  283             for(TagName tagName:tagNames) {
 
  285                 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
 
  286                     handleCancellation(progressPanel);
 
  289                 progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingFiles(tagName.getDisplayName()));
 
  290                 addFilesToPortableCase(tagName, progressPanel);
 
  293                 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
 
  294                     handleCancellation(progressPanel);
 
  298         } 
catch (TskCoreException ex) {
 
  299             handleError(
"Error copying tagged files", Bundle.PortableCaseReportModule_generateReport_errorCopyingFiles(), ex, progressPanel); 
 
  305             for(TagName tagName:tagNames) {
 
  307                 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
 
  308                     handleCancellation(progressPanel);
 
  311                 progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_copyingArtifacts(tagName.getDisplayName()));
 
  312                 addArtifactsToPortableCase(tagName, progressPanel);
 
  315                 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
 
  316                     handleCancellation(progressPanel);
 
  320         } 
catch (TskCoreException ex) {
 
  321             handleError(
"Error copying tagged artifacts", Bundle.PortableCaseReportModule_generateReport_errorCopyingArtifacts(), ex, progressPanel); 
 
  326         if (! setNames.isEmpty()) {
 
  328                 List<BlackboardArtifact> interestingFiles = currentCase.getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
 
  329                 for (BlackboardArtifact art:interestingFiles) {
 
  331                     if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
 
  332                         handleCancellation(progressPanel);
 
  336                     BlackboardAttribute setAttr = art.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
 
  337                     if (setNames.contains(setAttr.getValueString())) {
 
  338                         copyContentToPortableCase(art, progressPanel);
 
  341             } 
catch (TskCoreException ex) {
 
  342                 handleError(
"Error copying interesting files", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingFiles(), ex, progressPanel); 
 
  347                 List<BlackboardArtifact> interestingResults = currentCase.getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT);
 
  348                 for (BlackboardArtifact art:interestingResults) {
 
  350                     if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
 
  351                         handleCancellation(progressPanel);
 
  354                     BlackboardAttribute setAttr = art.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
 
  355                     if (setNames.contains(setAttr.getValueString())) {
 
  356                         copyContentToPortableCase(art, progressPanel);
 
  359             } 
catch (TskCoreException ex) {
 
  360                 handleError(
"Error copying interesting results", Bundle.PortableCaseReportModule_generateReport_errorCopyingInterestingResults(), ex, progressPanel); 
 
  366         if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
 
  367             handleCancellation(progressPanel);
 
  371         File reportsFolder = Paths.get(caseFolder.toString(), 
"Reports").toFile();
 
  372         if(!reportsFolder.mkdir()) {
 
  373             handleError(
"Could not make report folder", Bundle.PortableCaseReportModule_generateReport_errorCreatingReportFolder(), null, progressPanel); 
 
  378             CaseUcoFormatExporter.export(tagNames, setNames, reportsFolder, progressPanel);
 
  379         } 
catch (IOException | SQLException | NoCurrentCaseException | TskCoreException ex) {
 
  380             handleError(
"Problem while generating CASE-UCO report", 
 
  381                     Bundle.PortableCaseReportModule_generateReport_errorGeneratingUCOreport(), ex, progressPanel); 
 
  385         if (options.shouldCompress()) {
 
  386             progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_generateReport_compressingCase());
 
  388             boolean success = compressCase(progressPanel);
 
  391             if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
 
  392                 handleCancellation(progressPanel);
 
  405         progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE);
 
  417         "# {0} - case folder",
 
  418         "PortableCaseReportModule.createCase.caseDirExists=Case folder {0} already exists",
 
  419         "PortableCaseReportModule.createCase.errorCreatingCase=Error creating case",
 
  421         "PortableCaseReportModule.createCase.errorCreatingFolder=Error creating folder {0}",
 
  422         "PortableCaseReportModule.createCase.errorStoringMaxIds=Error storing maximum database IDs",
 
  424     private void createCase(File outputDir, ReportProgressPanel progressPanel) {
 
  427         caseFolder = Paths.get(outputDir.toString(), caseName).toFile();
 
  429         if (caseFolder.exists()) {
 
  430             handleError(
"Case folder " + caseFolder.toString() + 
" already exists",
 
  431                 Bundle.PortableCaseReportModule_createCase_caseDirExists(caseFolder.toString()), null, progressPanel); 
 
  437             portableSkCase = currentCase.createPortableCase(caseName, caseFolder);
 
  438         } 
catch (TskCoreException ex) {
 
  439             handleError(
"Error creating case " + caseName + 
" in folder " + caseFolder.toString(),
 
  440                 Bundle.PortableCaseReportModule_createCase_errorCreatingCase(), ex, progressPanel);   
 
  447         } 
catch (TskCoreException ex) {
 
  448             handleError(
"Error storing maximum database IDs",
 
  449                 Bundle.PortableCaseReportModule_createCase_errorStoringMaxIds(), ex, progressPanel);   
 
  454         copiedFilesFolder = Paths.get(caseFolder.toString(), FILE_FOLDER_NAME).toFile();
 
  455         if (! copiedFilesFolder.mkdir()) {
 
  456             handleError(
"Error creating folder " + copiedFilesFolder.toString(),
 
  457                     Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(copiedFilesFolder.toString()), null, progressPanel); 
 
  462         for (FileTypeCategory cat:FILE_TYPE_CATEGORIES) {
 
  463             File subFolder = Paths.get(copiedFilesFolder.toString(), cat.getDisplayName()).toFile();
 
  464             if (! subFolder.mkdir()) {
 
  465                 handleError(
"Error creating folder " + subFolder.toString(),
 
  466                     Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(subFolder.toString()), null, progressPanel);    
 
  470         File unknownTypeFolder = Paths.get(copiedFilesFolder.toString(), UNKNOWN_FILE_TYPE_FOLDER).toFile();
 
  471         if (! unknownTypeFolder.mkdir()) {
 
  472             handleError(
"Error creating folder " + unknownTypeFolder.toString(),
 
  473                 Bundle.PortableCaseReportModule_createCase_errorCreatingFolder(unknownTypeFolder.toString()), null, progressPanel);   
 
  484     private void saveHighestIds() throws TskCoreException {
 
  486         CaseDbAccessManager currentCaseDbManager = currentCase.getSleuthkitCase().getCaseDbAccessManager();
 
  488         String tableSchema = 
"( table_name TEXT PRIMARY KEY, " 
  491         portableSkCase.getCaseDbAccessManager().createTable(MAX_ID_TABLE_NAME, tableSchema);
 
  493         currentCaseDbManager.select(
"max(obj_id) as max_id from tsk_objects", 
new StoreMaxIdCallback(
"tsk_objects")); 
 
  494         currentCaseDbManager.select(
"max(tag_id) as max_id from content_tags", 
new StoreMaxIdCallback(
"content_tags")); 
 
  495         currentCaseDbManager.select(
"max(tag_id) as max_id from blackboard_artifact_tags", 
new StoreMaxIdCallback(
"blackboard_artifact_tags"));  
 
  496         currentCaseDbManager.select(
"max(examiner_id) as max_id from tsk_examiners", 
new StoreMaxIdCallback(
"tsk_examiners"));  
 
  506     private void initializeImageTags(ReportProgressPanel progressPanel) 
throws TskCoreException {
 
  509         CaseDbAccessManager portableDbAccessManager = portableSkCase.getCaseDbAccessManager();
 
  510         if (! portableDbAccessManager.tableExists(ContentViewerTagManager.TABLE_NAME)) {
 
  511             portableDbAccessManager.createTable(ContentViewerTagManager.TABLE_NAME, ContentViewerTagManager.TABLE_SCHEMA_SQLITE);
 
  523     private void addFilesToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel) 
throws TskCoreException {
 
  526         List<ContentTag> tags = currentCase.getServices().getTagsManager().getContentTagsByTagName(oldTagName);
 
  529         for (ContentTag tag : tags) {
 
  532             if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
 
  536             Content content = tag.getContent();
 
  537             if (content instanceof AbstractFile) {
 
  539                 long newFileId = copyContentToPortableCase(content, progressPanel);
 
  542                 if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
 
  543                     throw new TskCoreException(
"TagName map is missing entry for ID " + tag.getName().getId() + 
" with display name " + tag.getName().getDisplayName()); 
 
  545                 ContentTag newContentTag = portableSkCase.addContentTag(newIdToContent.get(newFileId), oldTagNameToNewTagName.get(tag.getName()), tag.getComment(), tag.getBeginByteOffset(), tag.getEndByteOffset());
 
  549                 String appData = getImageTagDataForContentTag(tag);
 
  550                 if (! appData.isEmpty()) {
 
  551                     addImageTagToPortableCase(newContentTag, appData);
 
  566     private String getImageTagDataForContentTag(ContentTag tag) 
throws TskCoreException {
 
  568         GetImageTagCallback callback = 
new GetImageTagCallback();
 
  569         String query = 
"* FROM " + ContentViewerTagManager.TABLE_NAME + 
" WHERE content_tag_id = " + tag.getId();
 
  570         currentCase.getSleuthkitCase().getCaseDbAccessManager().select(query, callback);
 
  571         return callback.getAppData();
 
  587                         appData = rs.getString(
"app_data"); 
 
  588                     } 
catch (SQLException ex) {
 
  589                         logger.log(Level.WARNING, 
"Unable to get app_data from result set", ex); 
 
  592             } 
catch (SQLException ex) {
 
  593                 logger.log(Level.WARNING, 
"Failed to get next result for app_data", ex); 
 
  602         String getAppData() {
 
  615     private void addImageTagToPortableCase(ContentTag newContentTag, String appData) 
throws TskCoreException {
 
  616         String insert = 
"(content_tag_id, app_data) VALUES (" + newContentTag.getId() + 
", '" + appData + 
"')";
 
  617         portableSkCase.getCaseDbAccessManager().insert(ContentViewerTagManager.TABLE_NAME, insert);
 
  629     private void addArtifactsToPortableCase(TagName oldTagName, ReportProgressPanel progressPanel) 
throws TskCoreException {
 
  631         List<BlackboardArtifactTag> tags = currentCase.getServices().getTagsManager().getBlackboardArtifactTagsByTagName(oldTagName);
 
  634         for (BlackboardArtifactTag tag : tags) {
 
  637             if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
 
  642             Content content = tag.getContent();
 
  643             long newContentId = copyContentToPortableCase(content, progressPanel);
 
  646             BlackboardArtifact newArtifact = copyArtifact(newContentId, tag.getArtifact());
 
  649             if (! oldTagNameToNewTagName.containsKey(tag.getName())) {
 
  650                 throw new TskCoreException(
"TagName map is missing entry for ID " + tag.getName().getId() + 
" with display name " + tag.getName().getDisplayName()); 
 
  652             portableSkCase.addBlackboardArtifactTag(newArtifact, oldTagNameToNewTagName.get(tag.getName()), tag.getComment());
 
  666     private BlackboardArtifact copyArtifact(
long newContentId, BlackboardArtifact artifactToCopy) 
throws TskCoreException {
 
  668         if (oldArtifactIdToNewArtifact.containsKey(artifactToCopy.getArtifactID())) {
 
  669             return oldArtifactIdToNewArtifact.get(artifactToCopy.getArtifactID());
 
  673         BlackboardAttribute oldAssociatedAttribute = artifactToCopy.getAttribute(
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
 
  674         List<BlackboardAttribute> newAttrs = 
new ArrayList<>();
 
  675         if (oldAssociatedAttribute != null) {
 
  676             BlackboardArtifact oldAssociatedArtifact = currentCase.getSleuthkitCase().getBlackboardArtifact(oldAssociatedAttribute.getValueLong());
 
  677             BlackboardArtifact newAssociatedArtifact = copyArtifact(newContentId, oldAssociatedArtifact);
 
  678             newAttrs.add(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, 
 
  679                         String.join(
",", oldAssociatedAttribute.getSources()), newAssociatedArtifact.getArtifactID()));
 
  683         int newArtifactTypeId = getNewArtifactTypeId(artifactToCopy);
 
  684         BlackboardArtifact newArtifact = portableSkCase.newBlackboardArtifact(newArtifactTypeId, newContentId);
 
  685         List<BlackboardAttribute> oldAttrs = artifactToCopy.getAttributes();
 
  688         for (BlackboardAttribute oldAttr:oldAttrs) {
 
  691             if (oldAttr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
 
  695             BlackboardAttribute.Type newAttributeType = getNewAttributeType(oldAttr);
 
  696             switch (oldAttr.getValueType()) {
 
  698                     newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
 
  699                             oldAttr.getValueBytes()));
 
  702                     newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
 
  703                             oldAttr.getValueDouble()));
 
  706                     newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
 
  707                             oldAttr.getValueInt()));
 
  711                     newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
 
  712                             oldAttr.getValueLong()));
 
  715                     newAttrs.add(
new BlackboardAttribute(newAttributeType, String.join(
",", oldAttr.getSources()),
 
  716                             oldAttr.getValueString()));
 
  719                     throw new TskCoreException(
"Unexpected attribute value type found: " + oldAttr.getValueType().getLabel()); 
 
  723         newArtifact.addAttributes(newAttrs);
 
  725         oldArtifactIdToNewArtifact.put(artifactToCopy.getArtifactID(), newArtifact);
 
  737     private int getNewArtifactTypeId(BlackboardArtifact oldArtifact) 
throws TskCoreException {
 
  738         if (oldArtTypeIdToNewArtTypeId.containsKey(oldArtifact.getArtifactTypeID())) {
 
  739             return oldArtTypeIdToNewArtTypeId.get(oldArtifact.getArtifactTypeID());
 
  742         BlackboardArtifact.Type oldCustomType = currentCase.getSleuthkitCase().getArtifactType(oldArtifact.getArtifactTypeName());
 
  744             BlackboardArtifact.Type newCustomType = portableSkCase.addBlackboardArtifactType(oldCustomType.getTypeName(), oldCustomType.getDisplayName());
 
  745             oldArtTypeIdToNewArtTypeId.put(oldArtifact.getArtifactTypeID(), newCustomType.getTypeID());
 
  746             return newCustomType.getTypeID();
 
  747         } 
catch (TskDataException ex) {
 
  748             throw new TskCoreException(
"Error creating new artifact type " + oldCustomType.getTypeName(), ex); 
 
  760     private BlackboardAttribute.Type getNewAttributeType(BlackboardAttribute oldAttribute) 
throws TskCoreException {
 
  761         BlackboardAttribute.Type oldAttrType = oldAttribute.getAttributeType();
 
  762         if (oldAttrTypeIdToNewAttrType.containsKey(oldAttrType.getTypeID())) {
 
  763             return oldAttrTypeIdToNewAttrType.get(oldAttrType.getTypeID());
 
  767             BlackboardAttribute.Type newCustomType = portableSkCase.addArtifactAttributeType(oldAttrType.getTypeName(), 
 
  768                     oldAttrType.getValueType(), oldAttrType.getDisplayName());
 
  769             oldAttrTypeIdToNewAttrType.put(oldAttribute.getAttributeType().getTypeID(), newCustomType);
 
  770             return newCustomType;
 
  771         } 
catch (TskDataException ex) {
 
  772             throw new TskCoreException(
"Error creating new attribute type " + oldAttrType.getTypeName(), ex); 
 
  788         "PortableCaseReportModule.copyContentToPortableCase.copyingFile=Copying file {0}",  
 
  790     private long copyContentToPortableCase(Content content, ReportProgressPanel progressPanel) 
throws TskCoreException {
 
  791         progressPanel.updateStatusLabel(Bundle.PortableCaseReportModule_copyContentToPortableCase_copyingFile(content.getUniquePath()));
 
  792         return copyContent(content);
 
  804     private long copyContent(Content content) 
throws TskCoreException {
 
  807         if (oldIdToNewContent.containsKey(content.getId())) {
 
  808             return oldIdToNewContent.get(content.getId()).getId();
 
  815         if (content.getParent() != null) {
 
  816             parentId = copyContent(content.getParent());
 
  820         if (content instanceof BlackboardArtifact) {
 
  821             BlackboardArtifact artifactToCopy = (BlackboardArtifact)content;
 
  822             newContent = copyArtifact(parentId, artifactToCopy);
 
  824             CaseDbTransaction trans = portableSkCase.beginTransaction();
 
  826                 if (content instanceof Image) {
 
  827                     Image image = (Image)content;
 
  828                     newContent = portableSkCase.addImage(image.getType(), image.getSsize(), image.getSize(), image.getName(), 
 
  829                             new ArrayList<>(), image.getTimeZone(), image.getMd5(), image.getSha1(), image.getSha256(), image.getDeviceId(), trans);
 
  830                 } 
else if (content instanceof VolumeSystem) {
 
  831                     VolumeSystem vs = (VolumeSystem)content;
 
  832                     newContent = portableSkCase.addVolumeSystem(parentId, vs.getType(), vs.getOffset(), vs.getBlockSize(), trans);
 
  833                 } 
else if (content instanceof Volume) {
 
  834                     Volume vs = (Volume)content;
 
  835                     newContent = portableSkCase.addVolume(parentId, vs.getAddr(), vs.getStart(), vs.getLength(), 
 
  836                             vs.getDescription(), vs.getFlags(), trans);
 
  837                 } 
else if (content instanceof FileSystem) {
 
  838                     FileSystem fs = (FileSystem)content;
 
  839                     newContent = portableSkCase.addFileSystem(parentId, fs.getImageOffset(), fs.getFsType(), fs.getBlock_size(), 
 
  840                             fs.getBlock_count(), fs.getRoot_inum(), fs.getFirst_inum(), fs.getLastInum(), 
 
  841                             fs.getName(), trans);
 
  842                 } 
else if (content instanceof BlackboardArtifact) {
 
  843                     BlackboardArtifact artifactToCopy = (BlackboardArtifact)content;
 
  844                     newContent = copyArtifact(parentId, artifactToCopy);
 
  845                 } 
else if (content instanceof AbstractFile) {
 
  846                     AbstractFile abstractFile = (AbstractFile)content;
 
  848                     if (abstractFile instanceof LocalFilesDataSource) {
 
  849                         LocalFilesDataSource localFilesDS = (LocalFilesDataSource)abstractFile;
 
  850                         newContent = portableSkCase.addLocalFilesDataSource(localFilesDS.getDeviceId(), localFilesDS.getName(), localFilesDS.getTimeZone(), trans);    
 
  852                         if (abstractFile.isDir()) {
 
  853                             newContent = portableSkCase.addLocalDirectory(parentId, abstractFile.getName(), trans);
 
  857                                 String fileName = abstractFile.getId() + 
"-" + FileUtil.escapeFileName(abstractFile.getName());
 
  858                                 String exportSubFolder = getExportSubfolder(abstractFile);
 
  859                                 File exportFolder = Paths.get(copiedFilesFolder.toString(), exportSubFolder).toFile();
 
  860                                 File localFile = 
new File(exportFolder, fileName);
 
  861                                 ContentUtils.writeToFile(abstractFile, localFile);
 
  864                                 Content oldParent = abstractFile.getParent();
 
  865                                 if (! oldIdToNewContent.containsKey(oldParent.getId())) {
 
  866                                     throw new TskCoreException(
"Parent of file with ID " + abstractFile.getId() + 
" has not been created"); 
 
  868                                 Content newParent = oldIdToNewContent.get(oldParent.getId());
 
  871                                 String relativePath = FILE_FOLDER_NAME + File.separator +  exportSubFolder + File.separator + fileName;
 
  873                                 newContent = portableSkCase.addLocalFile(abstractFile.getName(), relativePath, abstractFile.getSize(),
 
  874                                         abstractFile.getCtime(), abstractFile.getCrtime(), abstractFile.getAtime(), abstractFile.getMtime(),
 
  875                                         abstractFile.getMd5Hash(), abstractFile.getKnown(), abstractFile.getMIMEType(),
 
  876                                         true, TskData.EncodingType.NONE, 
 
  878                             } 
catch (IOException ex) {
 
  879                                 throw new TskCoreException(
"Error copying file " + abstractFile.getName() + 
" with original obj ID "  
  880                                         + abstractFile.getId(), ex); 
 
  885                     throw new TskCoreException(
"Trying to copy unexpected Content type " + content.getClass().getName()); 
 
  888             }  
catch (TskCoreException ex) {
 
  895         oldIdToNewContent.put(content.getId(), newContent);
 
  896         newIdToContent.put(newContent.getId(), newContent);
 
  897         return oldIdToNewContent.get(content.getId()).getId();
 
  907     private String getExportSubfolder(AbstractFile abstractFile) {
 
  908         if (abstractFile.getMIMEType() == null || abstractFile.getMIMEType().isEmpty()) {
 
  909             return UNKNOWN_FILE_TYPE_FOLDER;
 
  912         for (FileTypeCategory cat:FILE_TYPE_CATEGORIES) {
 
  913             if (cat.getMediaTypes().contains(abstractFile.getMIMEType())) {
 
  914                 return cat.getDisplayName();
 
  917         return UNKNOWN_FILE_TYPE_FOLDER;
 
  923     private void cleanup() {
 
  924         oldIdToNewContent.clear();
 
  925         newIdToContent.clear();
 
  926         oldTagNameToNewTagName.clear();
 
  927         oldArtTypeIdToNewArtTypeId.clear();
 
  928         oldAttrTypeIdToNewAttrType.clear();
 
  929         oldArtifactIdToNewArtifact.clear();
 
  931         closePortableCaseDatabase();
 
  935         copiedFilesFolder = null;
 
  941     private void closePortableCaseDatabase() {
 
  942         if (portableSkCase != null) {
 
  943             portableSkCase.close();
 
  944             portableSkCase = null;
 
  968                         Long maxId = rs.getLong(
"max_id"); 
 
  969                         String query = 
" (table_name, max_id) VALUES ('" + tableName + 
"', '" + maxId + 
"')"; 
 
  970                         portableSkCase.getCaseDbAccessManager().insert(MAX_ID_TABLE_NAME, query);
 
  972                     } 
catch (SQLException ex) {
 
  973                         logger.log(Level.WARNING, 
"Unable to get maximum ID from result set", ex); 
 
  974                     } 
catch (TskCoreException ex) {
 
  975                         logger.log(Level.WARNING, 
"Unable to save maximum ID from result set", ex); 
 
  979             } 
catch (SQLException ex) {
 
  980                 logger.log(Level.WARNING, 
"Failed to get maximum ID from result set", ex); 
 
  986         "PortableCaseReportModule.compressCase.errorFinding7zip=Could not locate 7-Zip executable",
 
  987         "# {0} - Temp folder path",
 
  988         "PortableCaseReportModule.compressCase.errorCreatingTempFolder=Could not create temporary folder {0}",
 
  989         "PortableCaseReportModule.compressCase.errorCompressingCase=Error compressing case",
 
  990         "PortableCaseReportModule.compressCase.canceled=Compression canceled by user",
 
  992     private boolean compressCase(ReportProgressPanel progressPanel) {
 
  995         closePortableCaseDatabase();
 
  998         File tempZipFolder = Paths.get(currentCase.getTempDirectory(), 
"portableCase" + System.currentTimeMillis()).toFile(); 
 
  999         if (! tempZipFolder.mkdir()) {
 
 1000             handleError(
"Error creating temporary folder " + tempZipFolder.toString(), 
 
 1001                     Bundle.PortableCaseReportModule_compressCase_errorCreatingTempFolder(tempZipFolder.toString()), null, progressPanel); 
 
 1006         File sevenZipExe = locate7ZipExecutable();
 
 1007         if (sevenZipExe == null) {
 
 1008             handleError(
"Error finding 7-Zip exectuable", Bundle.PortableCaseReportModule_compressCase_errorFinding7zip(), null, progressPanel); 
 
 1013         String chunkOption = 
"";
 
 1014         if (options.getChunkSize() != ChunkSize.NONE) {
 
 1015             chunkOption = 
"-v" + options.getChunkSize().getSevenZipParam();
 
 1018         File zipFile = Paths.get(tempZipFolder.getAbsolutePath(), caseName + 
".zip").toFile(); 
 
 1019         ProcessBuilder procBuilder = 
new ProcessBuilder();
 
 1020         procBuilder.command(
 
 1021                 sevenZipExe.getAbsolutePath(),
 
 1023                 zipFile.getAbsolutePath(),
 
 1024                 caseFolder.getAbsolutePath(),
 
 1029             Process process = procBuilder.start();
 
 1031             while (process.isAlive()) {
 
 1032                 if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
 
 1038             int exitCode = process.exitValue();
 
 1039             if (exitCode != 0) {
 
 1041                 StringBuilder sb = 
new StringBuilder();
 
 1042                 try (BufferedReader br = 
new BufferedReader(
new InputStreamReader(process.getErrorStream()))) {
 
 1044                     while ((line = br.readLine()) != null) {
 
 1045                         sb.append(line).append(System.getProperty(
"line.separator")); 
 
 1049                 handleError(
"Error compressing case\n7-Zip output: " + sb.toString(), Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), null, progressPanel); 
 
 1052         } 
catch (IOException | InterruptedException ex) {
 
 1053             handleError(
"Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel); 
 
 1059             FileUtils.cleanDirectory(caseFolder);
 
 1060             FileUtils.copyDirectory(tempZipFolder, caseFolder);
 
 1061             FileUtils.deleteDirectory(tempZipFolder);
 
 1062         } 
catch (IOException ex) {
 
 1063             handleError(
"Error compressing case", Bundle.PortableCaseReportModule_compressCase_errorCompressingCase(), ex, progressPanel); 
 
 1075     private static File locate7ZipExecutable() {
 
 1076         if (!PlatformUtil.isWindowsOS()) {
 
 1080         String executableToFindName = Paths.get(
"7-Zip", 
"7z.exe").toString(); 
 
 1081         File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, PortableCaseReportModule.class.getPackage().getName(), 
false);
 
 1082         if (null == exeFile) {
 
 1086         if (!exeFile.canExecute()) {
 
 1099         NONE(
"Do not split", 
""), 
 
 1100         ONE_HUNDRED_MB(
"Split into 100 MB chunks", 
"100m"),
 
 1101         CD(
"Split into 700 MB chunks (CD)", 
"700m"),
 
 1102         ONE_GB(
"Split into 1 GB chunks", 
"1000m"),
 
 1103         DVD(
"Split into 4.5 GB chunks (DVD)", 
"4500m"); 
 
 1105         private final String displayName;
 
 1106         private final String sevenZipParam;
 
 1114         private ChunkSize(String displayName, String sevenZipParam) {
 
 1115             this.displayName = displayName;
 
 1116             this.sevenZipParam = sevenZipParam;
 
 1119         String getDisplayName() {
 
 1123         String getSevenZipParam() {
 
 1124             return sevenZipParam;
 
 1128         public String toString() {
 
 1136     static class PortableCaseOptions {
 
 1138         private final List<TagName> tagNames = 
new ArrayList<>();
 
 1139         private final List<String> setNames = 
new ArrayList<>();
 
 1140         private boolean compress;
 
 1141         private ChunkSize chunkSize;
 
 1143         PortableCaseOptions(List<String> setNames, List<TagName> tagNames,
 
 1144                 boolean compress, ChunkSize chunkSize) {
 
 1145             this.setNames.addAll(setNames);
 
 1146             this.tagNames.addAll(tagNames);
 
 1147             this.compress = compress;
 
 1148             this.chunkSize = chunkSize;
 
 1151         PortableCaseOptions() {
 
 1152             this.compress = 
false;
 
 1153             this.chunkSize = ChunkSize.NONE;
 
 1156         void updateSetNames(List<String> setNames) {
 
 1157             this.setNames.clear();
 
 1158             this.setNames.addAll(setNames);
 
 1161         void updateTagNames(List<TagName> tagNames) {
 
 1162             this.tagNames.clear();
 
 1163             this.tagNames.addAll(tagNames);
 
 1166         void updateCompression(
boolean compress, ChunkSize chunkSize) {
 
 1167             this.compress = compress;
 
 1168             this.chunkSize = chunkSize;
 
 1172             return (( !setNames.isEmpty()) || ( ! tagNames.isEmpty()));
 
 1175         List<String> getSelectedSetNames() {
 
 1176             return new ArrayList<>(setNames);
 
 1179         List<TagName> getSelectedTagNames() {
 
 1180             return new ArrayList<>(tagNames);
 
 1183         boolean shouldCompress() {
 
 1187         ChunkSize getChunkSize() {
 
void process(ResultSet rs)
 
void process(ResultSet rs)
 
synchronized static Logger getLogger(String name)