mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge branch 'new_table_load' of github.com:sleuthkit/autopsy into 8146b-specialTreeWithPreparedStatements
This commit is contained in:
commit
100db86ebc
@ -46,8 +46,48 @@ FileExtRootFilter_documents_displayName=Documents
|
||||
FileExtRootFilter_executable_displayName=Executable
|
||||
FileExtRootFilter_image_displayName=Images
|
||||
FileExtRootFilter_video_displayName=Video
|
||||
FileSystemColumnUtils.abstractFileColumns.accessTimeColLbl=Access Time
|
||||
FileSystemColumnUtils.abstractFileColumns.attrAddrColLbl=Attr. Addr.
|
||||
FileSystemColumnUtils.abstractFileColumns.changeTimeColLbl=Change Time
|
||||
FileSystemColumnUtils.abstractFileColumns.commentName=C
|
||||
FileSystemColumnUtils.abstractFileColumns.countName=O
|
||||
FileSystemColumnUtils.abstractFileColumns.createdTimeColLbl=Created Time
|
||||
FileSystemColumnUtils.abstractFileColumns.extensionColLbl=Extension
|
||||
FileSystemColumnUtils.abstractFileColumns.flagsDirColLbl=Flags(Dir)
|
||||
FileSystemColumnUtils.abstractFileColumns.flagsMetaColLbl=Flags(Meta)
|
||||
FileSystemColumnUtils.abstractFileColumns.groupidColLbl=GroupID
|
||||
FileSystemColumnUtils.abstractFileColumns.knownColLbl=Known
|
||||
FileSystemColumnUtils.abstractFileColumns.locationColLbl=Location
|
||||
FileSystemColumnUtils.abstractFileColumns.md5HashColLbl=MD5 Hash
|
||||
FileSystemColumnUtils.abstractFileColumns.metaAddrColLbl=Meta Addr.
|
||||
FileSystemColumnUtils.abstractFileColumns.mimeType=MIME Type
|
||||
FileSystemColumnUtils.abstractFileColumns.modeColLbl=Mode
|
||||
FileSystemColumnUtils.abstractFileColumns.modifiedTimeColLbl=Modified Time
|
||||
FileSystemColumnUtils.abstractFileColumns.objectId=Object ID
|
||||
FileSystemColumnUtils.abstractFileColumns.originalName=Original Name
|
||||
FileSystemColumnUtils.abstractFileColumns.scoreName=S
|
||||
FileSystemColumnUtils.abstractFileColumns.sha256HashColLbl=SHA-256 Hash
|
||||
FileSystemColumnUtils.abstractFileColumns.sizeColLbl=Size
|
||||
FileSystemColumnUtils.abstractFileColumns.typeDirColLbl=Type(Dir)
|
||||
FileSystemColumnUtils.abstractFileColumns.typeMetaColLbl=Type(Meta)
|
||||
FileSystemColumnUtils.abstractFileColumns.useridColLbl=UserID
|
||||
FileSystemColumnUtils.imageColumns.devID=Device ID
|
||||
FileSystemColumnUtils.imageColumns.sectorSize=Sector Size (Bytes)
|
||||
FileSystemColumnUtils.imageColumns.size=Size (Bytes)
|
||||
FileSystemColumnUtils.imageColumns.timezone=Timezone
|
||||
FileSystemColumnUtils.imageColumns.type=Type
|
||||
FileSystemColumnUtils.imageColumns.typeValue=Image
|
||||
FileSystemColumnUtils.nameColumn.name=Name
|
||||
FileSystemColumnUtils.noDescription=No Description
|
||||
FileSystemColumnUtils.poolColumns.type=Type
|
||||
FileSystemColumnUtils.volumeColumns.desc=Description
|
||||
FileSystemColumnUtils.volumeColumns.flags=Flags
|
||||
FileSystemColumnUtils.volumeColumns.id=ID
|
||||
FileSystemColumnUtils.volumeColumns.length=Length in Sectors
|
||||
FileSystemColumnUtils.volumeColumns.startingSector=Starting Sector
|
||||
FileTag.name.text=File Tag
|
||||
FileTypesByMimeType.name.text=By MIME Type
|
||||
ResultTag.name.text=Result Tag
|
||||
TagsDAO.fileColumns.accessTimeColLbl=Accessed Time
|
||||
TagsDAO.fileColumns.changeTimeColLbl=Changed Time
|
||||
TagsDAO.fileColumns.commentColLbl=Comment
|
||||
@ -66,30 +106,3 @@ TagsDAO.tagColumns.sourceNameColLbl=Source Name
|
||||
TagsDAO.tagColumns.sourcePathColLbl=Source File Path
|
||||
TagsDAO.tagColumns.typeColLbl=Result Type
|
||||
TagsDAO.tagColumns.userNameColLbl=User Name
|
||||
ThreePanelViewsDAO.fileColumns.accessTimeColLbl=Access Time
|
||||
ThreePanelViewsDAO.fileColumns.attrAddrColLbl=Attr. Addr.
|
||||
ThreePanelViewsDAO.fileColumns.changeTimeColLbl=Change Time
|
||||
ThreePanelViewsDAO.fileColumns.commentName=C
|
||||
ThreePanelViewsDAO.fileColumns.countName=O
|
||||
ThreePanelViewsDAO.fileColumns.createdTimeColLbl=Created Time
|
||||
ThreePanelViewsDAO.fileColumns.extensionColLbl=Extension
|
||||
ThreePanelViewsDAO.fileColumns.flagsDirColLbl=Flags(Dir)
|
||||
ThreePanelViewsDAO.fileColumns.flagsMetaColLbl=Flags(Meta)
|
||||
ThreePanelViewsDAO.fileColumns.groupidColLbl=GroupID
|
||||
ThreePanelViewsDAO.fileColumns.knownColLbl=Known
|
||||
ThreePanelViewsDAO.fileColumns.locationColLbl=Location
|
||||
ThreePanelViewsDAO.fileColumns.md5HashColLbl=MD5 Hash
|
||||
ThreePanelViewsDAO.fileColumns.metaAddrColLbl=Meta Addr.
|
||||
ThreePanelViewsDAO.fileColumns.mimeType=MIME Type
|
||||
ThreePanelViewsDAO.fileColumns.modeColLbl=Mode
|
||||
ThreePanelViewsDAO.fileColumns.modifiedTimeColLbl=Modified Time
|
||||
ThreePanelViewsDAO.fileColumns.nameColLbl=Name
|
||||
ThreePanelViewsDAO.fileColumns.noDescription=No Description
|
||||
ThreePanelViewsDAO.fileColumns.objectId=Object ID
|
||||
ThreePanelViewsDAO.fileColumns.originalName=Original Name
|
||||
ThreePanelViewsDAO.fileColumns.scoreName=S
|
||||
ThreePanelViewsDAO.fileColumns.sha256HashColLbl=SHA-256 Hash
|
||||
ThreePanelViewsDAO.fileColumns.sizeColLbl=Size
|
||||
ThreePanelViewsDAO.fileColumns.typeDirColLbl=Type(Dir)
|
||||
ThreePanelViewsDAO.fileColumns.typeMetaColLbl=Type(Meta)
|
||||
ThreePanelViewsDAO.fileColumns.useridColLbl=UserID
|
||||
|
@ -0,0 +1,535 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> 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.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.FileSystem;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.Pool;
|
||||
import org.sleuthkit.datamodel.Volume;
|
||||
import org.sleuthkit.datamodel.VolumeSystem;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Utility class for creating consistent table data.
|
||||
*/
|
||||
class FileSystemColumnUtils {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(FileSystemColumnUtils.class.getName());
|
||||
|
||||
enum ContentType {
|
||||
IMAGE,
|
||||
POOL,
|
||||
VOLUME,
|
||||
ABSTRACT_FILE,
|
||||
UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Messages({"FileSystemColumnUtils.nameColumn.name=Name",
|
||||
"FileSystemColumnUtils.abstractFileColumns.originalName=Original Name",
|
||||
"FileSystemColumnUtils.abstractFileColumns.scoreName=S",
|
||||
"FileSystemColumnUtils.abstractFileColumns.commentName=C",
|
||||
"FileSystemColumnUtils.abstractFileColumns.countName=O",
|
||||
"FileSystemColumnUtils.abstractFileColumns.locationColLbl=Location",
|
||||
"FileSystemColumnUtils.abstractFileColumns.modifiedTimeColLbl=Modified Time",
|
||||
"FileSystemColumnUtils.abstractFileColumns.changeTimeColLbl=Change Time",
|
||||
"FileSystemColumnUtils.abstractFileColumns.accessTimeColLbl=Access Time",
|
||||
"FileSystemColumnUtils.abstractFileColumns.createdTimeColLbl=Created Time",
|
||||
"FileSystemColumnUtils.abstractFileColumns.sizeColLbl=Size",
|
||||
"FileSystemColumnUtils.abstractFileColumns.flagsDirColLbl=Flags(Dir)",
|
||||
"FileSystemColumnUtils.abstractFileColumns.flagsMetaColLbl=Flags(Meta)",
|
||||
"FileSystemColumnUtils.abstractFileColumns.modeColLbl=Mode",
|
||||
"FileSystemColumnUtils.abstractFileColumns.useridColLbl=UserID",
|
||||
"FileSystemColumnUtils.abstractFileColumns.groupidColLbl=GroupID",
|
||||
"FileSystemColumnUtils.abstractFileColumns.metaAddrColLbl=Meta Addr.",
|
||||
"FileSystemColumnUtils.abstractFileColumns.attrAddrColLbl=Attr. Addr.",
|
||||
"FileSystemColumnUtils.abstractFileColumns.typeDirColLbl=Type(Dir)",
|
||||
"FileSystemColumnUtils.abstractFileColumns.typeMetaColLbl=Type(Meta)",
|
||||
"FileSystemColumnUtils.abstractFileColumns.knownColLbl=Known",
|
||||
"FileSystemColumnUtils.abstractFileColumns.md5HashColLbl=MD5 Hash",
|
||||
"FileSystemColumnUtils.abstractFileColumns.sha256HashColLbl=SHA-256 Hash",
|
||||
"FileSystemColumnUtils.abstractFileColumns.objectId=Object ID",
|
||||
"FileSystemColumnUtils.abstractFileColumns.mimeType=MIME Type",
|
||||
"FileSystemColumnUtils.abstractFileColumns.extensionColLbl=Extension",
|
||||
"FileSystemColumnUtils.volumeColumns.id=ID",
|
||||
"FileSystemColumnUtils.volumeColumns.startingSector=Starting Sector",
|
||||
"FileSystemColumnUtils.volumeColumns.length=Length in Sectors",
|
||||
"FileSystemColumnUtils.volumeColumns.desc=Description",
|
||||
"FileSystemColumnUtils.volumeColumns.flags=Flags",
|
||||
"FileSystemColumnUtils.imageColumns.type=Type",
|
||||
"FileSystemColumnUtils.imageColumns.typeValue=Image",
|
||||
"FileSystemColumnUtils.imageColumns.size=Size (Bytes)",
|
||||
"FileSystemColumnUtils.imageColumns.sectorSize=Sector Size (Bytes)",
|
||||
"FileSystemColumnUtils.imageColumns.timezone=Timezone",
|
||||
"FileSystemColumnUtils.imageColumns.devID=Device ID",
|
||||
"FileSystemColumnUtils.poolColumns.type=Type",
|
||||
|
||||
"FileSystemColumnUtils.noDescription=No Description"})
|
||||
|
||||
private static final ColumnKey NAME_COLUMN = getColumnKey(Bundle.FileSystemColumnUtils_nameColumn_name());
|
||||
|
||||
private static final List<ColumnKey> ABSTRACT_FILE_COLUMNS = Arrays.asList(
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_originalName()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_scoreName()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_commentName()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_countName()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_locationColLbl()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_modifiedTimeColLbl()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_changeTimeColLbl()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_accessTimeColLbl()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_createdTimeColLbl()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_sizeColLbl()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_flagsDirColLbl()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_flagsMetaColLbl()),
|
||||
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_modeColLbl()),
|
||||
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_useridColLbl()),
|
||||
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_groupidColLbl()),
|
||||
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_metaAddrColLbl()),
|
||||
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_attrAddrColLbl()),
|
||||
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_typeDirColLbl()),
|
||||
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_typeMetaColLbl()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_knownColLbl()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_md5HashColLbl()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_sha256HashColLbl()),
|
||||
// getFileColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_objectId()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_mimeType()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_abstractFileColumns_extensionColLbl()));
|
||||
|
||||
private static final List<ColumnKey> VOLUME_COLUMNS = Arrays.asList(
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_volumeColumns_id()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_volumeColumns_startingSector()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_volumeColumns_length()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_volumeColumns_desc()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_volumeColumns_flags()));
|
||||
|
||||
private static final List<ColumnKey> IMAGE_COLUMNS = Arrays.asList(
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_imageColumns_type()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_imageColumns_size()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_imageColumns_sectorSize()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_imageColumns_timezone()),
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_imageColumns_devID())
|
||||
);
|
||||
|
||||
// Not used yet - Note that Hosts aren't content and will not be combined with other types, so we include the name here
|
||||
private static final List<ColumnKey> HOST_COLUMNS = Arrays.asList(
|
||||
NAME_COLUMN
|
||||
);
|
||||
|
||||
private static final List<ColumnKey> POOL_COLUMNS = Arrays.asList(
|
||||
getColumnKey(Bundle.FileSystemColumnUtils_poolColumns_type())
|
||||
);
|
||||
|
||||
/**
|
||||
* Convert a given Content object to an enum.
|
||||
*
|
||||
* @param content The Content object.
|
||||
*
|
||||
* @return The type corresponding to the content; UNSUPPORTED if the content will not be displayed
|
||||
*/
|
||||
private static ContentType getContentType(Content content) {
|
||||
if (content instanceof Image) {
|
||||
return ContentType.IMAGE;
|
||||
} else if (content instanceof Volume) {
|
||||
return ContentType.VOLUME;
|
||||
} else if (content instanceof Pool) {
|
||||
return ContentType.POOL;
|
||||
} else if (content instanceof AbstractFile) {
|
||||
return ContentType.ABSTRACT_FILE;
|
||||
}
|
||||
return ContentType.UNSUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given content object should be displayed.
|
||||
* We can display an object if ContentType is not UNSUPPORTED
|
||||
* and if it is not the root directory.
|
||||
*
|
||||
* @param content The content.
|
||||
*
|
||||
* @return True if the content is displayable, false otherwise.
|
||||
*/
|
||||
static boolean isDisplayable(Content content) {
|
||||
if (content instanceof AbstractFile) {
|
||||
return ! ((AbstractFile)content).isRoot();
|
||||
}
|
||||
return (getContentType(content) != ContentType.UNSUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the content types from the list that will be displayed.
|
||||
* Call this before getColumnKeysForContent() and getCellValuesForContent()
|
||||
* to ensure consistent columns.
|
||||
*
|
||||
* @param contentList List of content.
|
||||
*
|
||||
* @return List of types that will be displayed.
|
||||
*/
|
||||
static List<ContentType> getDisplayableTypesForContentList(List<Content> contentList) {
|
||||
List<ContentType> displayableTypes = new ArrayList<>();
|
||||
for (Content content : contentList) {
|
||||
ContentType type = getContentType(content);
|
||||
if (type != ContentType.UNSUPPORTED && ! displayableTypes.contains(type)) {
|
||||
displayableTypes.add(type);
|
||||
}
|
||||
}
|
||||
Collections.sort(displayableTypes);
|
||||
return displayableTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the column keys corresponding to the given list of types.
|
||||
*
|
||||
* @param contentTypes The list of types.
|
||||
*
|
||||
* @return The list of column keys.
|
||||
*/
|
||||
static List<ColumnKey> getColumnKeysForContent(List<ContentType> contentTypes) {
|
||||
List<ColumnKey> colKeys = new ArrayList<>();
|
||||
colKeys.add(NAME_COLUMN);
|
||||
|
||||
// Make sure content types are processed in the same order as in getCellValuesForContent()
|
||||
if (contentTypes.contains(ContentType.IMAGE)) {
|
||||
colKeys.addAll(IMAGE_COLUMNS);
|
||||
}
|
||||
if (contentTypes.contains(ContentType.POOL)) {
|
||||
colKeys.addAll(POOL_COLUMNS);
|
||||
}
|
||||
if (contentTypes.contains(ContentType.VOLUME)) {
|
||||
colKeys.addAll(VOLUME_COLUMNS);
|
||||
}
|
||||
if (contentTypes.contains(ContentType.ABSTRACT_FILE)) {
|
||||
colKeys.addAll(ABSTRACT_FILE_COLUMNS);
|
||||
}
|
||||
return colKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cell values for a given content object.
|
||||
*
|
||||
* @param content The content to display.
|
||||
* @param contentTypes The content types being displayed in the table.
|
||||
*
|
||||
* @return The cell values for this row.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
static List<Object> getCellValuesForContent(Content content, List<ContentType> contentTypes) throws TskCoreException {
|
||||
List<Object> cellValues = new ArrayList<>();
|
||||
cellValues.add(getNameValueForContent(content));
|
||||
|
||||
// Make sure content types are processed in the same order as in getColumnKeysForContent()
|
||||
if (contentTypes.contains(ContentType.IMAGE)) {
|
||||
cellValues.addAll(getNonNameCellValuesForImage(content));
|
||||
}
|
||||
if (contentTypes.contains(ContentType.POOL)) {
|
||||
cellValues.addAll(getNonNameCellValuesForPool(content));
|
||||
}
|
||||
if (contentTypes.contains(ContentType.VOLUME)) {
|
||||
cellValues.addAll(getNonNameCellValuesForVolume(content));
|
||||
}
|
||||
if (contentTypes.contains(ContentType.ABSTRACT_FILE)) {
|
||||
cellValues.addAll(getNonNameCellValuesForAbstractFile(content));
|
||||
}
|
||||
return cellValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value for the name column for the given content.
|
||||
*
|
||||
* @param content The content.
|
||||
*
|
||||
* @return The display name for the content.
|
||||
*/
|
||||
private static String getNameValueForContent(Content content) {
|
||||
if (content instanceof Image) {
|
||||
Image image = (Image)content;
|
||||
return image.getName();
|
||||
} else if (content instanceof Volume) {
|
||||
Volume vol = (Volume)content;
|
||||
return getVolumeDisplayName(vol);
|
||||
} else if (content instanceof Pool) {
|
||||
Pool pool = (Pool)content;
|
||||
return pool.getType().getName(); // We currently use the type name for both the name and type fields
|
||||
} else if (content instanceof AbstractFile) {
|
||||
AbstractFile file = (AbstractFile)content;
|
||||
return file.getName(); // GVDTODO handle . and .. from getContentDisplayName()
|
||||
}
|
||||
return content.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the column keys for an abstract file object.
|
||||
* Only use this method if all rows contain AbstractFile objects.
|
||||
* Make sure the order here matches that in getCellValuesForAbstractFile();
|
||||
*
|
||||
* @return The list of column keys.
|
||||
*/
|
||||
static List<ColumnKey> getColumnKeysForAbstractfile() {
|
||||
List<ColumnKey> colKeys = new ArrayList<>();
|
||||
colKeys.add(NAME_COLUMN);
|
||||
colKeys.addAll(ABSTRACT_FILE_COLUMNS);
|
||||
return colKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cell values for an abstract file.
|
||||
* Only use this method if all rows contain AbstractFile objects.
|
||||
* Make sure the order here matches that in getColumnKeysForAbstractfile();
|
||||
*
|
||||
* @param file The file to use to populate the cells.
|
||||
*
|
||||
* @return List of cell values.
|
||||
*/
|
||||
static List<Object> getCellValuesForAbstractFile(AbstractFile file) throws TskCoreException {
|
||||
List<Object> cells = new ArrayList<>();
|
||||
cells.add(getNameValueForContent(file));
|
||||
cells.addAll(getNonNameCellValuesForAbstractFile(file));
|
||||
return cells;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the order here matches that in ABSTRACT_FILE_COLUMNS
|
||||
*
|
||||
* @param content The content to use to populate the cells (may not be an abstract file)
|
||||
*
|
||||
* @return List of cell values
|
||||
*/
|
||||
private static List<Object> getNonNameCellValuesForAbstractFile(Content content) throws TskCoreException {
|
||||
final int nColumns = 17;
|
||||
if (! (content instanceof AbstractFile)) {
|
||||
return Collections.nCopies(nColumns, null);
|
||||
}
|
||||
|
||||
// Make sure to update nColumns if the number of columns here changes
|
||||
AbstractFile file = (AbstractFile) content;
|
||||
return Arrays.asList(
|
||||
// GVDTODO translation column
|
||||
null,
|
||||
//GVDTDO replace nulls with SCO
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
file.getUniquePath(),
|
||||
TimeZoneUtils.getFormattedTime(file.getMtime()),
|
||||
TimeZoneUtils.getFormattedTime(file.getCtime()),
|
||||
TimeZoneUtils.getFormattedTime(file.getAtime()),
|
||||
TimeZoneUtils.getFormattedTime(file.getCrtime()),
|
||||
file.getSize(),
|
||||
file.getDirFlagAsString(),
|
||||
file.getMetaFlagsAsString(),
|
||||
// mode,
|
||||
// userid,
|
||||
// groupid,
|
||||
// metaAddr,
|
||||
// attrAddr,
|
||||
// typeDir,
|
||||
// typeMeta,
|
||||
|
||||
file.getKnown().getName(),
|
||||
StringUtils.defaultString(file.getMd5Hash()),
|
||||
StringUtils.defaultString(file.getSha256Hash()),
|
||||
// objectId,
|
||||
|
||||
StringUtils.defaultString(file.getMIMEType()),
|
||||
file.getNameExtension()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the order here matches that in POOL_COLUMNS
|
||||
*
|
||||
* @param conent The content to use to populate the cells (may not be a pool)
|
||||
*
|
||||
* @return List of cell values
|
||||
*/
|
||||
private static List<Object> getNonNameCellValuesForPool(Content content) throws TskCoreException {
|
||||
final int nColumns = 1;
|
||||
if (! (content instanceof Pool)) {
|
||||
return Collections.nCopies(nColumns, null);
|
||||
}
|
||||
|
||||
// Make sure to update nColumns if the number of columns here changes
|
||||
Pool pool = (Pool) content;
|
||||
return Arrays.asList(
|
||||
pool.getType().getName() // We currently use the type name for both the name and type fields
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the order here matches that in HOST_COLUMNS
|
||||
*
|
||||
* @param host The host to use to populate the cells
|
||||
*
|
||||
* @return List of cell values
|
||||
*/
|
||||
static List<Object> getCellValuesForHost(Host host) throws TskCoreException {
|
||||
return Arrays.asList(
|
||||
host.getName()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the order here matches that in IMAGE_COLUMNS
|
||||
*
|
||||
* @param content The content to use to populate the cells (may not be an image)
|
||||
*
|
||||
* @return List of cell values
|
||||
*/
|
||||
private static List<Object> getNonNameCellValuesForImage(Content content) throws TskCoreException {
|
||||
final int nColumns = 5;
|
||||
if (! (content instanceof Image)) {
|
||||
return Collections.nCopies(nColumns, null);
|
||||
}
|
||||
|
||||
// Make sure to update nColumns if the number of columns here changes
|
||||
Image image = (Image) content;
|
||||
return Arrays.asList(
|
||||
Bundle.FileSystemColumnUtils_imageColumns_typeValue(),
|
||||
image.getSize(),
|
||||
image.getSsize(),
|
||||
image.getTimeZone(),
|
||||
image.getDeviceId()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the order here matches that in VOLUME_COLUMNS
|
||||
*
|
||||
* @param content The content to use to populate the cells (may not be a volume)
|
||||
*
|
||||
* @return List of cell values
|
||||
*/
|
||||
private static List<Object> getNonNameCellValuesForVolume(Content content) throws TskCoreException {
|
||||
final int nColumns = 5;
|
||||
if (! (content instanceof Volume)) {
|
||||
return Collections.nCopies(nColumns, null);
|
||||
}
|
||||
|
||||
// Make sure to update nColumns if the number of columns here changes
|
||||
Volume vol = (Volume) content;
|
||||
return Arrays.asList(
|
||||
vol.getAddr(),
|
||||
vol.getStart(),
|
||||
vol.getLength(),
|
||||
vol.getDescription(),
|
||||
vol.getFlagsAsString()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display name for a volume.
|
||||
*
|
||||
* @param vol The volume.
|
||||
*
|
||||
* @return The display name.
|
||||
*/
|
||||
private static String getVolumeDisplayName(Volume vol) {
|
||||
// set name, display name, and icon
|
||||
String volName = "vol" + Long.toString(vol.getAddr());
|
||||
long end = vol.getStart() + (vol.getLength() - 1);
|
||||
String tempVolName = volName + " (" + vol.getDescription() + ": " + vol.getStart() + "-" + end + ")";
|
||||
|
||||
// If this is a pool volume use a custom display name
|
||||
try {
|
||||
if (vol.getParent() != null
|
||||
&& vol.getParent().getParent() instanceof Pool) {
|
||||
// Pool volumes are not contiguous so printing a range of blocks is inaccurate
|
||||
tempVolName = volName + " (" + vol.getDescription() + ": " + vol.getStart() + ")";
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error looking up parent(s) of volume with obj ID = " + vol.getId(), ex);
|
||||
}
|
||||
return tempVolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content that should be displayed in the table based on the given object.
|
||||
* Algorithm:
|
||||
* - If content is already displayable, return it
|
||||
* - If content is a volume system, return its displayable children
|
||||
* - If content is a file system, return the displayable children of the root folder
|
||||
* - If content is the root folder, return the displayable children of the root folder
|
||||
*
|
||||
* @param content The base content.
|
||||
*
|
||||
* @return List of content to add to the table.
|
||||
*/
|
||||
static List<Content> getNextDisplayableContent(Content content) throws TskCoreException {
|
||||
|
||||
// If the given content is displayable, return it
|
||||
if (FileSystemColumnUtils.isDisplayable(content)) {
|
||||
return Arrays.asList(content);
|
||||
}
|
||||
|
||||
List<Content> contentToDisplay = new ArrayList<>();
|
||||
if (content instanceof VolumeSystem) {
|
||||
// Return all children that can be displayed
|
||||
VolumeSystem vs = (VolumeSystem)content;
|
||||
for (Content child : vs.getChildren()) {
|
||||
if (isDisplayable(child)) {
|
||||
contentToDisplay.add(child);
|
||||
}
|
||||
}
|
||||
} else if (content instanceof FileSystem) {
|
||||
// Return the children of the root node
|
||||
FileSystem fs = (FileSystem)content;
|
||||
for (Content child : fs.getRootDirectory().getChildren()) {
|
||||
if (isDisplayable(child)) {
|
||||
contentToDisplay.add(child);
|
||||
}
|
||||
}
|
||||
} else if (content instanceof AbstractFile) {
|
||||
if (((AbstractFile) content).isRoot()) {
|
||||
// If we have the root folder, skip it and display the children
|
||||
for (Content child : content.getChildren()) {
|
||||
if (isDisplayable(child)) {
|
||||
contentToDisplay.add(child);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Arrays.asList(content);
|
||||
}
|
||||
}
|
||||
|
||||
return contentToDisplay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a column key from a string.
|
||||
*
|
||||
* @param name The column name
|
||||
*
|
||||
* @return The column key
|
||||
*/
|
||||
private static ColumnKey getColumnKey(String name) {
|
||||
return new ColumnKey(name, name, Bundle.FileSystemColumnUtils_noDescription());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> 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 content object in order to retrieve data from DAO.
|
||||
*/
|
||||
public class FileSystemContentSearchParam {
|
||||
private final Long contentObjectId;
|
||||
|
||||
public FileSystemContentSearchParam(Long contentObjectId) {
|
||||
this.contentObjectId = contentObjectId;
|
||||
}
|
||||
|
||||
public Long getContentObjectId() {
|
||||
return contentObjectId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 67 * hash + Objects.hashCode(this.contentObjectId);
|
||||
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 FileSystemContentSearchParam other = (FileSystemContentSearchParam) obj;
|
||||
if (!Objects.equals(this.contentObjectId, other.contentObjectId)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> 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 com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class FileSystemDAO {
|
||||
private static final int CACHE_SIZE = 15; // rule of thumb: 5 entries times number of cached SearchParams sub-types
|
||||
private static final long CACHE_DURATION = 2;
|
||||
private static final TimeUnit CACHE_DURATION_UNITS = TimeUnit.MINUTES;
|
||||
private final Cache<SearchParams<?>, BaseSearchResultsDTO> searchParamsCache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).expireAfterAccess(CACHE_DURATION, CACHE_DURATION_UNITS).build();
|
||||
|
||||
private static final String FILE_SYSTEM_TYPE_ID = "FILE_SYSTEM";
|
||||
|
||||
private static FileSystemDAO instance = null;
|
||||
|
||||
synchronized static FileSystemDAO getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new FileSystemDAO();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private BaseSearchResultsDTO fetchContentForTableFromContent(SearchParams<FileSystemContentSearchParam> cacheKey) throws NoCurrentCaseException, TskCoreException {
|
||||
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
|
||||
Long objectId = cacheKey.getParamData().getContentObjectId();
|
||||
List<Content> contentForTable = new ArrayList<>();
|
||||
String parentName = "";
|
||||
Content parentContent = skCase.getContentById(objectId);
|
||||
if (parentContent == null) {
|
||||
throw new TskCoreException("Error loading children of object with ID " + objectId);
|
||||
}
|
||||
|
||||
parentName = parentContent.getName();
|
||||
for (Content content : parentContent.getChildren()) {
|
||||
contentForTable.addAll(FileSystemColumnUtils.getNextDisplayableContent(content));
|
||||
}
|
||||
|
||||
return fetchContentForTable(cacheKey, contentForTable, parentName);
|
||||
}
|
||||
|
||||
private BaseSearchResultsDTO fetchContentForTableFromHost(SearchParams<FileSystemHostSearchParam> cacheKey) throws NoCurrentCaseException, TskCoreException {
|
||||
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
|
||||
Long objectId = cacheKey.getParamData().getHostObjectId();
|
||||
List<Content> contentForTable = new ArrayList<>();
|
||||
String parentName = "";
|
||||
Optional<Host> host = skCase.getHostManager().getHostById(objectId);
|
||||
if (host.isPresent()) {
|
||||
parentName = host.get().getName();
|
||||
contentForTable.addAll(skCase.getHostManager().getDataSourcesForHost(host.get()));
|
||||
} else {
|
||||
throw new TskCoreException("Error loading host with ID " + objectId);
|
||||
}
|
||||
return fetchContentForTable(cacheKey, contentForTable, parentName);
|
||||
}
|
||||
|
||||
|
||||
private BaseSearchResultsDTO fetchContentForTable(SearchParams<?> cacheKey, List<Content> contentForTable,
|
||||
String parentName) throws NoCurrentCaseException, TskCoreException {
|
||||
// Ensure consistent columns for each page by doing this before paging
|
||||
List<FileSystemColumnUtils.ContentType> displayableTypes = FileSystemColumnUtils.getDisplayableTypesForContentList(contentForTable);
|
||||
|
||||
List<Content> pagedContent = getPaged(contentForTable, cacheKey);
|
||||
List<ColumnKey> columnKeys = FileSystemColumnUtils.getColumnKeysForContent(displayableTypes);
|
||||
|
||||
List<RowDTO> rows = new ArrayList<>();
|
||||
for (Content content : pagedContent) {
|
||||
List<Object> cellValues = FileSystemColumnUtils.getCellValuesForContent(content, displayableTypes);
|
||||
rows.add(new BaseRowDTO(cellValues, FILE_SYSTEM_TYPE_ID, content.getId()));
|
||||
}
|
||||
return new BaseSearchResultsDTO(FILE_SYSTEM_TYPE_ID, parentName, columnKeys, rows, cacheKey.getStartItem(), contentForTable.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of paged content.
|
||||
*
|
||||
* @param contentObjects The content objects.
|
||||
* @param searchParams The search parameters including the paging.
|
||||
*
|
||||
* @return The list of paged artifacts.
|
||||
*/
|
||||
private List<Content> getPaged(List<? extends Content> contentObjects, SearchParams<?> searchParams) {
|
||||
Stream<? extends Content> pagedArtsStream = contentObjects.stream()
|
||||
.sorted(Comparator.comparing((conent) -> conent.getId()))
|
||||
.skip(searchParams.getStartItem());
|
||||
|
||||
if (searchParams.getMaxResultsCount() != null) {
|
||||
pagedArtsStream = pagedArtsStream.limit(searchParams.getMaxResultsCount());
|
||||
}
|
||||
|
||||
return pagedArtsStream.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public BaseSearchResultsDTO getContentForTable(FileSystemContentSearchParam objectKey, long startItem, Long maxCount, boolean hardRefresh) throws ExecutionException, IllegalArgumentException {
|
||||
|
||||
SearchParams<FileSystemContentSearchParam> searchParams = new SearchParams<>(objectKey, startItem, maxCount);
|
||||
if (hardRefresh) {
|
||||
searchParamsCache.invalidate(searchParams);
|
||||
}
|
||||
|
||||
return searchParamsCache.get(searchParams, () -> fetchContentForTableFromContent(searchParams));
|
||||
}
|
||||
|
||||
public BaseSearchResultsDTO getContentForTable(FileSystemHostSearchParam objectKey, long startItem, Long maxCount, boolean hardRefresh) throws ExecutionException, IllegalArgumentException {
|
||||
|
||||
SearchParams<FileSystemHostSearchParam> searchParams = new SearchParams<>(objectKey, startItem, maxCount);
|
||||
if (hardRefresh) {
|
||||
searchParamsCache.invalidate(searchParams);
|
||||
}
|
||||
|
||||
return searchParamsCache.get(searchParams, () -> fetchContentForTableFromHost(searchParams));
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> 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 content object in order to retrieve data from DAO.
|
||||
*/
|
||||
public class FileSystemHostSearchParam {
|
||||
private final Long hostObjectId;
|
||||
|
||||
public FileSystemHostSearchParam(Long hostObjectId) {
|
||||
this.hostObjectId = hostObjectId;
|
||||
}
|
||||
|
||||
public Long getHostObjectId() {
|
||||
return hostObjectId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 67 * hash + Objects.hashCode(this.hostObjectId);
|
||||
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 FileSystemHostSearchParam other = (FileSystemHostSearchParam) obj;
|
||||
if (!Objects.equals(this.hostObjectId, other.hostObjectId)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ public class MainDAO {
|
||||
private final DataArtifactDAO dataArtifactDAO = DataArtifactDAO.getInstance();
|
||||
private final AnalysisResultDAO analysisResultDAO = AnalysisResultDAO.getInstance();
|
||||
private final ViewsDAO viewsDAO = ViewsDAO.getInstance();
|
||||
private final FileSystemDAO fileSystemDAO = FileSystemDAO.getInstance();
|
||||
private final TagsDAO tagsDAO = TagsDAO.getInstance();
|
||||
|
||||
public DataArtifactDAO getDataArtifactsDAO() {
|
||||
@ -51,6 +52,10 @@ public class MainDAO {
|
||||
return viewsDAO;
|
||||
}
|
||||
|
||||
public FileSystemDAO getFileSystemDAO() {
|
||||
return fileSystemDAO;
|
||||
}
|
||||
|
||||
public TagsDAO getTagsDAO() {
|
||||
return tagsDAO;
|
||||
}
|
||||
|
@ -50,33 +50,6 @@ import org.sleuthkit.datamodel.TskData;
|
||||
* Provides information to populate the results viewer for data in the views
|
||||
* section.
|
||||
*/
|
||||
@Messages({"ThreePanelViewsDAO.fileColumns.nameColLbl=Name",
|
||||
"ThreePanelViewsDAO.fileColumns.originalName=Original Name",
|
||||
"ThreePanelViewsDAO.fileColumns.scoreName=S",
|
||||
"ThreePanelViewsDAO.fileColumns.commentName=C",
|
||||
"ThreePanelViewsDAO.fileColumns.countName=O",
|
||||
"ThreePanelViewsDAO.fileColumns.locationColLbl=Location",
|
||||
"ThreePanelViewsDAO.fileColumns.modifiedTimeColLbl=Modified Time",
|
||||
"ThreePanelViewsDAO.fileColumns.changeTimeColLbl=Change Time",
|
||||
"ThreePanelViewsDAO.fileColumns.accessTimeColLbl=Access Time",
|
||||
"ThreePanelViewsDAO.fileColumns.createdTimeColLbl=Created Time",
|
||||
"ThreePanelViewsDAO.fileColumns.sizeColLbl=Size",
|
||||
"ThreePanelViewsDAO.fileColumns.flagsDirColLbl=Flags(Dir)",
|
||||
"ThreePanelViewsDAO.fileColumns.flagsMetaColLbl=Flags(Meta)",
|
||||
"ThreePanelViewsDAO.fileColumns.modeColLbl=Mode",
|
||||
"ThreePanelViewsDAO.fileColumns.useridColLbl=UserID",
|
||||
"ThreePanelViewsDAO.fileColumns.groupidColLbl=GroupID",
|
||||
"ThreePanelViewsDAO.fileColumns.metaAddrColLbl=Meta Addr.",
|
||||
"ThreePanelViewsDAO.fileColumns.attrAddrColLbl=Attr. Addr.",
|
||||
"ThreePanelViewsDAO.fileColumns.typeDirColLbl=Type(Dir)",
|
||||
"ThreePanelViewsDAO.fileColumns.typeMetaColLbl=Type(Meta)",
|
||||
"ThreePanelViewsDAO.fileColumns.knownColLbl=Known",
|
||||
"ThreePanelViewsDAO.fileColumns.md5HashColLbl=MD5 Hash",
|
||||
"ThreePanelViewsDAO.fileColumns.sha256HashColLbl=SHA-256 Hash",
|
||||
"ThreePanelViewsDAO.fileColumns.objectId=Object ID",
|
||||
"ThreePanelViewsDAO.fileColumns.mimeType=MIME Type",
|
||||
"ThreePanelViewsDAO.fileColumns.extensionColLbl=Extension",
|
||||
"ThreePanelViewsDAO.fileColumns.noDescription=No Description"})
|
||||
public class ViewsDAO {
|
||||
|
||||
private static final int CACHE_SIZE = 15; // rule of thumb: 5 entries times number of cached SearchParams sub-types
|
||||
@ -86,34 +59,6 @@ public class ViewsDAO {
|
||||
|
||||
private static final String FILE_VIEW_EXT_TYPE_ID = "FILE_VIEW_BY_EXT";
|
||||
|
||||
private static final List<ColumnKey> FILE_COLUMNS = Arrays.asList(
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_nameColLbl()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_originalName()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_scoreName()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_commentName()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_countName()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_locationColLbl()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_modifiedTimeColLbl()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_changeTimeColLbl()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_accessTimeColLbl()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_createdTimeColLbl()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_sizeColLbl()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_flagsDirColLbl()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_flagsMetaColLbl()),
|
||||
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_modeColLbl()),
|
||||
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_useridColLbl()),
|
||||
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_groupidColLbl()),
|
||||
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_metaAddrColLbl()),
|
||||
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_attrAddrColLbl()),
|
||||
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_typeDirColLbl()),
|
||||
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_typeMetaColLbl()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_knownColLbl()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_md5HashColLbl()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_sha256HashColLbl()),
|
||||
// getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_objectId()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_mimeType()),
|
||||
getFileColumnKey(Bundle.ThreePanelViewsDAO_fileColumns_extensionColLbl()));
|
||||
|
||||
private static ViewsDAO instance = null;
|
||||
|
||||
synchronized static ViewsDAO getInstance() {
|
||||
@ -124,10 +69,6 @@ public class ViewsDAO {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static ColumnKey getFileColumnKey(String name) {
|
||||
return new ColumnKey(name, name, Bundle.ThreePanelViewsDAO_fileColumns_noDescription());
|
||||
}
|
||||
|
||||
static ExtensionMediaType getExtensionMediaType(String ext) {
|
||||
if (StringUtils.isBlank(ext)) {
|
||||
return ExtensionMediaType.UNCATEGORIZED;
|
||||
@ -374,38 +315,7 @@ public class ViewsDAO {
|
||||
List<RowDTO> fileRows = new ArrayList<>();
|
||||
for (AbstractFile file : files) {
|
||||
|
||||
List<Object> cellValues = Arrays.asList(
|
||||
file.getName(), // GVDTODO handle . and .. from getContentDisplayName()
|
||||
// GVDTODO translation column
|
||||
null,
|
||||
//GVDTDO replace nulls with SCO
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
file.getUniquePath(),
|
||||
TimeZoneUtils.getFormattedTime(file.getMtime()),
|
||||
TimeZoneUtils.getFormattedTime(file.getCtime()),
|
||||
TimeZoneUtils.getFormattedTime(file.getAtime()),
|
||||
TimeZoneUtils.getFormattedTime(file.getCrtime()),
|
||||
file.getSize(),
|
||||
file.getDirFlagAsString(),
|
||||
file.getMetaFlagsAsString(),
|
||||
// mode,
|
||||
// userid,
|
||||
// groupid,
|
||||
// metaAddr,
|
||||
// attrAddr,
|
||||
// typeDir,
|
||||
// typeMeta,
|
||||
|
||||
file.getKnown().getName(),
|
||||
StringUtils.defaultString(file.getMd5Hash()),
|
||||
StringUtils.defaultString(file.getSha256Hash()),
|
||||
// objectId,
|
||||
|
||||
StringUtils.defaultString(file.getMIMEType()),
|
||||
file.getNameExtension()
|
||||
);
|
||||
List<Object> cellValues = FileSystemColumnUtils.getCellValuesForAbstractFile(file);
|
||||
|
||||
fileRows.add(new FileRowDTO(
|
||||
file,
|
||||
@ -418,7 +328,7 @@ public class ViewsDAO {
|
||||
cellValues));
|
||||
}
|
||||
|
||||
return new BaseSearchResultsDTO(FILE_VIEW_EXT_TYPE_ID, displayName, FILE_COLUMNS, fileRows, startItem, totalResultsCount);
|
||||
return new BaseSearchResultsDTO(FILE_VIEW_EXT_TYPE_ID, displayName, FileSystemColumnUtils.getColumnKeysForAbstractfile(), fileRows, startItem, totalResultsCount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,6 +37,7 @@ import org.sleuthkit.autopsy.testutils.CaseUtils;
|
||||
import org.sleuthkit.autopsy.testutils.TestUtilsException;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.AnalysisResult;
|
||||
import org.sleuthkit.datamodel.Attribute;
|
||||
import org.sleuthkit.datamodel.Blackboard;
|
||||
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@ -44,11 +45,19 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DataArtifact;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.FileSystem;
|
||||
import org.sleuthkit.datamodel.FsContent;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.Pool;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.Score;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.Volume;
|
||||
import org.sleuthkit.datamodel.VolumeSystem;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
@ -139,6 +148,19 @@ public class TableSearchTest extends NbTestCase {
|
||||
AnalysisResult keywordHitAnalysisResult = null; // A keyword hit
|
||||
Content keywordHitSource = null; // The source of the keyword hit above
|
||||
|
||||
// File system test
|
||||
Host fsTestHostA = null; // A host
|
||||
Image fsTestImageA = null; // An image
|
||||
VolumeSystem fsTestVsA = null; // A volume system
|
||||
Volume fsTestVolumeA1 = null; // A volume
|
||||
Volume fsTestVolumeA2 = null; // Another volume
|
||||
Volume fsTestVolumeA3 = null; // Another volume
|
||||
FileSystem fsTestFsA = null; // A file system
|
||||
AbstractFile fsTestRootDirA = null; // The root directory
|
||||
Image fsTestImageB = null; // Another image
|
||||
Volume fsTestVolumeB1 = null; // Another volume
|
||||
Pool fsTestPoolB = null; // A pool
|
||||
|
||||
// Tags test
|
||||
TagName knownTag1 = null;
|
||||
TagName tag2 = null;
|
||||
@ -167,6 +189,7 @@ public class TableSearchTest extends NbTestCase {
|
||||
mimeSearchTest();
|
||||
extensionSearchTest();
|
||||
sizeSearchTest();
|
||||
fileSystemTest();
|
||||
tagsTest();
|
||||
}
|
||||
|
||||
@ -174,6 +197,7 @@ public class TableSearchTest extends NbTestCase {
|
||||
* Create a case and add sample data.
|
||||
*/
|
||||
private void setUpCaseDatabase() {
|
||||
SleuthkitCase.CaseDbTransaction trans = null;
|
||||
try {
|
||||
// Create a test case
|
||||
openCase = CaseUtils.createAsCurrentCase("testTableSearchCase");
|
||||
@ -182,11 +206,12 @@ public class TableSearchTest extends NbTestCase {
|
||||
tagsManager = openCase.getServices().getTagsManager();
|
||||
|
||||
// Add two logical files data sources
|
||||
SleuthkitCase.CaseDbTransaction trans = db.beginTransaction();
|
||||
trans = db.beginTransaction();
|
||||
dataSource1 = db.addLocalFilesDataSource("devId1", "C:\\Fake\\Path\\1", "EST", null, trans);
|
||||
dataSource2 = db.addLocalFilesDataSource("devId2", "C:\\Fake\\Path\\2", "EST", null, trans);
|
||||
dataSource3 = db.addLocalFilesDataSource("devId3", "C:\\Fake\\Path\\3", "EST", null, trans);
|
||||
trans.commit();
|
||||
trans = null;
|
||||
|
||||
// Add files
|
||||
AbstractFile folderA1 = db.addLocalDirectory(dataSource1.getId(), "folder1");
|
||||
@ -341,6 +366,76 @@ public class TableSearchTest extends NbTestCase {
|
||||
null, KEYWORD_SET_1, null, attrs).getAnalysisResult();
|
||||
keywordHitSource = hashHitAnalysisResult;
|
||||
|
||||
// Create a normal image
|
||||
// fsTestImageA (Host: fsTestHostA)
|
||||
// - fsTestVsA
|
||||
// -- fsTestVolumeA1
|
||||
// --- fsTestFsA
|
||||
// ---- fsTestRootDirA
|
||||
// ----- (3 files)
|
||||
// -- fsTestVolumeA2
|
||||
// -- fsTestVolumeA3
|
||||
fsTestHostA = db.getHostManager().newHost("File system test host");
|
||||
trans = db.beginTransaction();
|
||||
fsTestImageA = db.addImage(TskData.TSK_IMG_TYPE_ENUM.TSK_IMG_TYPE_DETECT, 512, 1024, "image1", Arrays.asList("C:\\Fake\\Path\\4"),
|
||||
"EST", null, null, null, "deviceID12345", fsTestHostA, trans);
|
||||
fsTestVsA = db.addVolumeSystem(fsTestImageA.getId(), TskData.TSK_VS_TYPE_ENUM.TSK_VS_TYPE_DOS, 0, 1024, trans);
|
||||
fsTestVolumeA1 = db.addVolume(fsTestVsA.getId(), 0, 0, 512, "Test vol A1", 0, trans);
|
||||
fsTestVolumeA2 = db.addVolume(fsTestVsA.getId(), 1, 512, 512, "Test vol A2", 0, trans);
|
||||
fsTestVolumeA3 = db.addVolume(fsTestVsA.getId(), 2, 1024, 512, "Test vol A3", 0, trans);
|
||||
long rootInum = 1;
|
||||
fsTestFsA = db.addFileSystem(fsTestVolumeA1.getId(), 0, TskData.TSK_FS_TYPE_ENUM.TSK_FS_TYPE_EXT2, 512, 1,
|
||||
rootInum, rootInum, 10, "Test file system", trans);
|
||||
trans.commit();
|
||||
trans = null;
|
||||
fsTestRootDirA = db.addFileSystemFile(fsTestImageA.getId(), fsTestFsA.getId(),
|
||||
"", rootInum, 0,
|
||||
TskData.TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
|
||||
TskData.TSK_FS_NAME_FLAG_ENUM.ALLOC, (short)0, 0,
|
||||
0, 0, 0, 0, false, fsTestFsA);
|
||||
db.addFileSystemFile(fsTestImageA.getId(), fsTestFsA.getId(),
|
||||
"Test file 1", 0, 0,
|
||||
TskData.TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
|
||||
TskData.TSK_FS_NAME_FLAG_ENUM.ALLOC, (short)0, 123,
|
||||
0, 0, 0, 0, true, fsTestRootDirA);
|
||||
db.addFileSystemFile(fsTestImageA.getId(), fsTestFsA.getId(),
|
||||
"Test file 2", 0, 0,
|
||||
TskData.TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
|
||||
TskData.TSK_FS_NAME_FLAG_ENUM.ALLOC, (short)0, 456,
|
||||
0, 0, 0, 0, true, fsTestRootDirA);
|
||||
db.addFileSystemFile(fsTestImageA.getId(), fsTestFsA.getId(),
|
||||
"Test file 3", 0, 0,
|
||||
TskData.TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
|
||||
TskData.TSK_FS_NAME_FLAG_ENUM.ALLOC, (short)0, 789,
|
||||
0, 0, 0, 0, true, fsTestRootDirA);
|
||||
|
||||
// Create an image with some odd structures for testing
|
||||
trans = db.beginTransaction();
|
||||
fsTestImageB = db.addImage(TskData.TSK_IMG_TYPE_ENUM.TSK_IMG_TYPE_DETECT, 512, 1024, "image2", Arrays.asList("C:\\Fake\\Path\\5"),
|
||||
"EST", null, null, null, "deviceID678", fsTestHostA, trans);
|
||||
|
||||
// Images can have VS, pool, FS, file, artifact, and report children.
|
||||
// Add a VS, pool, and local file
|
||||
VolumeSystem vsB = db.addVolumeSystem(fsTestImageB.getId(), TskData.TSK_VS_TYPE_ENUM.TSK_VS_TYPE_BSD, 0, 2048, trans);
|
||||
db.addPool(fsTestImageB.getId(), TskData.TSK_POOL_TYPE_ENUM.TSK_POOL_TYPE_APFS, trans);
|
||||
db.addLocalFile("Test local file B1", "C:\\Fake\\Path\\6", 6000, 0, 0, 0, 0,
|
||||
true, TskData.EncodingType.NONE, fsTestImageB, trans);
|
||||
|
||||
// Volumes can have pool, FS, file, and artifact children
|
||||
fsTestVolumeB1 = db.addVolume(vsB.getId(), 0, 0, 512, "Test vol B1", 0, trans);
|
||||
fsTestPoolB = db.addPool(fsTestVolumeB1.getId(), TskData.TSK_POOL_TYPE_ENUM.TSK_POOL_TYPE_APFS, trans);
|
||||
db.addLocalFile("Test local file B2", "C:\\Fake\\Path\\7", 7000, 0, 0, 0, 0,
|
||||
true, TskData.EncodingType.NONE, fsTestVolumeB1, trans);
|
||||
|
||||
// Pools can have VS, file, and artifact children
|
||||
VolumeSystem vsB2 = db.addVolumeSystem(fsTestPoolB.getId(), TskData.TSK_VS_TYPE_ENUM.TSK_VS_TYPE_GPT, 0, 2048, trans);
|
||||
db.addVolume(vsB2.getId(), 0, 0, 512, "Test vol B2", 0, trans);
|
||||
db.addLocalFile("Test local file B3", "C:\\Fake\\Path\\8", 8000, 0, 0, 0, 0,
|
||||
true, TskData.EncodingType.NONE, fsTestPoolB, trans);
|
||||
|
||||
trans.commit();
|
||||
trans = null;
|
||||
|
||||
// Add tags ----
|
||||
knownTag1 = tagsManager.addTagName("Tag 1", TAG_DESCRIPTION, TagName.HTML_COLOR.RED, TskData.FileKnown.KNOWN);
|
||||
tag2 = tagsManager.addTagName("Tag 2", "Descrition");
|
||||
@ -360,6 +455,13 @@ public class TableSearchTest extends NbTestCase {
|
||||
openCase.getServices().getTagsManager().addContentTag(customFile, knownTag1);
|
||||
|
||||
} catch (TestUtilsException | TskCoreException | BlackboardException | TagsManager.TagNameAlreadyExistsException ex) {
|
||||
if (trans != null) {
|
||||
try {
|
||||
trans.rollback();
|
||||
} catch (TskCoreException ex2) {
|
||||
Exceptions.printStackTrace(ex2);
|
||||
}
|
||||
}
|
||||
Exceptions.printStackTrace(ex);
|
||||
Assert.fail(ex.getMessage());
|
||||
}
|
||||
@ -878,6 +980,78 @@ public class TableSearchTest extends NbTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private void fileSystemTest() {
|
||||
// Quick test that everything is initialized
|
||||
assertTrue(db != null);
|
||||
|
||||
try {
|
||||
FileSystemDAO fileSystemDAO = MainDAO.getInstance().getFileSystemDAO();
|
||||
|
||||
// HostA is associated with two images
|
||||
FileSystemHostSearchParam hostParam = new FileSystemHostSearchParam(fsTestHostA.getHostId());
|
||||
BaseSearchResultsDTO results = fileSystemDAO.getContentForTable(hostParam, 0, null, false);
|
||||
assertEquals(2, results.getTotalResultsCount());
|
||||
assertEquals(2, results.getItems().size());
|
||||
|
||||
// ImageA has one volume system child, which has three volumes that will be displayed
|
||||
FileSystemContentSearchParam param = new FileSystemContentSearchParam(fsTestImageA.getId());
|
||||
results = fileSystemDAO.getContentForTable(param, 0, null, false);
|
||||
assertEquals(3, results.getTotalResultsCount());
|
||||
assertEquals(3, results.getItems().size());
|
||||
|
||||
// VsA has three volume children (this should match the previous search)
|
||||
param = new FileSystemContentSearchParam(fsTestVsA.getId());
|
||||
results = fileSystemDAO.getContentForTable(param, 0, null, false);
|
||||
assertEquals(3, results.getTotalResultsCount());
|
||||
assertEquals(3, results.getItems().size());
|
||||
|
||||
// VolumeA1 has a file system child, which in turn has a root directory child with three file children
|
||||
param = new FileSystemContentSearchParam(fsTestVolumeA1.getId());
|
||||
results = fileSystemDAO.getContentForTable(param, 0, null, false);
|
||||
assertEquals(3, results.getTotalResultsCount());
|
||||
assertEquals(3, results.getItems().size());
|
||||
|
||||
// FsA has a root directory child with three file children (this should match the previous search)
|
||||
param = new FileSystemContentSearchParam(fsTestFsA.getId());
|
||||
results = fileSystemDAO.getContentForTable(param, 0, null, false);
|
||||
assertEquals(3, results.getTotalResultsCount());
|
||||
assertEquals(3, results.getItems().size());
|
||||
|
||||
// The root dir contains three files
|
||||
param = new FileSystemContentSearchParam(fsTestRootDirA.getId());
|
||||
results = fileSystemDAO.getContentForTable(param, 0, null, false);
|
||||
assertEquals(3, results.getTotalResultsCount());
|
||||
assertEquals(3, results.getItems().size());
|
||||
|
||||
// ImageB has VS (which will display one volume), pool, and one local file children
|
||||
param = new FileSystemContentSearchParam(fsTestImageB.getId());
|
||||
results = fileSystemDAO.getContentForTable(param, 0, null, false);
|
||||
assertEquals(3, results.getTotalResultsCount());
|
||||
assertEquals(3, results.getItems().size());
|
||||
|
||||
// Check that we have the "Type" column from the Pool and the "Known" column from the file
|
||||
List<String> columnDisplayNames = results.getColumns().stream().map(p -> p.getDisplayName()).collect(Collectors.toList());
|
||||
assertTrue(columnDisplayNames.contains("Type"));
|
||||
assertTrue(columnDisplayNames.contains("Known"));
|
||||
|
||||
// fsTestVolumeB1 has pool and one local file children
|
||||
param = new FileSystemContentSearchParam(fsTestVolumeB1.getId());
|
||||
results = fileSystemDAO.getContentForTable(param, 0, null, false);
|
||||
assertEquals(2, results.getTotalResultsCount());
|
||||
assertEquals(2, results.getItems().size());
|
||||
|
||||
// fsTestPoolB has VS (which will display one volume) and local file children
|
||||
param = new FileSystemContentSearchParam(fsTestPoolB.getId());
|
||||
results = fileSystemDAO.getContentForTable(param, 0, null, false);
|
||||
assertEquals(2, results.getTotalResultsCount());
|
||||
assertEquals(2, results.getItems().size());
|
||||
|
||||
} catch (ExecutionException ex) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
Assert.fail(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private enum CustomRootFilter implements FileExtSearchFilter {
|
||||
|
||||
CUSTOM_FILTER(0, "CUSTOM_FILTER", "Test", CUSTOM_EXTENSIONS), //NON-NLS
|
||||
|
Loading…
x
Reference in New Issue
Block a user