diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form index 1f9dfc4ceb..eb04956a1d 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.form @@ -1,6 +1,11 @@
+ + + + + @@ -32,7 +37,7 @@ - + @@ -41,7 +46,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java index ae314da16d..18cc71a752 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java @@ -79,7 +79,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { commentsOccurencesColumnsCheckbox.setEnabled(EamDbUtil.useCentralRepo()); commentsOccurencesColumnWrapAroundText.setEnabled(EamDbUtil.useCentralRepo()); commentsOccurencesColumnsCheckbox.setSelected(UserPreferences.hideCentralRepoCommentsAndOccurrences()); - + + hideOtherUsersTagsCheckbox.setSelected(UserPreferences.showOnlyCurrentUserTags()); deletedFilesLimitCheckbox.setSelected(DeletedFilePreferences.getDefault().getShouldLimitDeletedFiles()); translateNamesInTableRadioButton.setSelected(UserPreferences.displayTranslatedFileNames()); @@ -91,8 +92,11 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { currentCaseSettingsPanel.setEnabled(caseIsOpen); groupByDataSourceCheckbox.setEnabled(caseIsOpen); - hideOtherUsersTagsCheckbox.setSelected(UserPreferences.showOnlyCurrentUserTags()); - groupByDataSourceCheckbox.setSelected(Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)); + if (caseIsOpen) { + groupByDataSourceCheckbox.setSelected(Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)); + } else { + groupByDataSourceCheckbox.setSelected(false); + } // Current Session Settings hideRejectedResultsCheckbox.setSelected(DirectoryTreeTopComponent.getDefault().getShowRejectedResults() == false); @@ -174,10 +178,12 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { currentSessionSettingsPanel = new javax.swing.JPanel(); hideRejectedResultsCheckbox = new javax.swing.JCheckBox(); - viewPreferencesScrollPane.setBorder(null); - viewPreferencesScrollPane.setPreferredSize(new java.awt.Dimension(625, 452)); + setPreferredSize(new java.awt.Dimension(625, 465)); - viewPreferencesPanel.setPreferredSize(new java.awt.Dimension(625, 452)); + viewPreferencesScrollPane.setBorder(null); + viewPreferencesScrollPane.setPreferredSize(new java.awt.Dimension(625, 465)); + + viewPreferencesPanel.setPreferredSize(new java.awt.Dimension(625, 465)); globalSettingsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.globalSettingsPanel.border.title"))); // NOI18N diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index e5016b89ec..162b3b978c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -214,10 +214,6 @@ public abstract class AbstractAbstractFileNode extends A CorrelationAttributeInstance attribute = getCorrelationAttributeInstance(); updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(),NO_DESCR,getCommentProperty(tags, attribute))); } - /* - * Data that was being computed in the background task. Kicked off by a - * call to createSheet(). - */ } else if (eventType.equals(NodeSpecificEvents.TRANSLATION_AVAILABLE.toString())) { updateSheet(new NodeProperty<>(TRANSLATION.toString(),TRANSLATION.toString(),NO_DESCR,evt.getNewValue())); } @@ -304,8 +300,16 @@ public abstract class AbstractAbstractFileNode extends A "AbstractAbstractFileNode.sizeColLbl=Size", "AbstractAbstractFileNode.flagsDirColLbl=Flags(Dir)", "AbstractAbstractFileNode.flagsMetaColLbl=Flags(Meta)", + "AbstractAbstractFileNode.modeColLbl=Mode", + "AbstractAbstractFileNode.useridColLbl=UserID", + "AbstractAbstractFileNode.groupidColLbl=GroupID", + "AbstractAbstractFileNode.metaAddrColLbl=Meta Addr.", + "AbstractAbstractFileNode.attrAddrColLbl=Attr. Addr.", + "AbstractAbstractFileNode.typeDirColLbl=Type(Dir)", + "AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)", "AbstractAbstractFileNode.knownColLbl=Known", "AbstractAbstractFileNode.md5HashColLbl=MD5 Hash", + "AbstractAbstractFileNode.objectId=Object ID", "AbstractAbstractFileNode.mimeType=MIME Type", "AbstractAbstractFileNode.extensionColLbl=Extension"}) public enum AbstractFilePropertyType { @@ -323,8 +327,16 @@ public abstract class AbstractAbstractFileNode extends A SIZE(AbstractAbstractFileNode_sizeColLbl()), FLAGS_DIR(AbstractAbstractFileNode_flagsDirColLbl()), FLAGS_META(AbstractAbstractFileNode_flagsMetaColLbl()), + MODE(AbstractAbstractFileNode_modeColLbl()), + USER_ID(AbstractAbstractFileNode_useridColLbl()), + GROUP_ID(AbstractAbstractFileNode_groupidColLbl()), + META_ADDR(AbstractAbstractFileNode_metaAddrColLbl()), + ATTR_ADDR(AbstractAbstractFileNode_attrAddrColLbl()), + TYPE_DIR(AbstractAbstractFileNode_typeDirColLbl()), + TYPE_META(AbstractAbstractFileNode_typeMetaColLbl()), KNOWN(AbstractAbstractFileNode_knownColLbl()), MD5HASH(AbstractAbstractFileNode_md5HashColLbl()), + ObjectID(AbstractAbstractFileNode_objectId()), MIMETYPE(AbstractAbstractFileNode_mimeType()), EXTENSION(AbstractAbstractFileNode_extensionColLbl()); @@ -345,7 +357,7 @@ public abstract class AbstractAbstractFileNode extends A */ private List> getProperties() { List> properties = new ArrayList<>(); - properties.add(new NodeProperty<>(NAME.toString(), NAME.toString(), NO_DESCR, getContentDisplayName(content))); + properties.add(new NodeProperty<>(NAME.toString(), NAME.toString(), NO_DESCR, getContentDisplayName(content))); /* * Initialize an empty place holder value. At the bottom, we kick off a * background task that promises to update these values. diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties index 34a1274c32..2f91dfb27a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties @@ -6,6 +6,9 @@ AbstractAbstractFileNode.changeTimeColLbl=\u5909\u66f4\u65e5\u6642 AbstractAbstractFileNode.accessTimeColLbl=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 AbstractAbstractFileNode.createdTimeColLbl=\u4f5c\u6210\u65e5\u6642 AbstractAbstractFileNode.sizeColLbl=\u30b5\u30a4\u30ba +AbstractAbstractFileNode.modeColLbl=\u30e2\u30fc\u30c9 +AbstractAbstractFileNode.useridColLbl=\u30e6\u30fc\u30b6ID +AbstractAbstractFileNode.groupidColLbl=\u30b0\u30eb\u30fc\u30d7ID AbstractAbstractFileNode.knownColLbl=\u65e2\u77e5 AbstractAbstractFileNode.md5HashColLbl=MD5\u30cf\u30c3\u30b7\u30e5 AbstractContentChildren.CreateTSKNodeVisitor.exception.noNodeMsg=\u6307\u5b9a\u3055\u308c\u305fSleuthkitItem\u306e\u30ce\u30fc\u30c9\u304c\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u307e\u305b\u3093 @@ -195,6 +198,10 @@ TagsNode.displayName.text=\u30bf\u30b0 TagsNode.createSheet.name.name=\u540d\u524d AbstractAbstractFileNode.flagsDirColLbl=\u30d5\u30e9\u30b0\uff08\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff09 AbstractAbstractFileNode.flagsMetaColLbl=\u30d5\u30e9\u30b0\uff08\u30e1\u30bf\u30c7\u30fc\u30bf\uff09 +AbstractAbstractFileNode.metaAddrColLbl=\u30e1\u30bf\u30c7\u30fc\u30bf\u30a2\u30c9\u30ec\u30b9 +AbstractAbstractFileNode.attrAddrColLbl=\u5c5e\u6027\u30a2\u30c9\u30ec\u30b9 +AbstractAbstractFileNode.typeDirColLbl=\u30bf\u30a4\u30d7\uff08\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff09 +AbstractAbstractFileNode.typeMetaColLbl=\u30bf\u30a4\u30d7\uff08\u30e1\u30bf\u30c7\u30fc\u30bf\uff09 ArtifactTypeNode.createSheet.childCnt.displayName=\u30c1\u30e3\u30a4\u30eb\u30c9\u6570 TagsNode.createSheet.name.displayName=\u540d\u524d ViewsNode.name.text=\u30d3\u30e5\u30fc @@ -231,6 +238,7 @@ KeywordHits.createSheet.numChildren.name=\u30c1\u30e3\u30a4\u30eb\u30c9\u6570 KeywordHits.createSheet.numChildren.displayName=\u30c1\u30e3\u30a4\u30eb\u30c9\u6570 KeywordHits.simpleLiteralSearch.text=\u30b7\u30f3\u30b0\u30eb\u30ea\u30c6\u30e9\u30eb\u691c\u7d22 KeywordHits.singleRegexSearch.text=\u30b7\u30f3\u30b0\u30eb\u6b63\u898f\u8868\u73fe\u691c\u7d22 +AbstractAbstractFileNode.objectId=\u30aa\u30d6\u30b8\u30a7\u30af\u30c8ID ArtifactStringContent.getStr.artifactId.text=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8ID OpenReportAction.actionDisplayName=\u30ec\u30dd\u30fc\u30c8\u3092\u958b\u304f OpenReportAction.actionPerformed.MessageBoxTitle=\u5931\u6557\u30ec\u30dd\u30fc\u30c8\u3092\u958b\u304f diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index 862713a807..a0ecbee39f 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -51,6 +51,7 @@ import javax.annotation.Nonnull; import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; import org.netbeans.api.progress.ProgressHandle; import org.openide.util.Cancellable; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case.CaseType; @@ -99,7 +100,7 @@ public final class ImageGalleryController { private final SimpleBooleanProperty listeningEnabled = new SimpleBooleanProperty(false); @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - private final ReadOnlyBooleanWrapper stale = new ReadOnlyBooleanWrapper(false); + private final ReadOnlyBooleanWrapper isCaseStale = new ReadOnlyBooleanWrapper(false); private final ReadOnlyBooleanWrapper metaDataCollapsed = new ReadOnlyBooleanWrapper(false); private final SimpleDoubleProperty thumbnailSizeProp = new SimpleDoubleProperty(100); @@ -160,26 +161,34 @@ public final class ImageGalleryController { } } - boolean isListeningEnabled() { + public boolean isListeningEnabled() { synchronized (listeningEnabled) { return listeningEnabled.get(); } } + /** + * + * @param b True if any data source in the case is stale + */ @ThreadConfined(type = ThreadConfined.ThreadType.ANY) - void setStale(Boolean b) { + void setCaseStale(Boolean b) { Platform.runLater(() -> { - stale.set(b); + isCaseStale.set(b); }); } public ReadOnlyBooleanProperty staleProperty() { - return stale.getReadOnlyProperty(); + return isCaseStale.getReadOnlyProperty(); } + /** + * + * @return true if any data source in the case is stale + */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - boolean isStale() { - return stale.get(); + boolean isCaseStale() { + return isCaseStale.get(); } ImageGalleryController(@Nonnull Case newCase) throws TskCoreException { @@ -197,7 +206,7 @@ public final class ImageGalleryController { tagsManager.registerListener(categoryManager); hashSetManager = new HashSetManager(drawableDB); - setStale(isDataSourcesTableStale()); + setCaseStale(isDataSourcesTableStale()); dbExecutor = getNewDBExecutor(); @@ -344,7 +353,7 @@ public final class ImageGalleryController { * Returns a set of data source object ids that are stale. * * This includes any data sources already in the table, that are not in - * COMPLETE status, or any data sources that might have been added to the + * COMPLETE or IN_PROGRESS status, or any data sources that might have been added to the * case, but are not in the datasources table. * * @return list of data source object ids that are stale. @@ -368,9 +377,27 @@ public final class ImageGalleryController { // collect all data sources already in the table, that are not yet COMPLETE knownDataSourceIds.entrySet().stream().forEach((Map.Entry t) -> { DrawableDbBuildStatusEnum status = t.getValue(); - if (DrawableDbBuildStatusEnum.COMPLETE != status) { - staleDataSourceIds.add(t.getKey()); + switch (status) { + case COMPLETE: + case IN_PROGRESS: + // not stale + break; + case REBUILT_STALE: + staleDataSourceIds.add(t.getKey()); + break; + case UNKNOWN: + try { + // stale if there are files in CaseDB with MIME types + if (hasFilesWithMimeType(t.getKey())) { + staleDataSourceIds.add(t.getKey()); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting MIME types", ex); + } + + break; } + }); // collect any new data sources in the case. @@ -385,7 +412,6 @@ public final class ImageGalleryController { logger.log(Level.SEVERE, "Image Gallery failed to check if datasources table is stale.", ex); return staleDataSourceIds; } - } /** @@ -451,12 +477,12 @@ public final class ImageGalleryController { * * @throws TskCoreException */ - public boolean hasFilesWithNoMimetype(Content datasource) throws TskCoreException { + public boolean hasFilesWithNoMimeType(long dataSourceId) throws TskCoreException { // There are some special files/attributes in the root folder, like $BadClus:$Bad and $Security:$SDS // The IngestTasksScheduler does not push them down to the ingest modules, // and hence they do not have any assigned mimetype - String whereClause = "data_source_obj_id = " + datasource.getId() + String whereClause = "data_source_obj_id = " + dataSourceId + " AND ( meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() + ")" + " AND ( mime_type IS NULL )" + " AND ( meta_addr >= 32 ) " @@ -465,6 +491,15 @@ public final class ImageGalleryController { return sleuthKitCase.countFilesWhere(whereClause) > 0; } + + public boolean hasFilesWithMimeType(long dataSourceId) throws TskCoreException { + + String whereClause = "data_source_obj_id = " + dataSourceId + + " AND ( meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() + ")" + + " AND ( mime_type IS NOT NULL )"; + + return sleuthKitCase.countFilesWhere(whereClause) > 0; + } synchronized private void shutDownDBExecutor() { if (dbExecutor != null) { @@ -645,12 +680,6 @@ public final class ImageGalleryController { "BulkTask.stopCopy.status=Stopping copy to drawable db task.", "BulkTask.errPopulating.errMsg=There was an error populating Image Gallery database."}) abstract static class BulkTransferTask extends BackgroundTask { - - static private final String FILE_EXTENSION_CLAUSE - = "(extension LIKE '" //NON-NLS - + String.join("' OR extension LIKE '", FileTypeUtils.getAllSupportedExtensions()) //NON-NLS - + "') "; - static private final String MIMETYPE_CLAUSE = "(mime_type LIKE '" //NON-NLS + String.join("' OR mime_type LIKE '", FileTypeUtils.getAllSupportedMimeTypes()) //NON-NLS @@ -679,20 +708,16 @@ public final class ImageGalleryController { = DATASOURCE_CLAUSE + " AND ( meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() + ")" + " AND ( " - + //grab files with supported extension - FILE_EXTENSION_CLAUSE //grab files with supported mime-types - + " OR " + MIMETYPE_CLAUSE //NON-NLS + + MIMETYPE_CLAUSE //NON-NLS //grab files with image or video mime-types even if we don't officially support them + " OR mime_type LIKE 'video/%' OR mime_type LIKE 'image/%' )"; //NON-NLS } /** * Do any cleanup for this task. - * - * @param success true if the transfer was successful */ - abstract void cleanup(boolean success); + abstract void cleanup(); abstract void processFile(final AbstractFile f, DrawableDB.DrawableTransaction tr, CaseDbTransaction caseDBTransaction) throws TskCoreException; @@ -715,17 +740,23 @@ public final class ImageGalleryController { DrawableDB.DrawableTransaction drawableDbTransaction = null; CaseDbTransaction caseDbTransaction = null; + boolean hasFilesWithNoMime = true; + boolean endedEarly = false; + try { - //grab all files with supported extension or detected mime types + // See if there are any files in the DS w/out a MIME TYPE + hasFilesWithNoMime = controller.hasFilesWithNoMimeType(dataSourceObjId); + + //grab all files with detected mime types final List files = getFiles(); progressHandle.switchToDeterminate(files.size()); taskDB.insertOrUpdateDataSource(dataSourceObjId, DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS); updateProgress(0.0); - taskCompletionStatus = true; int workDone = 0; + // Cycle through all of the files returned and call processFile on each //do in transaction drawableDbTransaction = taskDB.beginTransaction(); @@ -743,8 +774,9 @@ public final class ImageGalleryController { if (isCancelled() || Thread.interrupted()) { logger.log(Level.WARNING, "Task cancelled or interrupted: not all contents may be transfered to drawable database."); //NON-NLS - taskCompletionStatus = false; + endedEarly = true; progressHandle.finish(); + break; } @@ -798,27 +830,25 @@ public final class ImageGalleryController { progressHandle.progress(Bundle.BulkTask_stopCopy_status()); logger.log(Level.WARNING, "Stopping copy to drawable db task. Failed to transfer all database contents", ex); //NON-NLS MessageNotifyUtil.Notify.warn(Bundle.BulkTask_errPopulating_errMsg(), ex.getMessage()); - cleanup(false); + endedEarly = true; } finally { progressHandle.finish(); + // Mark to REBUILT_STALE if some files didnt' have MIME (ingest was still ongoing) or + // if there was cancellation or errors DrawableDB.DrawableDbBuildStatusEnum datasourceDrawableDBStatus - = (taskCompletionStatus) - ? DrawableDB.DrawableDbBuildStatusEnum.COMPLETE - : DrawableDB.DrawableDbBuildStatusEnum.DEFAULT; + = ((hasFilesWithNoMime == true) || (endedEarly == true)) + ? DrawableDB.DrawableDbBuildStatusEnum.REBUILT_STALE + : DrawableDB.DrawableDbBuildStatusEnum.COMPLETE; taskDB.insertOrUpdateDataSource(dataSourceObjId, datasourceDrawableDBStatus); updateMessage(""); updateProgress(-1.0); } - cleanup(taskCompletionStatus); + cleanup(); } abstract ProgressHandle getInitialProgressHandle(); - - protected void setTaskCompletionStatus(boolean status) { - taskCompletionStatus = status; - } } /** @@ -839,11 +869,11 @@ public final class ImageGalleryController { } @Override - protected void cleanup(boolean success) { + protected void cleanup() { taskDB.freeFileMetaDataCache(); // at the end of the task, set the stale status based on the // cumulative status of all data sources - controller.setStale(controller.isDataSourcesTableStale()); + controller.setCaseStale(controller.isDataSourcesTableStale()); } @Override @@ -853,12 +883,9 @@ public final class ImageGalleryController { if (known) { taskDB.removeFile(f.getId(), tr); //remove known files } else { - // if mimetype of the file hasn't been ascertained, ingest might not have completed yet. - if (null == f.getMIMEType()) { - // set to false to force the DB to be marked as stale - this.setTaskCompletionStatus(false); - } //supported mimetype => analyzed - else if (FileTypeUtils.hasDrawableMIMEType(f)) { + // NOTE: Files are being processed because they have the right MIME type, + // so we do not need to worry about this calculating them + if (FileTypeUtils.hasDrawableMIMEType(f)) { taskDB.updateFile(DrawableFile.create(f, true, false), tr, caseDbTransaction); } //unsupported mimtype => analyzed but shouldn't include else { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java index 5341e0be27..ad7ebc72f5 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryModule.java @@ -277,7 +277,7 @@ public class ImageGalleryModule { if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.LOCAL) { Content newDataSource = (Content) evt.getNewValue(); if (con.isListeningEnabled()) { - controller.getDatabase().insertOrUpdateDataSource(newDataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.DEFAULT); + controller.getDatabase().insertOrUpdateDataSource(newDataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN); } } break; @@ -329,10 +329,14 @@ public class ImageGalleryModule { if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.LOCAL) { if (controller.isListeningEnabled()) { - DataSourceAnalysisStartedEvent dataSourceAnalysisStartedEvent = (DataSourceAnalysisStartedEvent) evt; + DataSourceAnalysisStartedEvent dataSourceAnalysisStartedEvent = (DataSourceAnalysisStartedEvent) evt; Content dataSource = dataSourceAnalysisStartedEvent.getDataSource(); - - controller.getDatabase().insertOrUpdateDataSource(dataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS); + + DrawableDB drawableDb = controller.getDatabase(); + // Don't update status if it is is already marked as COMPLETE + if (drawableDb.getDataSourceDbBuildStatus(dataSource.getId()) != DrawableDB.DrawableDbBuildStatusEnum.COMPLETE) { + drawableDb.insertOrUpdateDataSource(dataSource.getId(), DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS); + } } } } else if (eventType == IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED) { @@ -342,12 +346,18 @@ public class ImageGalleryModule { DataSourceAnalysisCompletedEvent dataSourceAnalysisCompletedEvent = (DataSourceAnalysisCompletedEvent) evt; Content dataSource = dataSourceAnalysisCompletedEvent.getDataSource(); - DrawableDB.DrawableDbBuildStatusEnum datasourceDrawableDBStatus = - controller.hasFilesWithNoMimetype(dataSource) ? - DrawableDB.DrawableDbBuildStatusEnum.DEFAULT : - DrawableDB.DrawableDbBuildStatusEnum.COMPLETE; + DrawableDB drawableDb = controller.getDatabase(); + if (drawableDb.getDataSourceDbBuildStatus(dataSource.getId()) == DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS) { - controller.getDatabase().insertOrUpdateDataSource(dataSource.getId(), datasourceDrawableDBStatus); + // If at least one file in CaseDB has mime type, then set to COMPLETE + // Otherwise, back to UNKNOWN since we assume file type module was not run + DrawableDB.DrawableDbBuildStatusEnum datasourceDrawableDBStatus = + controller.hasFilesWithMimeType(dataSource.getId()) ? + DrawableDB.DrawableDbBuildStatusEnum.COMPLETE : + DrawableDB.DrawableDbBuildStatusEnum.UNKNOWN; + + controller.getDatabase().insertOrUpdateDataSource(dataSource.getId(), datasourceDrawableDBStatus); + } } return; } @@ -355,7 +365,7 @@ public class ImageGalleryModule { if (((AutopsyEvent) evt).getSourceType() == AutopsyEvent.SourceType.REMOTE) { // A remote node added a new data source and just finished ingest on it. //drawable db is stale, and if ImageGallery is open, ask user what to do - controller.setStale(true); + controller.setCaseStale(true); if (controller.isListeningEnabled()) { SwingUtilities.invokeLater(() -> { if (ImageGalleryTopComponent.isImageGalleryOpen()) { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java index 994ec7b3cd..c11005045d 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/NextUnseenGroup.java @@ -41,7 +41,7 @@ import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils; */ @NbBundle.Messages({ "NextUnseenGroup.markGroupSeen=Mark Group Seen", - "NextUnseenGroup.nextUnseenGroup=Next Unseen group", + "NextUnseenGroup.nextUnseenGroup=Next Unseen Group", "NextUnseenGroup.allGroupsSeen=All Groups Have Been Seen"}) public class NextUnseenGroup extends Action { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java index 5a921e6130..4cf4f10b25 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java @@ -39,6 +39,7 @@ import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; import org.openide.awt.ActionRegistration; +import org.openide.util.Exceptions; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; @@ -192,47 +193,50 @@ public final class OpenAction extends CallableSystemAction { addFXCallback(dataSourceStatusMapFuture, dataSourceStatusMap -> { - boolean dbIsStale = false; + int numStale = 0; + int numNoAnalysis = 0; + // NOTE: There is some overlapping code here with Controller.getStaleDataSourceIds(). We could possibly just use + // that method to figure out stale and then do more simple stuff here to figure out if there is no data at all for (Map.Entry entry : dataSourceStatusMap.entrySet()) { DrawableDbBuildStatusEnum status = entry.getValue(); - if (DrawableDbBuildStatusEnum.COMPLETE != status) { - dbIsStale = true; + if (DrawableDbBuildStatusEnum.UNKNOWN == status) { + try { + // likely a data source analyzed on a remote node in multi-user case OR single-user case with listening off + if (controller.hasFilesWithMimeType(entry.getKey())) { + numStale++; + // likely a data source (local or remote) that has no analysis yet (note there is also IN_PROGRESS state) + } else { + numNoAnalysis++; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error querying case database", ex); + } + } + // was already rebuilt, but wasn't complete at the end + else if (DrawableDbBuildStatusEnum.REBUILT_STALE == status) { + numStale++; } } - //back on fx thread. - if (false == dbIsStale) { - //drawable db is not stale, just open it - openTopComponent(); - } else { - - // If there is only one datasource and it's in DEFAULT State - - // ingest modules need to be run on the data source - if (dataSourceStatusMap.size()== 1) { - Map.Entry entry = dataSourceStatusMap.entrySet().iterator().next(); - if (entry.getValue() == DrawableDbBuildStatusEnum.DEFAULT ) { - Alert alert = new Alert(Alert.AlertType.WARNING, Bundle.OpenAction_notAnalyzedDlg_msg(), ButtonType.OK); - alert.setTitle(Bundle.OpenAction_stale_confDlg_title()); - alert.initModality(Modality.APPLICATION_MODAL); + // NOTE: we are running on the fx thread. - alert.showAndWait(); - return; - } - } - - //drawable db is stale, - //ask what to do + // If there are any that are STALE, give them a prompt to do so. + if (numStale > 0) { + // See if user wants to rebuild, cancel out, or open as is Alert alert = new Alert(Alert.AlertType.WARNING, - Bundle.OpenAction_stale_confDlg_msg(), - ButtonType.YES, ButtonType.NO, ButtonType.CANCEL); + Bundle.OpenAction_stale_confDlg_msg(), + ButtonType.YES, ButtonType.NO, ButtonType.CANCEL); alert.initModality(Modality.APPLICATION_MODAL); alert.setTitle(Bundle.OpenAction_stale_confDlg_title()); GuiUtils.setDialogIcons(alert); ButtonType answer = alert.showAndWait().orElse(ButtonType.CANCEL); + if (answer == ButtonType.CANCEL) { - //just do nothing + //just do nothing - don't open window + return; } else if (answer == ButtonType.NO) { - openTopComponent(); + // They don't want to rebuild. Just open the UI as is. + // NOTE: There could be no data.... } else if (answer == ButtonType.YES) { if (controller.getAutopsyCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE) { /* For a single-user case, we favor user @@ -255,9 +259,23 @@ public final class OpenAction extends CallableSystemAction { */ controller.rebuildDB(); } - openTopComponent(); } + openTopComponent(); + return; } + + // if there is no data to display, then let them know + if (numNoAnalysis == dataSourceStatusMap.size()) { + // give them a dialog to enable modules if no data sources have been analyzed + Alert alert = new Alert(Alert.AlertType.WARNING, Bundle.OpenAction_notAnalyzedDlg_msg(), ButtonType.OK); + alert.setTitle(Bundle.OpenAction_stale_confDlg_title()); + alert.initModality(Modality.APPLICATION_MODAL); + alert.showAndWait(); + return; + } + + // otherwise, lets open the UI + openTopComponent(); }, throwable -> logger.log(Level.SEVERE, "Error checking if drawable db is stale.", throwable)//NON-NLS ); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java index c981c91b3e..122ebbd75a 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableDB.java @@ -178,10 +178,10 @@ public final class DrawableDB { * DO NOT add in the middle. */ public enum DrawableDbBuildStatusEnum { - UNKNOWN, /// no known status + UNKNOWN, /// no known status - not yet analyzed IN_PROGRESS, /// ingest or db rebuild is in progress - COMPLETE, /// All files in the data source have had file type detected - DEFAULT; /// Not all files in the data source have had file type detected + COMPLETE, /// At least one file in the data source had a MIME type. Ingest filters may have been applied. + REBUILT_STALE; /// data source was rebuilt, but MIME types were missing during rebuild } private void dbWriteLock() { @@ -1209,6 +1209,15 @@ public final class DrawableDB { } return map; } + + + public DrawableDbBuildStatusEnum getDataSourceDbBuildStatus(Long dataSourceId) throws TskCoreException { + Map statusMap = getDataSourceDbBuildStatus(); + if (statusMap.containsKey(dataSourceId) == false) { + throw new TskCoreException("Data Source ID not found: " + dataSourceId); + } + return statusMap.get(dataSourceId); + } /** * Insert/update given data source object id and it's DB rebuild status in @@ -1554,16 +1563,9 @@ public final class DrawableDB { } public long countAllFiles() throws TskCoreException { - return countAllFiles(null); + return countFilesWhere(" 1 "); } - public long countAllFiles(DataSource dataSource) throws TskCoreException { - if (null != dataSource) { - return countFilesWhere(" data_source_obj_id = "); - } else { - return countFilesWhere(" 1 "); - } - } /** * delete the row with obj_id = id. diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/DataSourceCell.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/DataSourceCell.java index 7626c4fd77..7f8bb397f6 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/DataSourceCell.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/DataSourceCell.java @@ -50,6 +50,11 @@ public class DataSourceCell extends ListCell> { } + /** + * + * @param item + * @param empty + */ @Override protected void updateItem(Optional item, boolean empty) { super.updateItem(item, empty); @@ -57,31 +62,42 @@ public class DataSourceCell extends ListCell> { setText(""); } else { DataSource dataSource = item.orElse(null); - String text = (dataSource == null) ? "All" : dataSource.getName() + " (Id: " + dataSource.getId() + ")"; - Boolean tooManyFilesInDataSource = dataSourcesTooManyFiles.getOrDefault(dataSource, false); + String dataSourceName; + boolean shouldEnable = true; // false if user should not be able to select the item - DrawableDbBuildStatusEnum dataSourceDBStatus = (dataSource != null) ? - dataSourcesDrawableDBStatus.get(dataSource.getId()) : DrawableDbBuildStatusEnum.UNKNOWN; - - Boolean dataSourceNotAnalyzed = (dataSourceDBStatus == DrawableDbBuildStatusEnum.DEFAULT); - if (tooManyFilesInDataSource) { - text += " - Too many files"; - } - if (dataSourceNotAnalyzed) { - text += " - Not Analyzed"; - } - - // check if item should be disabled - if (tooManyFilesInDataSource || dataSourceNotAnalyzed) { - setDisable(true); - setStyle("-fx-opacity : .5"); + if (dataSource == null) { + dataSourceName = "All"; + // NOTE: openAction verifies that there is at least one data source with data. + // So, at this point, "All" should never need to be disabled because none of the data sources + // are analyzed. } else { + dataSourceName = dataSource.getName() + " (Id: " + dataSource.getId() + ")"; + if (dataSourcesDrawableDBStatus.get(dataSource.getId()) == DrawableDbBuildStatusEnum.UNKNOWN) { + dataSourceName += " - Not Analyzed"; + shouldEnable = false; + } + } + + // if it's analyzed, then make sure there aren't too many files + if (shouldEnable) { + if (dataSourcesTooManyFiles.getOrDefault(dataSource, false)) { + dataSourceName += " - Too Many Files"; + shouldEnable = false; + } + } + + // check if item should be disabled + if (shouldEnable) { setGraphic(null); setStyle("-fx-opacity : 1"); } + else { + setDisable(true); + setStyle("-fx-opacity : .5"); + } - setText(text); + setText(dataSourceName); } } } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.fxml b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.fxml index c0ee4488b1..386183a88a 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.fxml +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/StatusBar.fxml @@ -16,7 +16,7 @@ -