diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/Bundle.properties-MERGED index 2a45b536bd..192d522fe0 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/Bundle.properties-MERGED @@ -16,6 +16,7 @@ FileExtRootFilter_documents_displayName=Documents FileExtRootFilter_executable_displayName=Executable FileExtRootFilter_image_displayName=Images FileExtRootFilter_video_displayName=Video +FileTypesByMimeType.name.text=By MIME Type ThreePanelDataArtifactDAO.dataArtifact.columnKeys.comment.description=Comment ThreePanelDataArtifactDAO.dataArtifact.columnKeys.comment.displayName=C ThreePanelDataArtifactDAO.dataArtifact.columnKeys.comment.name=Comment diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/FileTypeMimeSearchParam.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/FileTypeMimeSearchParam.java new file mode 100755 index 0000000000..6ef8881984 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/FileTypeMimeSearchParam.java @@ -0,0 +1,92 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.mainui.datamodel; + +import java.util.Objects; + +/** + * Key for accessing data about file MIME type from the DAO. + */ +public class FileTypeMimeSearchParam extends BaseSearchParam { + + private final String mimeType; + private final Long dataSourceId; + private final boolean knownShown; + + // TODO: This should ideally take in some kind of ENUM once we redo the tree. + // this assumes that filters implicitly or explicitly implement hashCode and equals to work + public FileTypeMimeSearchParam(String mimeType, Long dataSourceId, boolean showKnown) { + this.mimeType = mimeType; + this.dataSourceId = dataSourceId; + this.knownShown = showKnown; + } + + public FileTypeMimeSearchParam(String mimeType, Long dataSourceId, boolean knownShown, long startItem, Long maxResultsCount) { + super(startItem, maxResultsCount); + this.mimeType = mimeType; + this.dataSourceId = dataSourceId; + this.knownShown = knownShown; + } + + public String getFilter() { + return mimeType; + } + + public Long getDataSourceId() { + return dataSourceId; + } + + public boolean isKnownShown() { + return knownShown; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 23 * hash + Objects.hashCode(this.mimeType); + hash = 23 * hash + Objects.hashCode(this.dataSourceId); + hash = 23 * hash + (this.knownShown ? 1 : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final FileTypeMimeSearchParam other = (FileTypeMimeSearchParam) obj; + if (this.knownShown != other.knownShown) { + return false; + } + if (!(this.mimeType.equals(other.mimeType))) { + return false; + } + if (!Objects.equals(this.dataSourceId, other.dataSourceId)) { + return false; + } + return true; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ViewsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ViewsDAO.java index 6feea44708..727529e15c 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ViewsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ViewsDAO.java @@ -28,9 +28,11 @@ import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTree; import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; import org.sleuthkit.autopsy.datamodel.FileTypeExtensions; import org.sleuthkit.autopsy.mainui.datamodel.FileRowDTO.ExtensionMediaType; @@ -151,6 +153,7 @@ public class ViewsDAO { } private final Cache fileTypeByExtensionCache = CacheBuilder.newBuilder().maximumSize(1000).build(); + private final Cache fileTypeByMimeCache = CacheBuilder.newBuilder().maximumSize(1000).build(); public SearchResultsDTO getFilesByExtension(FileTypeExtensionsSearchParam key) throws ExecutionException, IllegalArgumentException { if (key.getFilter() == null) { @@ -159,8 +162,19 @@ public class ViewsDAO { throw new IllegalArgumentException("Data source id must be greater than 0 or null"); } - return fileTypeByExtensionCache.get(key, () -> fetchFileViewFiles(key.getFilter(), key.getDataSourceId(), key.isKnownShown())); + return fileTypeByExtensionCache.get(key, () -> fetchExtensionSearchResultsDTOs(key.getFilter(), key.getDataSourceId(), key.isKnownShown())); } + + public SearchResultsDTO getFilesByMime(FileTypeMimeSearchParam key) throws ExecutionException, IllegalArgumentException { + if (key.getFilter() == null) { + throw new IllegalArgumentException("Must have non-null filter"); + } else if (key.getDataSourceId() != null && key.getDataSourceId() <= 0) { + throw new IllegalArgumentException("Data source id must be greater than 0 or null"); + } + + return fileTypeByMimeCache.get(key, () -> fetchMimeSearchResultsDTOs(key.getFilter(), key.getDataSourceId(), key.isKnownShown())); + } + // private ViewFileTableSearchResultsDTO fetchFilesForTable(ViewFileCacheKey cacheKey) throws NoCurrentCaseException, TskCoreException { // @@ -179,7 +193,7 @@ public class ViewsDAO { private Map fetchFileViewCounts(List filters, Long dataSourceId, boolean showKnown) throws NoCurrentCaseException, TskCoreException { Map counts = new HashMap<>(); for (FileExtSearchFilter filter : filters) { - String whereClause = getFileWhereStatement(filter, dataSourceId, showKnown); + String whereClause = getFileExtensionWhereStatement(filter, dataSourceId, showKnown); long count = getCase().countFilesWhere(whereClause); counts.put(filter.getId(), count); } @@ -187,7 +201,7 @@ public class ViewsDAO { return counts; } - private String getFileWhereStatement(FileExtSearchFilter filter, Long dataSourceId, boolean showKnown) { + private String getFileExtensionWhereStatement(FileExtSearchFilter filter, Long dataSourceId, boolean showKnown) { String whereClause = "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")" + (showKnown ? " " @@ -201,9 +215,38 @@ public class ViewsDAO { .collect(Collectors.joining(", ")) + "))"; return whereClause; } + + private String getFileMimeWhereStatement(String mimeType, Long dataSourceId, boolean showKnown) { - private SearchResultsDTO fetchFileViewFiles(FileExtSearchFilter filter, Long dataSourceId, boolean showKnown) throws NoCurrentCaseException, TskCoreException { - String whereStatement = getFileWhereStatement(filter, dataSourceId, showKnown); + String whereClause = "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")" + + " AND (type IN (" + + TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal() + "," + + TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal() + "," + + TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal() + "," + + TskData.TSK_DB_FILES_TYPE_ENUM.LAYOUT_FILE.ordinal() + "," + + TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal() + + (hideSlackFilesInViewsTree() ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal())) + + "))" + + ((dataSourceId > 0) ? " AND data_source_obj_id = " + dataSourceId : " ") + + (showKnown ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : "") + + " AND mime_type = '" + mimeType + "'"; + + return whereClause; + } + + private SearchResultsDTO fetchExtensionSearchResultsDTOs(FileExtSearchFilter filter, Long dataSourceId, boolean showKnown) throws NoCurrentCaseException, TskCoreException { + String whereStatement = getFileExtensionWhereStatement(filter, dataSourceId, showKnown); + return fetchFileViewFiles(whereStatement, filter.getDisplayName()); + } + + @NbBundle.Messages({"FileTypesByMimeType.name.text=By MIME Type"}) + private SearchResultsDTO fetchMimeSearchResultsDTOs(String mimeType, Long dataSourceId, boolean showKnown) throws NoCurrentCaseException, TskCoreException { + String whereStatement = getFileMimeWhereStatement(mimeType, dataSourceId, showKnown); + final String MIME_TYPE_DISPLAY_NAME = Bundle.FileTypesByMimeType_name_text(); + return fetchFileViewFiles(whereStatement, MIME_TYPE_DISPLAY_NAME); + } + + private SearchResultsDTO fetchFileViewFiles(String whereStatement, String displayName) throws NoCurrentCaseException, TskCoreException { List files = getCase().findAllFilesWhere(whereStatement); List fileRows = new ArrayList<>(); @@ -259,7 +302,7 @@ public class ViewsDAO { cellValues)); } - return new BaseSearchResultsDTO(FILE_VIEW_EXT_TYPE_ID, filter.getDisplayName(), FILE_COLUMNS, fileRows); + return new BaseSearchResultsDTO(FILE_VIEW_EXT_TYPE_ID, displayName, FILE_COLUMNS, fileRows); } }