mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-20 11:26:53 +00:00
updates
This commit is contained in:
parent
1f7a19bb11
commit
1c9d647d0b
@ -32,7 +32,6 @@ import java.util.EnumSet;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -50,7 +49,6 @@ import javax.swing.event.PopupMenuEvent;
|
|||||||
import javax.swing.event.PopupMenuListener;
|
import javax.swing.event.PopupMenuListener;
|
||||||
import javax.swing.tree.TreeSelectionModel;
|
import javax.swing.tree.TreeSelectionModel;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
import org.openide.explorer.ExplorerManager;
|
import org.openide.explorer.ExplorerManager;
|
||||||
import org.openide.explorer.ExplorerUtils;
|
import org.openide.explorer.ExplorerUtils;
|
||||||
import org.openide.explorer.view.BeanTreeView;
|
import org.openide.explorer.view.BeanTreeView;
|
||||||
@ -89,8 +87,7 @@ import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
|
|||||||
import org.sleuthkit.autopsy.corecomponents.SelectionResponder;
|
import org.sleuthkit.autopsy.corecomponents.SelectionResponder;
|
||||||
import org.sleuthkit.autopsy.datamodel.CreditCards;
|
import org.sleuthkit.autopsy.datamodel.CreditCards;
|
||||||
import org.sleuthkit.autopsy.datamodel.accounts.BINRange;
|
import org.sleuthkit.autopsy.datamodel.accounts.BINRange;
|
||||||
import org.sleuthkit.autopsy.mainui.datamodel.EmailsDAO;
|
import org.sleuthkit.autopsy.mainui.datamodel.MainDAO;
|
||||||
import org.sleuthkit.autopsy.mainui.nodes.AnalysisResultTypeFactory;
|
|
||||||
import org.sleuthkit.autopsy.mainui.nodes.AnalysisResultTypeFactory.KeywordSetFactory;
|
import org.sleuthkit.autopsy.mainui.nodes.AnalysisResultTypeFactory.KeywordSetFactory;
|
||||||
import org.sleuthkit.autopsy.mainui.nodes.ChildNodeSelectionInfo.BlackboardArtifactNodeSelectionInfo;
|
import org.sleuthkit.autopsy.mainui.nodes.ChildNodeSelectionInfo.BlackboardArtifactNodeSelectionInfo;
|
||||||
import org.sleuthkit.autopsy.mainui.nodes.TreeNode;
|
import org.sleuthkit.autopsy.mainui.nodes.TreeNode;
|
||||||
@ -1564,19 +1561,14 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Node parentNode = emailMsgRootNode;
|
Node parentNode = null;
|
||||||
Node[] childNodes = emailMsgRootNode.getChildren().getNodes(true);
|
Node[] childNodes = emailMsgRootNode.getChildren().getNodes(true);
|
||||||
while (childNodes != null) {
|
while (childNodes != null) {
|
||||||
for (Node child : childNodes) {
|
for (Node child : childNodes) {
|
||||||
if (Objects.equals(path, child.getName())) {
|
if (MainDAO.getInstance().getEmailsDAO().getNextSubFolder(child.getName(), path).isPresent()) {
|
||||||
return child;
|
|
||||||
} else if ((StringUtils.isBlank(path) && StringUtils.isBlank(child.getName()))
|
|
||||||
|| (StringUtils.isNotBlank(path) && path.startsWith(child.getName()))) {
|
|
||||||
parentNode = child;
|
parentNode = child;
|
||||||
childNodes = parentNode.getChildren().getNodes(true);
|
childNodes = parentNode.getChildren().getNodes(true);
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.python.icu.text.MessageFormat;
|
import org.python.icu.text.MessageFormat;
|
||||||
@ -73,7 +72,6 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
private static final Logger logger = Logger.getLogger(EmailsDAO.class.getName());
|
private static final Logger logger = Logger.getLogger(EmailsDAO.class.getName());
|
||||||
|
|
||||||
private static final String PATH_DELIMITER = "\\";
|
private static final String PATH_DELIMITER = "\\";
|
||||||
private static final String REGEX_PATH_DELIMITER = "\\\\";
|
|
||||||
private static final String ESCAPE_CHAR = "$";
|
private static final String ESCAPE_CHAR = "$";
|
||||||
|
|
||||||
private final Cache<SearchParams<EmailSearchParams>, SearchResultsDTO> searchParamsCache
|
private final Cache<SearchParams<EmailSearchParams>, SearchResultsDTO> searchParamsCache
|
||||||
@ -104,15 +102,41 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
return searchParamsCache.get(emailSearchParams, () -> fetchEmailMessageDTOs(emailSearchParams));
|
return searchParamsCache.get(emailSearchParams, () -> fetchEmailMessageDTOs(emailSearchParams));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the values of a results view prepared statement used in
|
||||||
|
* fetchEmailMessageDTOs.
|
||||||
|
*
|
||||||
|
* @param preparedStatement The prepared statement.
|
||||||
|
* @param normalizedPath The query path indicated by TSK_PATH.
|
||||||
|
* @param dataSourceId The data source id.
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
private void setResultsViewPreparedStatement(CaseDbPreparedStatement preparedStatement, String normalizedPath, Long dataSourceId) throws TskCoreException {
|
||||||
|
int paramIdx = 0;
|
||||||
|
if (normalizedPath != null) {
|
||||||
|
preparedStatement.setString(++paramIdx, normalizedPath);
|
||||||
|
String noEndingSlash = normalizedPath.endsWith(PATH_DELIMITER) ? normalizedPath.substring(0, normalizedPath.length() - 1) : normalizedPath;
|
||||||
|
preparedStatement.setString(++paramIdx, noEndingSlash);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataSourceId != null) {
|
||||||
|
preparedStatement.setLong(++paramIdx, dataSourceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private SearchResultsDTO fetchEmailMessageDTOs(SearchParams<EmailSearchParams> searchParams) throws NoCurrentCaseException, TskCoreException, SQLException, IllegalStateException {
|
private SearchResultsDTO fetchEmailMessageDTOs(SearchParams<EmailSearchParams> searchParams) throws NoCurrentCaseException, TskCoreException, SQLException, IllegalStateException {
|
||||||
|
|
||||||
// get current page of communication accounts results
|
// get current page of communication accounts results
|
||||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||||
Blackboard blackboard = skCase.getBlackboard();
|
Blackboard blackboard = skCase.getBlackboard();
|
||||||
|
|
||||||
String pathWhereStatement = StringUtils.isBlank(searchParams.getParamData().getFolder())
|
String normalizedPath = getNormalizedPath(searchParams.getParamData().getFolder());
|
||||||
? "AND attr.value_text IS NULL OR attr.value_text NOT LIKE '/%' ESCAPE '" + ESCAPE_CHAR + " \n"
|
String pathWhereStatement = (normalizedPath == null)
|
||||||
: "AND attr.value_text LIKE ? ESCAPE '" + ESCAPE_CHAR + "' \n";
|
// if searching for result without any folder, find items that are not prefixed with '\' or aren't null
|
||||||
|
? "AND attr.value_text IS NULL OR attr.value_text NOT LIKE '" + PATH_DELIMITER + "%' ESCAPE '" + ESCAPE_CHAR + " \n"
|
||||||
|
// the path should start with the prescribed folder
|
||||||
|
: "AND (attr.value_text = ? OR attr.value_text = ?)\n";
|
||||||
|
|
||||||
String baseQuery = "FROM blackboard_artifacts art \n"
|
String baseQuery = "FROM blackboard_artifacts art \n"
|
||||||
+ "LEFT JOIN blackboard_attributes attr ON attr.artifact_id = art.artifact_id \n"
|
+ "LEFT JOIN blackboard_attributes attr ON attr.artifact_id = art.artifact_id \n"
|
||||||
@ -134,19 +158,9 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
List<Long> pagedIds = new ArrayList<>();
|
List<Long> pagedIds = new ArrayList<>();
|
||||||
AtomicReference<Long> totalCount = new AtomicReference<>(0L);
|
AtomicReference<Long> totalCount = new AtomicReference<>(0L);
|
||||||
|
|
||||||
|
// query for counts
|
||||||
try (CaseDbPreparedStatement preparedStatement = getCase().getCaseDbAccessManager().prepareSelect(countsQuery)) {
|
try (CaseDbPreparedStatement preparedStatement = getCase().getCaseDbAccessManager().prepareSelect(countsQuery)) {
|
||||||
|
setResultsViewPreparedStatement(preparedStatement, searchParams.getParamData().getFolder(), searchParams.getParamData().getDataSourceId());
|
||||||
int paramIdx = 0;
|
|
||||||
if (searchParams.getParamData().getFolder() != null) {
|
|
||||||
preparedStatement.setString(++paramIdx, MessageFormat.format("%{0}%",
|
|
||||||
SubDAOUtils.likeEscape(searchParams.getParamData().getFolder() + "%", ESCAPE_CHAR)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (searchParams.getParamData().getDataSourceId() != null) {
|
|
||||||
preparedStatement.setLong(++paramIdx, searchParams.getParamData().getDataSourceId());
|
|
||||||
}
|
|
||||||
|
|
||||||
getCase().getCaseDbAccessManager().select(preparedStatement, (resultSet) -> {
|
getCase().getCaseDbAccessManager().select(preparedStatement, (resultSet) -> {
|
||||||
try {
|
try {
|
||||||
if (resultSet.next()) {
|
if (resultSet.next()) {
|
||||||
@ -159,21 +173,11 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if there is a result count, get paged artifact ids
|
||||||
List<BlackboardArtifact> allArtifacts = Collections.emptyList();
|
List<BlackboardArtifact> allArtifacts = Collections.emptyList();
|
||||||
if (totalCount.get() > 0) {
|
if (totalCount.get() > 0) {
|
||||||
try (CaseDbPreparedStatement preparedStatement = getCase().getCaseDbAccessManager().prepareSelect(itemsQuery)) {
|
try (CaseDbPreparedStatement preparedStatement = getCase().getCaseDbAccessManager().prepareSelect(itemsQuery)) {
|
||||||
|
setResultsViewPreparedStatement(preparedStatement, searchParams.getParamData().getFolder(), searchParams.getParamData().getDataSourceId());
|
||||||
int paramIdx = 0;
|
|
||||||
if (searchParams.getParamData().getFolder() != null) {
|
|
||||||
preparedStatement.setString(++paramIdx, MessageFormat.format("%{0}%",
|
|
||||||
SubDAOUtils.likeEscape(searchParams.getParamData().getFolder(), ESCAPE_CHAR)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (searchParams.getParamData().getDataSourceId() != null) {
|
|
||||||
preparedStatement.setLong(++paramIdx, searchParams.getParamData().getDataSourceId());
|
|
||||||
}
|
|
||||||
|
|
||||||
getCase().getCaseDbAccessManager().select(preparedStatement, (resultSet) -> {
|
getCase().getCaseDbAccessManager().select(preparedStatement, (resultSet) -> {
|
||||||
try {
|
try {
|
||||||
while (resultSet.next()) {
|
while (resultSet.next()) {
|
||||||
@ -200,77 +204,138 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
tableData.rows, searchParams.getStartItem(), totalCount.get());
|
tableData.rows, searchParams.getStartItem(), totalCount.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a list of data artifacts gathered using
|
||||||
|
* Blackboard.getDataArtifactsWhere into a list of blackboard artifacts.
|
||||||
|
*
|
||||||
|
* @param blackboard The TSK blackboard.
|
||||||
|
* @param whereClause The where clause to use with
|
||||||
|
* Blackboard.getDataArtifactsWhere.
|
||||||
|
*
|
||||||
|
* @return The list of BlackboardArtifacts.
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private List<BlackboardArtifact> getDataArtifactsAsBBA(Blackboard blackboard, String whereClause) throws TskCoreException {
|
private List<BlackboardArtifact> getDataArtifactsAsBBA(Blackboard blackboard, String whereClause) throws TskCoreException {
|
||||||
return (List<BlackboardArtifact>) (List<? extends BlackboardArtifact>) blackboard.getDataArtifactsWhere(whereClause);
|
return (List<BlackboardArtifact>) (List<? extends BlackboardArtifact>) blackboard.getDataArtifactsWhere(whereClause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the last non-blank folder segment.
|
||||||
|
*
|
||||||
|
* @param fullPath The full path taken from a TSK_PATH.
|
||||||
|
*
|
||||||
|
* @return The last non-blank folder segment.
|
||||||
|
*/
|
||||||
private static String getLastFolderSegment(String fullPath) {
|
private static String getLastFolderSegment(String fullPath) {
|
||||||
if (StringUtils.isNotBlank(fullPath)) {
|
// getNormalizedPath should remove any trailing whitespace or path delimiters,
|
||||||
String[] folderPieces = fullPath.split(REGEX_PATH_DELIMITER);
|
// so take the last index of the path delimiter if it exists, and use everything after that.
|
||||||
for (int i = folderPieces.length - 1; i >= 0; i--) {
|
String normalizedPath = getNormalizedPath(fullPath);
|
||||||
if (StringUtils.isNotBlank(folderPieces[i])) {
|
if (normalizedPath == null) {
|
||||||
return folderPieces[i].trim();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (normalizedPath.length() > 1) {
|
||||||
|
int lastIdx = normalizedPath.lastIndexOf(PATH_DELIMITER, normalizedPath.length() - 1);
|
||||||
|
if (lastIdx >= 0) {
|
||||||
|
return normalizedPath.substring(lastIdx + 1, normalizedPath.length() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the folder display name based on the folder. If blank, returns
|
||||||
|
* Default folder string.
|
||||||
|
*
|
||||||
|
* @param folder The folder.
|
||||||
|
*
|
||||||
|
* @return The folder display name.
|
||||||
|
*/
|
||||||
@Messages({"EmailsDAO_getFolderDisplayName_defaultName=[Default]"})
|
@Messages({"EmailsDAO_getFolderDisplayName_defaultName=[Default]"})
|
||||||
public static String getFolderDisplayName(String folder) {
|
public static String getFolderDisplayName(String folder) {
|
||||||
return StringUtils.isBlank(folder)
|
return folder == null
|
||||||
? Bundle.EmailsDAO_getFolderDisplayName_defaultName()
|
? Bundle.EmailsDAO_getFolderDisplayName_defaultName()
|
||||||
: folder;
|
: folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes the path. If path is blank or does not start with a path
|
||||||
|
* delimiter, return an empty string. Otherwise, remove all trailing
|
||||||
|
* whitespace and path delimiters.
|
||||||
|
*
|
||||||
|
* @param origPath The original path.
|
||||||
|
*
|
||||||
|
* @return The normalized path.
|
||||||
|
*/
|
||||||
private static String getNormalizedPath(String origPath) {
|
private static String getNormalizedPath(String origPath) {
|
||||||
String safePath = StringUtils.defaultString(origPath);
|
if (origPath == null || !origPath.startsWith(PATH_DELIMITER)) {
|
||||||
if (StringUtils.isBlank(safePath)) {
|
return null;
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
safePath = safePath.trim();
|
if (!origPath.endsWith(PATH_DELIMITER)) {
|
||||||
if (!safePath.endsWith(PATH_DELIMITER)) {
|
origPath = origPath + PATH_DELIMITER;
|
||||||
safePath = safePath + PATH_DELIMITER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return safePath;
|
return origPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a tree item dto with the given parameters.
|
||||||
|
*
|
||||||
|
* @param fullPath The full TSK_PATH path.
|
||||||
|
* @param dataSourceId The data source object id.
|
||||||
|
* @param count The count to display.
|
||||||
|
*
|
||||||
|
* @return The tree item dto.
|
||||||
|
*/
|
||||||
public TreeItemDTO<EmailSearchParams> createEmailTreeItem(String fullPath, Long dataSourceId, TreeDisplayCount count) {
|
public TreeItemDTO<EmailSearchParams> createEmailTreeItem(String fullPath, Long dataSourceId, TreeDisplayCount count) {
|
||||||
return createEmailTreeItem(fullPath, getLastFolderSegment(fullPath), dataSourceId, count);
|
String normalizedPath = getNormalizedPath(fullPath);
|
||||||
}
|
String lastSegment = getLastFolderSegment(fullPath);
|
||||||
|
String displayName = getFolderDisplayName(lastSegment);
|
||||||
public TreeItemDTO<EmailSearchParams> createEmailTreeItem(String fullPath, String folderName, Long dataSourceId, TreeDisplayCount count) {
|
|
||||||
return new TreeItemDTO<>(
|
return new TreeItemDTO<>(
|
||||||
EmailSearchParams.getTypeId(),
|
EmailSearchParams.getTypeId(),
|
||||||
new EmailSearchParams(dataSourceId, fullPath),
|
new EmailSearchParams(dataSourceId, normalizedPath),
|
||||||
folderName,
|
normalizedPath == null ? 0 : normalizedPath,
|
||||||
getFolderDisplayName(folderName),
|
displayName,
|
||||||
count
|
count
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> getNextSubFolder(String folderParent, String folder) {
|
/**
|
||||||
String normalizedParent = folderParent == null ? null : getNormalizedPath(folderParent);
|
* Returns the next relevant subfolder (the full path) after the parent path
|
||||||
String normalizedFolder = folder == null ? null : getNormalizedPath(folder);
|
* or empty if child path is not a sub path of parent path path.
|
||||||
|
*
|
||||||
|
* @param parentPath The parent path.
|
||||||
|
* @param childPath The child path.
|
||||||
|
*
|
||||||
|
* @return The next subfolder or empty.
|
||||||
|
*/
|
||||||
|
public Optional<String> getNextSubFolder(String parentPath, String childPath) {
|
||||||
|
String normalizedParent = getNormalizedPath(parentPath);
|
||||||
|
String normalizedChild = getNormalizedPath(childPath);
|
||||||
|
|
||||||
if (normalizedParent == null || normalizedFolder.startsWith(normalizedParent)) {
|
if (normalizedChild == null) {
|
||||||
if (normalizedFolder == null) {
|
return (normalizedParent == null)
|
||||||
return Optional.of(null);
|
? Optional.of(null)
|
||||||
} else {
|
: Optional.empty();
|
||||||
int nextDelim = normalizedFolder.indexOf(PATH_DELIMITER, normalizedParent.length());
|
|
||||||
if (nextDelim >= 0) {
|
|
||||||
return Optional.of(normalizedFolder.substring(normalizedParent.length(), nextDelim));
|
|
||||||
} else {
|
|
||||||
return Optional.of(normalizedFolder.substring(normalizedParent.length()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (normalizedParent == null) {
|
||||||
|
normalizedParent = PATH_DELIMITER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that child is a sub path of parent
|
||||||
|
if (normalizedChild.startsWith(normalizedParent)) {
|
||||||
|
int nextDelimiter = normalizedChild.indexOf(PATH_DELIMITER, normalizedParent.length());
|
||||||
|
return nextDelimiter >= 0
|
||||||
|
? Optional.of(getNormalizedPath(normalizedChild.substring(0, nextDelimiter + 1)))
|
||||||
|
: Optional.of(normalizedChild);
|
||||||
|
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -302,13 +367,18 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
|
|
||||||
String substringFolderSql;
|
String substringFolderSql;
|
||||||
String folderWhereStatement;
|
String folderWhereStatement;
|
||||||
if (StringUtils.isBlank(folder)) {
|
if (folder == null) {
|
||||||
substringFolderSql = "CASE WHEN p.path LIKE '" + PATH_DELIMITER + "%' ESCAPE '" + ESCAPE_CHAR + "' THEN SUBSTR(p.path, 2) ELSE '' END";
|
substringFolderSql = "CASE WHEN p.path LIKE '" + PATH_DELIMITER + "%' ESCAPE '" + ESCAPE_CHAR + "' THEN SUBSTR(p.path, 2) ELSE NULL END";
|
||||||
folderWhereStatement = "";
|
folderWhereStatement = "";
|
||||||
} else {
|
} else {
|
||||||
// if exact match,
|
// if exact match,
|
||||||
substringFolderSql = "CASE WHEN (p.path = ? OR p.path = ?) THEN NULL ELSE SUBSTR(p.path, LENGTH(?) + 1) END";
|
substringFolderSql = "CASE\n"
|
||||||
folderWhereStatement = " WHERE (p.path LIKE ? ESCAPE '" + ESCAPE_CHAR + "' OR p.path = ?)\n";
|
+ " WHEN p.path LIKE ? THEN \n"
|
||||||
|
+ " SUBSTR(p.path, LENGTH(?))\n"
|
||||||
|
+ " ELSE\n"
|
||||||
|
+ " NULL\n"
|
||||||
|
+ "END";
|
||||||
|
folderWhereStatement = " WHERE (p.path = ? OR p.path LIKE ? ESCAPE '" + ESCAPE_CHAR + "')\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
String query = "\n MAX(grouped_res.folder) AS folder\n"
|
String query = "\n MAX(grouped_res.folder) AS folder\n"
|
||||||
@ -348,9 +418,10 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
* Returns the accounts and their counts in the current data source if a
|
* Returns the accounts and their counts in the current data source if a
|
||||||
* data source id is provided or all accounts if data source id is null.
|
* data source id is provided or all accounts if data source id is null.
|
||||||
*
|
*
|
||||||
* @param dataSourceId The data source id or null for no data source filter.
|
* @param dataSourceId The data source id or null for no data source
|
||||||
* @param folder The email folder parent (using '\' as prefix, suffix,
|
* filter.
|
||||||
* and delimiter). If null, root level folders
|
* @param normalizedPath The email folder parent (using '\' as prefix,
|
||||||
|
* suffix, and delimiter). If null, root level folders
|
||||||
*
|
*
|
||||||
* @return The results.
|
* @return The results.
|
||||||
*
|
*
|
||||||
@ -358,15 +429,12 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
*/
|
*/
|
||||||
public TreeResultsDTO<EmailSearchParams> getEmailCounts(Long dataSourceId, String folder) throws ExecutionException {
|
public TreeResultsDTO<EmailSearchParams> getEmailCounts(Long dataSourceId, String folder) throws ExecutionException {
|
||||||
|
|
||||||
// folder ending with slash if not null
|
String normalizedParent = getNormalizedPath(folder);
|
||||||
String endSlashFolder = (folder != null && !folder.endsWith(PATH_DELIMITER))
|
|
||||||
? folder + PATH_DELIMITER
|
|
||||||
: folder;
|
|
||||||
|
|
||||||
// a series of full folder paths prefixed and delimiter of '\' (no suffix)
|
// a series of full folder paths prefixed and delimiter of '\' (no suffix)
|
||||||
Set<String> indeterminateTypes = this.emailCounts.getEnqueued().stream()
|
Set<String> indeterminateTypes = this.emailCounts.getEnqueued().stream()
|
||||||
.filter(evt -> (dataSourceId == null || Objects.equals(evt.getDataSourceId(), dataSourceId)))
|
.filter(evt -> (dataSourceId == null || Objects.equals(evt.getDataSourceId(), dataSourceId)))
|
||||||
.map(evt -> getNextSubFolder(folder, evt.getFolder()))
|
.map(evt -> getNextSubFolder(normalizedParent, evt.getFolder()))
|
||||||
.filter(opt -> opt.isPresent())
|
.filter(opt -> opt.isPresent())
|
||||||
.map(opt -> opt.get())
|
.map(opt -> opt.get())
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
@ -374,16 +442,20 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
String query = null;
|
String query = null;
|
||||||
try {
|
try {
|
||||||
SleuthkitCase skCase = getCase();
|
SleuthkitCase skCase = getCase();
|
||||||
query = getFolderChildrenSql(skCase.getDatabaseType(), endSlashFolder, dataSourceId);
|
query = getFolderChildrenSql(skCase.getDatabaseType(), normalizedParent, dataSourceId);
|
||||||
|
|
||||||
try (CaseDbPreparedStatement preparedStatement = skCase.getCaseDbAccessManager().prepareSelect(query)) {
|
try (CaseDbPreparedStatement preparedStatement = skCase.getCaseDbAccessManager().prepareSelect(query)) {
|
||||||
int paramIdx = 0;
|
int paramIdx = 0;
|
||||||
if (folder != null) {
|
if (normalizedParent != null) {
|
||||||
preparedStatement.setString(++paramIdx, folder);
|
String likeStatement = SubDAOUtils.likeEscape(normalizedParent, ESCAPE_CHAR) + "%";
|
||||||
preparedStatement.setString(++paramIdx, endSlashFolder);
|
String normalizedWithoutSlash = normalizedParent.endsWith(PATH_DELIMITER)
|
||||||
preparedStatement.setString(++paramIdx, endSlashFolder);
|
? normalizedParent.substring(0, normalizedParent.length() - 1)
|
||||||
preparedStatement.setString(++paramIdx, SubDAOUtils.likeEscape(endSlashFolder, ESCAPE_CHAR) + "%");
|
: normalizedParent;
|
||||||
preparedStatement.setString(++paramIdx, folder);
|
|
||||||
|
preparedStatement.setString(++paramIdx, likeStatement);
|
||||||
|
preparedStatement.setString(++paramIdx, normalizedParent);
|
||||||
|
preparedStatement.setString(++paramIdx, normalizedWithoutSlash);
|
||||||
|
preparedStatement.setString(++paramIdx, likeStatement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataSourceId != null) {
|
if (dataSourceId != null) {
|
||||||
@ -396,23 +468,23 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
try {
|
try {
|
||||||
while (resultSet.next()) {
|
while (resultSet.next()) {
|
||||||
String rsFolderSegment = resultSet.getString("folder");
|
String rsFolderSegment = resultSet.getString("folder");
|
||||||
// if blank returned, assume it belongs to base results; don't provide ending slash
|
|
||||||
String rsPath;
|
String rsPath;
|
||||||
if (StringUtils.isNotBlank(folder) && StringUtils.isNotBlank(rsFolderSegment)) {
|
if (normalizedParent != null && rsFolderSegment != null) {
|
||||||
rsPath = endSlashFolder + rsFolderSegment;
|
// both the parent path and next folder segment are present
|
||||||
} else if (StringUtils.isBlank(folder) && StringUtils.isBlank(rsFolderSegment)) {
|
rsPath = getNormalizedPath(normalizedParent + PATH_DELIMITER + rsFolderSegment + PATH_DELIMITER);
|
||||||
rsPath = "";
|
} else if (rsFolderSegment == null) {
|
||||||
} else if (StringUtils.isNotBlank(folder)) {
|
// the folder segment is not present
|
||||||
rsPath = folder;
|
rsPath = getNormalizedPath(normalizedParent);
|
||||||
} else {
|
} else {
|
||||||
rsPath = PATH_DELIMITER + rsFolderSegment;
|
// the normalized parent is not present but the folder segment is
|
||||||
|
rsPath = getNormalizedPath(PATH_DELIMITER + rsFolderSegment + PATH_DELIMITER);
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeDisplayCount treeDisplayCount = indeterminateTypes.contains(rsPath)
|
TreeDisplayCount treeDisplayCount = indeterminateTypes.contains(rsPath)
|
||||||
? TreeDisplayCount.INDETERMINATE
|
? TreeDisplayCount.INDETERMINATE
|
||||||
: TreeResultsDTO.TreeDisplayCount.getDeterminate(resultSet.getLong("count"));
|
: TreeResultsDTO.TreeDisplayCount.getDeterminate(resultSet.getLong("count"));
|
||||||
|
|
||||||
accumulatedData.add(createEmailTreeItem(rsPath, rsFolderSegment, dataSourceId, treeDisplayCount));
|
accumulatedData.add(createEmailTreeItem(rsPath, dataSourceId, treeDisplayCount));
|
||||||
}
|
}
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new IllegalStateException("A sql exception occurred.", ex);
|
throw new IllegalStateException("A sql exception occurred.", ex);
|
||||||
@ -420,7 +492,7 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// if only one item of this type, don't show children
|
// if only one item of this type, don't show children
|
||||||
if (accumulatedData.size() == 1 && StringUtils.isBlank(accumulatedData.get(0).getId().toString())) {
|
if (accumulatedData.size() == 1 && accumulatedData.get(0).getId() == null) {
|
||||||
return new TreeResultsDTO<>(Collections.emptyList());
|
return new TreeResultsDTO<>(Collections.emptyList());
|
||||||
} else {
|
} else {
|
||||||
// return results
|
// return results
|
||||||
@ -431,7 +503,7 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
} catch (SQLException | NoCurrentCaseException | TskCoreException | IllegalStateException ex) {
|
} catch (SQLException | NoCurrentCaseException | TskCoreException | IllegalStateException ex) {
|
||||||
throw new ExecutionException(
|
throw new ExecutionException(
|
||||||
MessageFormat.format("An error occurred while fetching email counts for folder: {0} and sql: \n{1}",
|
MessageFormat.format("An error occurred while fetching email counts for folder: {0} and sql: \n{1}",
|
||||||
folder == null ? "<null>" : folder,
|
normalizedParent == null ? "<null>" : normalizedParent,
|
||||||
query == null ? "<null>" : query),
|
query == null ? "<null>" : query),
|
||||||
ex);
|
ex);
|
||||||
}
|
}
|
||||||
@ -480,7 +552,7 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
try {
|
try {
|
||||||
if (art.getType().getTypeID() == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) {
|
if (art.getType().getTypeID() == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) {
|
||||||
BlackboardAttribute attr = art.getAttribute(BlackboardAttribute.Type.TSK_PATH);
|
BlackboardAttribute attr = art.getAttribute(BlackboardAttribute.Type.TSK_PATH);
|
||||||
String folder = attr == null ? null : attr.getValueString();
|
String folder = attr == null ? null : getNormalizedPath(attr.getValueString());
|
||||||
emailMap
|
emailMap
|
||||||
.computeIfAbsent(folder, (k) -> new HashSet<>())
|
.computeIfAbsent(folder, (k) -> new HashSet<>())
|
||||||
.add(art.getDataSourceObjectID());
|
.add(art.getDataSourceObjectID());
|
||||||
@ -531,8 +603,9 @@ public class EmailsDAO extends AbstractDAO {
|
|||||||
private boolean isEmailInvalidating(EmailSearchParams parameters, DAOEvent evt) {
|
private boolean isEmailInvalidating(EmailSearchParams parameters, DAOEvent evt) {
|
||||||
if (evt instanceof EmailEvent) {
|
if (evt instanceof EmailEvent) {
|
||||||
EmailEvent emailEvt = (EmailEvent) evt;
|
EmailEvent emailEvt = (EmailEvent) evt;
|
||||||
return Objects.equals(getNormalizedPath(parameters.getFolder()), getNormalizedPath(emailEvt.getFolder()))
|
// determines if sub folder or not. if equivalent, will return present
|
||||||
&& (parameters.getDataSourceId() == null || Objects.equals(parameters.getDataSourceId(), emailEvt.getDataSourceId()));
|
return (getNextSubFolder(parameters.getFolder(), emailEvt.getFolder()).isPresent()
|
||||||
|
&& (parameters.getDataSourceId() == null || Objects.equals(parameters.getDataSourceId(), emailEvt.getDataSourceId())));
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -298,7 +298,8 @@ public class DataArtifactTypeFactory extends TreeChildFactory<DataArtifactSearch
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TreeNode<EmailSearchParams> createNewNode(TreeResultsDTO.TreeItemDTO<? extends EmailSearchParams> rowData) {
|
protected TreeNode<EmailSearchParams> createNewNode(TreeResultsDTO.TreeItemDTO<? extends EmailSearchParams> rowData) {
|
||||||
return new EmailNode(rowData);
|
boolean isLeafNode = Objects.equals(rowData.getSearchParams().getFolder(), this.folderParent);
|
||||||
|
return new EmailNode(rowData, isLeafNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -328,11 +329,11 @@ public class DataArtifactTypeFactory extends TreeChildFactory<DataArtifactSearch
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(TreeItemDTO<? extends EmailSearchParams> o1, TreeItemDTO<? extends EmailSearchParams> o2) {
|
public int compare(TreeItemDTO<? extends EmailSearchParams> o1, TreeItemDTO<? extends EmailSearchParams> o2) {
|
||||||
String safeO1 = o1.getId() == null ? "" : o1.getId().toString();
|
String safeO1 = o1.getId() instanceof String ? o1.getId().toString() : "";
|
||||||
String safeO2 = o2.getId() == null ? "" : o2.getId().toString();
|
String safeO2 = o2.getId() instanceof String ? o2.getId().toString() : "";
|
||||||
|
|
||||||
boolean firstDown = StringUtils.isBlank(safeO1);
|
boolean firstDown = o1.getId() instanceof String;
|
||||||
boolean secondDown = StringUtils.isBlank(safeO2);
|
boolean secondDown = o2.getId() instanceof String;
|
||||||
|
|
||||||
if (firstDown == secondDown) {
|
if (firstDown == secondDown) {
|
||||||
return safeO1.compareToIgnoreCase(safeO2);
|
return safeO1.compareToIgnoreCase(safeO2);
|
||||||
@ -347,37 +348,37 @@ public class DataArtifactTypeFactory extends TreeChildFactory<DataArtifactSearch
|
|||||||
*/
|
*/
|
||||||
static class EmailNode extends TreeNode<EmailSearchParams> {
|
static class EmailNode extends TreeNode<EmailSearchParams> {
|
||||||
|
|
||||||
private final Children children;
|
private final boolean isLeafNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main constructor.
|
* Main constructor.
|
||||||
*
|
*
|
||||||
* @param itemData The data to display.
|
* @param itemData The data to display.
|
||||||
*/
|
*/
|
||||||
public EmailNode(TreeResultsDTO.TreeItemDTO<? extends EmailSearchParams> itemData) {
|
public EmailNode(TreeResultsDTO.TreeItemDTO<? extends EmailSearchParams> itemData, boolean isLeafNode) {
|
||||||
this(itemData, Children.create(new EmailFolderFactory(itemData.getSearchParams().getFolder(), itemData.getSearchParams().getDataSourceId()), true));
|
this(itemData,
|
||||||
|
isLeafNode
|
||||||
|
? Children.LEAF
|
||||||
|
: Children.create(new EmailFolderFactory(itemData.getSearchParams().getFolder(), itemData.getSearchParams().getDataSourceId()), true),
|
||||||
|
isLeafNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private EmailNode(TreeResultsDTO.TreeItemDTO<? extends EmailSearchParams> itemData, Children children) {
|
private EmailNode(TreeResultsDTO.TreeItemDTO<? extends EmailSearchParams> itemData, Children children, boolean isLeafNode) {
|
||||||
super(itemData.getId().toString(),
|
super(itemData.getId().toString(),
|
||||||
"org/sleuthkit/autopsy/images/folder-icon-16.png",
|
"org/sleuthkit/autopsy/images/folder-icon-16.png",
|
||||||
itemData,
|
itemData,
|
||||||
children,
|
children,
|
||||||
getDefaultLookup(itemData));
|
getDefaultLookup(itemData));
|
||||||
|
|
||||||
this.children = children;
|
this.isLeafNode = isLeafNode;
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasChildren() {
|
|
||||||
return !StringUtils.isBlank(this.getItemData().getId().toString()) && this.children.getNodesCount(true) > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void respondSelection(DataResultTopComponent dataResultPanel) {
|
public void respondSelection(DataResultTopComponent dataResultPanel) {
|
||||||
if (hasChildren()) {
|
if (this.isLeafNode) {
|
||||||
super.respondSelection(dataResultPanel);
|
|
||||||
} else {
|
|
||||||
dataResultPanel.displayEmailMessages(super.getItemData().getSearchParams());
|
dataResultPanel.displayEmailMessages(super.getItemData().getSearchParams());
|
||||||
|
} else {
|
||||||
|
super.respondSelection(dataResultPanel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user