mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 08:56:15 +00:00
1917-Comments added and other minor clean up, emptyNode removed
This commit is contained in:
parent
f45b0b642b
commit
e8f7029a0f
@ -143,13 +143,11 @@ public interface DisplayableItemNodeVisitor<T> {
|
|||||||
|
|
||||||
T visit(FileTypes.FileTypesNode fileTypes);
|
T visit(FileTypes.FileTypesNode fileTypes);
|
||||||
|
|
||||||
T visit(FileTypesByMimeType.FileTypesByMimeTypeNode aThis);
|
T visit(FileTypesByMimeType.FileTypesByMimeTypeNode ftByMimeTypeNode);
|
||||||
|
|
||||||
T visit(FileTypesByMimeType.MediaTypeNode aThis);
|
T visit(FileTypesByMimeType.MediaTypeNode ftByMimeTypeMediaType);
|
||||||
|
|
||||||
T visit(FileTypesByMimeType.MediaSubTypeNode aThis);
|
T visit(FileTypesByMimeType.MediaSubTypeNode ftByMimeTypeMediaSubType);
|
||||||
|
|
||||||
T visit(FileTypesByMimeType.EmptyNode aThis);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -208,22 +206,21 @@ public interface DisplayableItemNodeVisitor<T> {
|
|||||||
public T visit(FileTypeByExtNode fsfn) {
|
public T visit(FileTypeByExtNode fsfn) {
|
||||||
return defaultVisit(fsfn);
|
return defaultVisit(fsfn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T visit(FileTypesByMimeType.FileTypesByMimeTypeNode ftByMimeTypeNode) {
|
public T visit(FileTypesByMimeType.FileTypesByMimeTypeNode ftByMimeTypeNode) {
|
||||||
return defaultVisit(ftByMimeTypeNode);
|
return defaultVisit(ftByMimeTypeNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T visit(FileTypesByMimeType.MediaTypeNode ftByMimeTypeMediaTypeNode) {
|
public T visit(FileTypesByMimeType.MediaTypeNode ftByMimeTypeMediaTypeNode) {
|
||||||
return defaultVisit(ftByMimeTypeMediaTypeNode);
|
return defaultVisit(ftByMimeTypeMediaTypeNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T visit(FileTypesByMimeType.MediaSubTypeNode ftByMimeTypeMediaTypeNode) {
|
public T visit(FileTypesByMimeType.MediaSubTypeNode ftByMimeTypeMediaTypeNode) {
|
||||||
return defaultVisit(ftByMimeTypeMediaTypeNode);
|
return defaultVisit(ftByMimeTypeMediaTypeNode);
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
public T visit(FileTypesByMimeType.EmptyNode ftByMimeTypeEmptyNode) {
|
|
||||||
return defaultVisit(ftByMimeTypeEmptyNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T visit(DeletedContentNode dcn) {
|
public T visit(DeletedContentNode dcn) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2014 Basis Technology Corp.
|
* Copyright 2011-2016 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -26,7 +26,7 @@ import org.sleuthkit.autopsy.datamodel.accounts.FileTypeExtensionFilters;
|
|||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Results node support
|
* File Types node support
|
||||||
*/
|
*/
|
||||||
public class FileTypes implements AutopsyVisitableItem {
|
public class FileTypes implements AutopsyVisitableItem {
|
||||||
|
|
||||||
@ -46,8 +46,7 @@ public class FileTypes implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Node which will contain By Mime Type and By Extension nodes.
|
||||||
* @author wschaefer
|
|
||||||
*/
|
*/
|
||||||
public static class FileTypesNode extends DisplayableItemNode {
|
public static class FileTypesNode extends DisplayableItemNode {
|
||||||
|
|
||||||
|
@ -51,9 +51,11 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Node for Root of the 'By Mime Type' view located in the File Types view,
|
* Class which contains the Nodes for the 'By Mime Type' view located in the
|
||||||
* shows all files with a mime type. Will intially be empty until file type
|
* File Types view, shows all files with a mime type. Will initially be empty
|
||||||
* identification has been performed.
|
* until file type identification has been performed. Contains a Property Change
|
||||||
|
* Listener which is checking for changes in IngestJobEvent Completed or
|
||||||
|
* Cancelled and IngestModuleEvent Content Changed.
|
||||||
*/
|
*/
|
||||||
class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
||||||
|
|
||||||
@ -96,6 +98,12 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the media types by retrieving the keyset from the hashmap.
|
||||||
|
*
|
||||||
|
* @return mediaTypes - a list of strings representing all distinct media
|
||||||
|
* types of files for this case
|
||||||
|
*/
|
||||||
private List<String> getMediaTypeList() {
|
private List<String> getMediaTypeList() {
|
||||||
synchronized (existingMimeTypes) {
|
synchronized (existingMimeTypes) {
|
||||||
List<String> mediaTypes = new ArrayList<>(existingMimeTypes.keySet());
|
List<String> mediaTypes = new ArrayList<>(existingMimeTypes.keySet());
|
||||||
@ -104,6 +112,10 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the query on the database to get all distinct MIME types of
|
||||||
|
* files in it, and populate the hashmap with those results.
|
||||||
|
*/
|
||||||
private void populateHashMap() {
|
private void populateHashMap() {
|
||||||
StringBuilder allDistinctMimeTypesQuery = new StringBuilder();
|
StringBuilder allDistinctMimeTypesQuery = new StringBuilder();
|
||||||
allDistinctMimeTypesQuery.append("SELECT DISTINCT mime_type from tsk_files where mime_type NOT null");
|
allDistinctMimeTypesQuery.append("SELECT DISTINCT mime_type from tsk_files where mime_type NOT null");
|
||||||
@ -112,11 +124,11 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
existingMimeTypes.clear();
|
existingMimeTypes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skCase == null) {
|
if (getSleuthkitCase() == null) {
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery(allDistinctMimeTypesQuery.toString())) {
|
try (SleuthkitCase.CaseDbQuery dbQuery = getSleuthkitCase().executeQuery(allDistinctMimeTypesQuery.toString())) {
|
||||||
ResultSet resultSet = dbQuery.getResultSet();
|
ResultSet resultSet = dbQuery.getResultSet();
|
||||||
synchronized (existingMimeTypes) {
|
synchronized (existingMimeTypes) {
|
||||||
while (resultSet.next()) {
|
while (resultSet.next()) {
|
||||||
@ -142,11 +154,12 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
FileTypesByMimeType(SleuthkitCase skCase) {
|
FileTypesByMimeType(SleuthkitCase skCase) {
|
||||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||||
// Case.addPropertyChangeListener(pcl);
|
|
||||||
// populateHashMap();
|
|
||||||
FileTypesByMimeType.skCase = skCase;
|
FileTypesByMimeType.skCase = skCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return skCase - the sluethkit case
|
||||||
|
*/
|
||||||
SleuthkitCase getSleuthkitCase() {
|
SleuthkitCase getSleuthkitCase() {
|
||||||
return skCase;
|
return skCase;
|
||||||
}
|
}
|
||||||
@ -156,13 +169,19 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
return v.visit(this);
|
return v.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class which represents the root node of the "By MIME Type" tree, will
|
||||||
|
* have children of each media type present in the database or no children
|
||||||
|
* when the file detection module has not been run and MIME type is
|
||||||
|
* currently unknown.
|
||||||
|
*/
|
||||||
class FileTypesByMimeTypeNode extends DisplayableItemNode {
|
class FileTypesByMimeTypeNode extends DisplayableItemNode {
|
||||||
|
|
||||||
@NbBundle.Messages("FileTypesByMimeType.name.text=By MIME Type")
|
@NbBundle.Messages("FileTypesByMimeType.name.text=By MIME Type")
|
||||||
final String NAME = Bundle.FileTypesByMimeType_name_text();
|
final String NAME = Bundle.FileTypesByMimeType_name_text();
|
||||||
|
|
||||||
FileTypesByMimeTypeNode(SleuthkitCase sleuthkitCase) {
|
FileTypesByMimeTypeNode(SleuthkitCase sleuthkitCase) {
|
||||||
super(Children.create(new FileTypesByMimeTypeNodeChildren(skCase), true));
|
super(Children.create(new FileTypesByMimeTypeNodeChildren(), true));
|
||||||
setName(NAME);
|
setName(NAME);
|
||||||
setDisplayName(NAME);
|
setDisplayName(NAME);
|
||||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png");
|
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png");
|
||||||
@ -170,12 +189,7 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLeafTypeNode() {
|
public boolean isLeafTypeNode() {
|
||||||
// if (!existingMimeTypes.isEmpty()) {
|
|
||||||
return false;
|
return false;
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -190,27 +204,21 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the children for the "By MIME Type" node these children will each
|
||||||
|
* represent a distinct media type present in the DB
|
||||||
|
*/
|
||||||
class FileTypesByMimeTypeNodeChildren extends ChildFactory<String> implements Observer {
|
class FileTypesByMimeTypeNodeChildren extends ChildFactory<String> implements Observer {
|
||||||
|
|
||||||
private SleuthkitCase skCase;
|
public FileTypesByMimeTypeNodeChildren() {
|
||||||
static final String EMPTY_MIME_TREE_STRING = "Data not available. Run file type identification module.";
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param skCase (or null if one needs to be created)
|
|
||||||
*/
|
|
||||||
public FileTypesByMimeTypeNodeChildren(SleuthkitCase skCase) {
|
|
||||||
super();
|
super();
|
||||||
addObserver(this);
|
addObserver(this);
|
||||||
this.skCase = skCase;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean createKeys(List<String> mediaTypeNodes) {
|
protected boolean createKeys(List<String> mediaTypeNodes) {
|
||||||
if (!existingMimeTypes.isEmpty()) {
|
if (!existingMimeTypes.isEmpty()) {
|
||||||
mediaTypeNodes.addAll(getMediaTypeList());
|
mediaTypeNodes.addAll(getMediaTypeList());
|
||||||
} else {
|
|
||||||
mediaTypeNodes.add(EMPTY_MIME_TREE_STRING);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -220,7 +228,7 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
if (!existingMimeTypes.isEmpty()) {
|
if (!existingMimeTypes.isEmpty()) {
|
||||||
return new MediaTypeNode(key);
|
return new MediaTypeNode(key);
|
||||||
} else {
|
} else {
|
||||||
return new EmptyNode(EMPTY_MIME_TREE_STRING);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -232,35 +240,13 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class EmptyNode extends DisplayableItemNode {
|
/**
|
||||||
|
* The Media type node created by the FileTypesByMimeTypeNodeChildren and
|
||||||
EmptyNode(String name) {
|
* contains one of the unique media types present in the database for this
|
||||||
super(Children.LEAF);
|
* case.
|
||||||
super.setName(name);
|
*/
|
||||||
setName(name);
|
|
||||||
setDisplayName(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLeafTypeNode() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T accept(DisplayableItemNodeVisitor<T> v) {
|
|
||||||
return v.visit(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getItemType() {
|
|
||||||
return getClass().getName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MediaTypeNode extends DisplayableItemNode {
|
class MediaTypeNode extends DisplayableItemNode {
|
||||||
|
|
||||||
String mediaType;
|
|
||||||
|
|
||||||
MediaTypeNode(String name) {
|
MediaTypeNode(String name) {
|
||||||
super(Children.create(new MediaTypeChildren(name), true));
|
super(Children.create(new MediaTypeChildren(name), true));
|
||||||
setName(name);
|
setName(name);
|
||||||
@ -285,6 +271,11 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates children fro media type nodes, children will be MediaSubTypeNodes
|
||||||
|
* and represent one of the subtypes which are present in the database of
|
||||||
|
* their media type.
|
||||||
|
*/
|
||||||
class MediaTypeChildren extends ChildFactory<String> implements Observer {
|
class MediaTypeChildren extends ChildFactory<String> implements Observer {
|
||||||
|
|
||||||
String mediaType;
|
String mediaType;
|
||||||
@ -313,6 +304,10 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node which represents the media sub type in the By MIME type tree, the
|
||||||
|
* media subtype is the portion of the MIME type following the /.
|
||||||
|
*/
|
||||||
class MediaSubTypeNode extends DisplayableItemNode {
|
class MediaSubTypeNode extends DisplayableItemNode {
|
||||||
|
|
||||||
private MediaSubTypeNode(String mimeType) {
|
private MediaSubTypeNode(String mimeType) {
|
||||||
@ -326,11 +321,24 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS
|
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the display name of the mediaSubTypeNode to include the count
|
||||||
|
* of files which it represents.
|
||||||
|
*
|
||||||
|
* @param mimeType - the complete MimeType, needed for accurate query
|
||||||
|
* results
|
||||||
|
*/
|
||||||
private void updateDisplayName(String mimeType) {
|
private void updateDisplayName(String mimeType) {
|
||||||
final long count = MediaSubTypeNodeChildren.calculateItems(skCase, mimeType);
|
final long count = MediaSubTypeNodeChildren.calculateItems(getSleuthkitCase(), mimeType);
|
||||||
super.setDisplayName(mimeType.split("/")[1] + " (" + count + ")");
|
super.setDisplayName(mimeType.split("/")[1] + " (" + count + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This returns true because any MediaSubTypeNode that exists is going
|
||||||
|
* to be a bottom level node in the Tree view on the left of Autopsy.
|
||||||
|
*
|
||||||
|
* @return true
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean isLeafTypeNode() {
|
public boolean isLeafTypeNode() {
|
||||||
return true;
|
return true;
|
||||||
@ -348,6 +356,11 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for populating the contents of the Media Sub Type Node with the
|
||||||
|
* files that match MimeType which is represented by this position in the
|
||||||
|
* tree.
|
||||||
|
*/
|
||||||
private static class MediaSubTypeNodeChildren extends ChildFactory.Detachable<Content> {
|
private static class MediaSubTypeNodeChildren extends ChildFactory.Detachable<Content> {
|
||||||
|
|
||||||
private final String mimeType;
|
private final String mimeType;
|
||||||
@ -360,17 +373,27 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
/**
|
/**
|
||||||
* Get children count without actually loading all nodes
|
* Get children count without actually loading all nodes
|
||||||
*
|
*
|
||||||
* @return
|
* @return count(*) - the number of items that will be shown in this
|
||||||
|
* items Directory Listing
|
||||||
*/
|
*/
|
||||||
private static long calculateItems(SleuthkitCase sleuthkitCase, String mimeType) {
|
private static long calculateItems(SleuthkitCase sleuthkitCase, String mime_type) {
|
||||||
try {
|
try {
|
||||||
return sleuthkitCase.countFilesWhere(createQuery(mimeType));
|
return sleuthkitCase.countFilesWhere(createQuery(mime_type));
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS
|
LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the createQuery method to complete the query, Select * from
|
||||||
|
* tsk_files WHERE. The results from the database will contain the files
|
||||||
|
* which match this mime type and their information.
|
||||||
|
*
|
||||||
|
* @param list - will contain all files and their attributes from the
|
||||||
|
* tsk_files table where mime_type matches the one specified
|
||||||
|
* @return true
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean createKeys(List<Content> list) {
|
protected boolean createKeys(List<Content> list) {
|
||||||
try {
|
try {
|
||||||
@ -382,16 +405,33 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String createQuery(String mimeType) {
|
/**
|
||||||
|
* Create the portion of the query following WHERE for a query of the
|
||||||
|
* database for each file which matches the complete MIME type
|
||||||
|
* represented by this node. Matches against the mime_type column in
|
||||||
|
* tsk_files.
|
||||||
|
*
|
||||||
|
* @param mimeType - the complete mimetype of the file mediatype/subtype
|
||||||
|
* @return query.toString - portion of SQL query which will follow a
|
||||||
|
* WHERE clause.
|
||||||
|
*/
|
||||||
|
private static String createQuery(String mime_type) {
|
||||||
StringBuilder query = new StringBuilder();
|
StringBuilder query = new StringBuilder();
|
||||||
query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS
|
query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS
|
||||||
if (UserPreferences.hideKnownFilesInViewsTree()) {
|
if (UserPreferences.hideKnownFilesInViewsTree()) {
|
||||||
query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS
|
query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS
|
||||||
}
|
}
|
||||||
query.append(" AND mime_type = '").append(mimeType).append("'");
|
query.append(" AND mime_type = '").append(mime_type).append("'");
|
||||||
return query.toString();
|
return query.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the content to populate the Directory Listing Table view for
|
||||||
|
* each file
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Node createNodeForKey(Content key) {
|
protected Node createNodeForKey(Content key) {
|
||||||
return key.accept(new ContentVisitor.Default<AbstractNode>() {
|
return key.accept(new ContentVisitor.Default<AbstractNode>() {
|
||||||
@ -426,5 +466,6 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user