Merge branch 'develop' of https://github.com/sleuthkit/autopsy into image-gallery-db-migration

# Conflicts:
#	ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/grouping/GroupManager.java
This commit is contained in:
Raman 2018-07-12 07:42:24 -04:00
commit 57eff3df95
41 changed files with 742 additions and 387 deletions

View File

@ -64,14 +64,14 @@ public final class ReplaceBlackboardArtifactTagAction extends ReplaceTagAction<B
*
* @param oldArtifactTag tag to be replaced
* @param newTagName name of the tag to replace with
* @param comment the comment for the tag use an empty string for no comment
* @param newComment the newComment for the tag use an empty string for no newComment
*/
@NbBundle.Messages({
"# {0} - old tag name",
"# {1} - artifactID",
"ReplaceBlackboardArtifactTagAction.replaceTag.alert=Unable to replace tag {0} for artifact {1}."})
@Override
protected void replaceTag( BlackboardArtifactTag oldArtifactTag, TagName newTagName, String comment) {
protected void replaceTag( BlackboardArtifactTag oldArtifactTag, TagName newTagName, String newComment) {
new SwingWorker<Void, Void>() {
@Override
@ -91,7 +91,7 @@ public final class ReplaceBlackboardArtifactTagAction extends ReplaceTagAction<B
logger.log(Level.INFO, "Replacing tag {0} with tag {1} for artifact {2}", new Object[]{oldArtifactTag.getName().getDisplayName(), newTagName.getDisplayName(), oldArtifactTag.getContent().getName()}); //NON-NLS
tagsManager.deleteBlackboardArtifactTag(oldArtifactTag);
tagsManager.addBlackboardArtifactTag(oldArtifactTag.getArtifact(), newTagName, comment);
tagsManager.addBlackboardArtifactTag(oldArtifactTag.getArtifact(), newTagName, newComment);
} catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "Error replacing artifact tag", tskCoreException); //NON-NLS

View File

@ -64,7 +64,7 @@ public final class ReplaceContentTagAction extends ReplaceTagAction<ContentTag>
"# {1} - content obj id",
"ReplaceContentTagAction.replaceTag.alert=Unable to replace tag {0} for {1}."})
@Override
protected void replaceTag(ContentTag oldTag, TagName newTagName, String comment) {
protected void replaceTag(ContentTag oldTag, TagName newTagName, String newComment) {
new SwingWorker<Void, Void>() {
@Override
@ -84,7 +84,7 @@ public final class ReplaceContentTagAction extends ReplaceTagAction<ContentTag>
logger.log(Level.INFO, "Replacing tag {0} with tag {1} for artifact {2}", new Object[]{oldTag.getName().getDisplayName(), newTagName.getDisplayName(), oldTag.getContent().getName()}); //NON-NLS
tagsManager.deleteContentTag(oldTag);
tagsManager.addContentTag(oldTag.getContent(), newTagName, comment);
tagsManager.addContentTag(oldTag.getContent(), newTagName, newComment);
} catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "Error replacing artifact tag", tskCoreException); //NON-NLS

View File

@ -141,7 +141,7 @@ abstract class ReplaceTagAction<T extends Tag> extends AbstractAction implements
// Add action to replace the tag
tagNameItem.addActionListener((ActionEvent event) -> {
selectedTags.forEach((oldtag) -> {
replaceTag(oldtag, entry.getValue(), "");
replaceTag(oldtag, entry.getValue(), oldtag.getComment());
});
});
@ -178,7 +178,7 @@ abstract class ReplaceTagAction<T extends Tag> extends AbstractAction implements
TagName newTagName = GetTagNameDialog.doDialog();
if (null != newTagName) {
selectedTags.forEach((oldtag) -> {
replaceTag(oldtag, newTagName, "");
replaceTag(oldtag, newTagName, oldtag.getComment());
});
}
});

View File

@ -129,6 +129,7 @@ public class Case {
private static final String EXPORT_FOLDER = "Export"; //NON-NLS
private static final String LOG_FOLDER = "Log"; //NON-NLS
private static final String REPORTS_FOLDER = "Reports"; //NON-NLS
private static final String CONFIG_FOLDER = "Config"; // NON-NLS
private static final String TEMP_FOLDER = "Temp"; //NON-NLS
private static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS
private static final String CASE_ACTION_THREAD_NAME = "%s-case-action";
@ -1350,6 +1351,16 @@ public class Case {
public String getReportDirectory() {
return getOrCreateSubdirectory(REPORTS_FOLDER);
}
/**
* Gets the full path to the config directory for this case, creating it if
* it does not exist.
*
* @return The config directory path.
*/
public String getConfigDirectory() {
return getOrCreateSubdirectory(CONFIG_FOLDER);
}
/**
* Gets the full path to the module output directory for this case, creating

View File

@ -54,7 +54,7 @@ final class TagNameDefinition implements Comparable<TagNameDefinition> {
private static final List<String> STANDARD_TAG_DISPLAY_NAMES = Arrays.asList(Bundle.TagNameDefinition_predefTagNames_bookmark_text(), Bundle.TagNameDefinition_predefTagNames_followUp_text(),
Bundle.TagNameDefinition_predefTagNames_notableItem_text(), DhsImageCategory.ONE.getDisplayName(),
DhsImageCategory.TWO.getDisplayName(), DhsImageCategory.THREE.getDisplayName(),
DhsImageCategory.FOUR.getDisplayName(), DhsImageCategory.FIVE.getDisplayName());
DhsImageCategory.FOUR.getDisplayName(), DhsImageCategory.FIVE.getDisplayName(), DhsImageCategory.ZERO.getDisplayName());
private final String displayName;
private final String description;
private final TagName.HTML_COLOR color;

View File

@ -105,7 +105,7 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
dbManager.updateAttributeInstanceComment(correlationAttribute);
}
} catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
logger.log(Level.SEVERE, "Error adding comment", ex);
}
}
return centralRepoCommentDialog.getComment();

View File

@ -58,6 +58,9 @@ abstract class AbstractSqlEamDb implements EamDb {
protected int bulkArtifactsThreshold;
private final Map<String, Collection<CorrelationAttribute>> bulkArtifacts;
// Maximum length for the value column in the instance tables
static final int MAX_VALUE_LENGTH = 128;
// number of instances to keep in bulk queue before doing an insert.
// Update Test code if this changes. It's hard coded there.
static final int DEFAULT_BULK_THRESHHOLD = 1000;
@ -432,7 +435,8 @@ abstract class AbstractSqlEamDb implements EamDb {
if (eamDataSource.getCaseID() == -1) {
throw new EamDbException("Case ID is -1");
} else if (eamDataSource.getID() != -1) {
throw new EamDbException("Database ID is already set in object");
// This data source is already in the central repo
return;
}
Connection conn = connect();
@ -550,6 +554,13 @@ abstract class AbstractSqlEamDb implements EamDb {
if (eamArtifact.getCorrelationValue() == null) {
throw new EamDbException("Correlation value is null");
}
if (eamArtifact.getCorrelationValue().length() >= MAX_VALUE_LENGTH) {
throw new EamDbException("Artifact value too long for central repository."
+ "\nCorrelationArtifact ID: " + eamArtifact.getID()
+ "\nCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
+ "\nCorrelationArtifact Value: " + eamArtifact.getCorrelationValue());
}
Connection conn = connect();
@ -975,27 +986,50 @@ abstract class AbstractSqlEamDb implements EamDb {
if (!eamArtifact.getCorrelationValue().isEmpty()) {
if (eamInstance.getCorrelationCase() == null) {
throw new EamDbException("CorrelationAttributeInstance case is null");
throw new EamDbException("CorrelationAttributeInstance case is null for: "
+ "\n\tCorrelationArtifact ID: " + eamArtifact.getID()
+ "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
+ "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue());
}
if (eamInstance.getCorrelationDataSource() == null) {
throw new EamDbException("CorrelationAttributeInstance data source is null");
throw new EamDbException("CorrelationAttributeInstance data source is null for: "
+ "\n\tCorrelationArtifact ID: " + eamArtifact.getID()
+ "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
+ "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue());
}
if (eamInstance.getKnownStatus() == null) {
throw new EamDbException("CorrelationAttributeInstance known status is null");
throw new EamDbException("CorrelationAttributeInstance known status is null for: "
+ "\n\tCorrelationArtifact ID: " + eamArtifact.getID()
+ "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
+ "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()
+ "\n\tEam Instance: "
+ "\n\t\tCaseId: " + eamInstance.getCorrelationDataSource().getCaseID()
+ "\n\t\tDeviceID: " + eamInstance.getCorrelationDataSource().getDeviceID());
}
bulkPs.setString(1, eamInstance.getCorrelationCase().getCaseUUID());
bulkPs.setString(2, eamInstance.getCorrelationDataSource().getDeviceID());
bulkPs.setInt(3, eamInstance.getCorrelationDataSource().getCaseID());
bulkPs.setString(4, eamArtifact.getCorrelationValue());
bulkPs.setString(5, eamInstance.getFilePath());
bulkPs.setByte(6, eamInstance.getKnownStatus().getFileKnownValue());
if ("".equals(eamInstance.getComment())) {
bulkPs.setNull(7, Types.INTEGER);
if (eamArtifact.getCorrelationValue().length() < MAX_VALUE_LENGTH) {
bulkPs.setString(1, eamInstance.getCorrelationCase().getCaseUUID());
bulkPs.setString(2, eamInstance.getCorrelationDataSource().getDeviceID());
bulkPs.setInt(3, eamInstance.getCorrelationDataSource().getCaseID());
bulkPs.setString(4, eamArtifact.getCorrelationValue());
bulkPs.setString(5, eamInstance.getFilePath());
bulkPs.setByte(6, eamInstance.getKnownStatus().getFileKnownValue());
if ("".equals(eamInstance.getComment())) {
bulkPs.setNull(7, Types.INTEGER);
} else {
bulkPs.setString(7, eamInstance.getComment());
}
bulkPs.addBatch();
} else {
bulkPs.setString(7, eamInstance.getComment());
logger.log(Level.WARNING, ("Artifact value too long for central repository."
+ "\n\tCorrelationArtifact ID: " + eamArtifact.getID()
+ "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
+ "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue())
+ "\n\tEam Instance: "
+ "\n\t\tCaseId: " + eamInstance.getCorrelationDataSource().getCaseID()
+ "\n\t\tDeviceID: " + eamInstance.getCorrelationDataSource().getDeviceID()
+ "\n\t\tFilePath: " + eamInstance.getFilePath());
}
bulkPs.addBatch();
}
}
}
@ -1740,11 +1774,13 @@ abstract class AbstractSqlEamDb implements EamDb {
return 0 < badInstances;
}
/**
* Process the Artifact instance in the EamDb
*
* @param type EamArtifact.Type to search for
* @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance
*
* @throws EamDbException
*/
@Override

View File

@ -67,6 +67,7 @@ public class CorrelationDataSource implements Serializable {
/**
* Create a CorrelationDataSource object from a TSK Content object.
* This will add it to the central repository.
*
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource

View File

@ -450,10 +450,10 @@ final class CaseEventListener implements PropertyChangeListener {
correlationCase = dbManager.newCase(openCase);
}
if (null == dbManager.getDataSource(correlationCase, deviceId)) {
dbManager.newDataSource(CorrelationDataSource.fromTSKDataSource(correlationCase, newDataSource));
CorrelationDataSource.fromTSKDataSource(correlationCase, newDataSource);
}
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex); //NON-NLS
LOGGER.log(Level.SEVERE, "Error adding new data source to the central repository", ex); //NON-NLS
} catch (TskCoreException | TskDataException ex) {
LOGGER.log(Level.SEVERE, "Error getting data source from DATA_SOURCE_ADDED event content.", ex); //NON-NLS
}

View File

@ -276,12 +276,12 @@ public class IngestEventsListener {
}
}
if (FALSE == eamArtifacts.isEmpty()) {
try {
for (CorrelationAttribute eamArtifact : eamArtifacts) {
for (CorrelationAttribute eamArtifact : eamArtifacts) {
try {
dbManager.addArtifact(eamArtifact);
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error adding artifact to database.", ex); //NON-NLS
}
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex); //NON-NLS
}
} // DATA_ADDED
}

View File

@ -264,7 +264,6 @@ public final class CommonFilesPanel extends javax.swing.JPanel {
viewers.add(table);
progress.setDisplayName(Bundle.CommonFilesPanel_search_done_searchProgressDisplay());
DataResultTopComponent.createInstance(tabTitle, pathText, tableFilterWithDescendantsNode, metadata.size(), viewers);
progress.finish();
} catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, "Interrupted while loading Common Files", ex);
MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_search_done_interupted());
@ -285,6 +284,8 @@ public final class CommonFilesPanel extends javax.swing.JPanel {
errorMessage = Bundle.CommonFilesPanel_search_done_exception();
}
MessageNotifyUtil.Message.error(errorMessage);
} finally {
progress.finish();
}
}
}.execute();

View File

@ -24,7 +24,6 @@ import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.core.Installer;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.coreutils.Logger;

View File

@ -23,23 +23,29 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
/**
* <code>DataResultViewerTable</code> which overrides the default column
* header width calculations. The <code>CommonFilesSearchResultsViewerTable</code>
* presents multiple tiers of data which are not always present and it may not
* make sense to try to calculate the column widths for such tables by sampling
* rows and looking for wide cells. Rather, we just pick some reasonable values.
* <code>DataResultViewerTable</code> which overrides the default column header
* width calculations. The <code>CommonFilesSearchResultsViewerTable</code>
* presents multiple tiers of data which are not always present and it may not
* make sense to try to calculate the column widths for such tables by sampling
* rows and looking for wide cells. Rather, we just pick some reasonable values.
*/
public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable {
private static final Map<String, Integer> COLUMN_WIDTHS;
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(CommonFilesSearchResultsViewerTable.class.getName());
private static final int DEFAULT_WIDTH = 100;
static {
Map<String, Integer> map = new HashMap<>();
map.put(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), 260);
@ -49,10 +55,10 @@ public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable {
map.put(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), 100);
map.put(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), 130);
map.put(Bundle.CommonFilesSearchResultsViewerTable_tagsColLbl1(), 300);
COLUMN_WIDTHS = Collections.unmodifiableMap(map);
}
@NbBundle.Messages({
"CommonFilesSearchResultsViewerTable.filesColLbl=Files",
"CommonFilesSearchResultsViewerTable.instancesColLbl=Instances",
@ -63,18 +69,24 @@ public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable {
"CommonFilesSearchResultsViewerTable.tagsColLbl1=Tags"
})
@Override
protected void setColumnWidths(){
protected void setColumnWidths() {
TableColumnModel model = this.getColumnModel();
Enumeration<TableColumn> columnsEnumerator = model.getColumns();
while(columnsEnumerator.hasMoreElements()){
while (columnsEnumerator.hasMoreElements()) {
TableColumn column = columnsEnumerator.nextElement();
final String headerValue = column.getHeaderValue().toString();
final Integer get = COLUMN_WIDTHS.get(headerValue);
column.setPreferredWidth(get);
final Integer defaultWidth = COLUMN_WIDTHS.get(headerValue);
if(defaultWidth == null){
column.setPreferredWidth(DEFAULT_WIDTH);
LOGGER.log(Level.SEVERE, String.format("Tried to set width on a column not supported by the CommonFilesSearchResultsViewerTable: %s", headerValue));
} else {
column.setPreferredWidth(defaultWidth);
}
}
}
}

View File

@ -56,7 +56,6 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
/**
* A file content viewer for SQLite database files.
@ -289,28 +288,22 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
fileChooser.setAcceptAllFileFilterUsed(true);
fileChooser.setFileFilter(csvFilter);
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
String defaultFileName = (String) this.tablesDropdownList.getSelectedItem();
fileChooser.setSelectedFile(new File(defaultFileName));
int choice = fileChooser.showSaveDialog((Component) evt.getSource()); //TODO
if (JFileChooser.APPROVE_OPTION == choice) {
boolean overwrite = false;
File file = fileChooser.getSelectedFile();
if (file == null) {
JOptionPane.showMessageDialog(this,
Bundle.SQLiteViewer_csvExport_fileName_empty(),
Bundle.SQLiteViewer_csvExport_title(),
JOptionPane.WARNING_MESSAGE);
return;
} else if (file.exists() && FilenameUtils.getExtension(file.getName()).equalsIgnoreCase("csv")) {
if (file.exists() && FilenameUtils.getExtension(file.getName()).equalsIgnoreCase("csv")) {
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(this,
Bundle.SQLiteViewer_csvExport_confirm_msg(),
Bundle.SQLiteViewer_csvExport_title(),
JOptionPane.YES_NO_OPTION)) {
overwrite = true;
} else {
return;
}
}
exportTableToCsv(file, overwrite);
exportTableToCsv(file);
}
}//GEN-LAST:event_exportCsvButtonActionPerformed
@ -567,9 +560,8 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
"SQLiteViewer.exportTableToCsv.FileName=File name: ",
"SQLiteViewer.exportTableToCsv.TableName=Table name: "
})
private void exportTableToCsv(File file, boolean overwrite) {
private void exportTableToCsv(File file) {
String tableName = (String) this.tablesDropdownList.getSelectedItem();
String csvFileSuffix = "_" + tableName + "_" + TimeStampUtils.createTimeStamp() + ".csv";
try (
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM " + tableName)) {
@ -578,42 +570,42 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) {
logger.log(Level.INFO, String.format("The table %s is empty. (objId=%d)", tableName, sqliteDbFile.getId())); //NON-NLS
} else {
String fileName = file.getName();
File csvFile;
if (overwrite) {
String fileName = file.getName();
if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) {
csvFile = file;
} else if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) {
csvFile = new File(file.getParentFile(), FilenameUtils.removeExtension(fileName) + csvFileSuffix);
} else {
csvFile = new File(file.toString() + csvFileSuffix);
csvFile = new File(file.toString() + ".csv");
}
FileOutputStream out = new FileOutputStream(csvFile, false);
out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes());
out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes());
// Set up the column names
Map<String, Object> row = currentTableRows.get(0);
StringBuffer header = new StringBuffer();
for (Map.Entry<String, Object> col : row.entrySet()) {
String colName = col.getKey();
if (header.length() > 0) {
header.append(',').append(colName);
} else {
header.append(colName);
}
}
out.write(header.append('\n').toString().getBytes());
try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
for (Map<String, Object> maps : currentTableRows) {
StringBuffer valueLine = new StringBuffer();
maps.values().forEach((value) -> {
if (valueLine.length() > 0) {
valueLine.append(',').append(value.toString());
out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes());
out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes());
// Set up the column names
Map<String, Object> row = currentTableRows.get(0);
StringBuffer header = new StringBuffer();
for (Map.Entry<String, Object> col : row.entrySet()) {
String colName = col.getKey();
if (header.length() > 0) {
header.append(',').append(colName);
} else {
valueLine.append(value.toString());
header.append(colName);
}
});
out.write(valueLine.append('\n').toString().getBytes());
}
out.write(header.append('\n').toString().getBytes());
for (Map<String, Object> maps : currentTableRows) {
StringBuffer valueLine = new StringBuffer();
maps.values().forEach((value) -> {
if (valueLine.length() > 0) {
valueLine.append(',').append(value.toString());
} else {
valueLine.append(value.toString());
}
});
out.write(valueLine.append('\n').toString().getBytes());
}
}
}
} catch (SQLException ex) {

View File

@ -312,7 +312,11 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
"BlackboardArtifactNode.createSheet.artifactDetails.name=Artifact Details",
"BlackboardArtifactNode.artifact.displayName=Artifact",
"BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5 Hash",
"BlackboardArtifactNode.createSheet.artifactMD5.name=MD5 Hash"})
"BlackboardArtifactNode.createSheet.artifactMD5.name=MD5 Hash",
"BlackboardArtifactNode.createSheet.fileSize.name=Size",
"BlackboardArtifactNode.createSheet.fileSize.displayName=Size",
"BlackboardArtifactNode.createSheet.path.displayName=Path",
"BlackboardArtifactNode.createSheet.path.name=Path"})
@Override
protected Sheet createSheet() {
@ -452,6 +456,31 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
dataSourceStr));
}
}
// If EXIF, add props for file size and path
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
long size = 0;
String path = ""; //NON-NLS
if (associated instanceof AbstractFile) {
AbstractFile af = (AbstractFile) associated;
size = af.getSize();
try {
path = af.getUniquePath();
} catch (TskCoreException ex) {
path = af.getParentPath();
}
}
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.displayName"),
NO_DESCR,
size));
sheetSet.put(new NodeProperty<>(
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.displayName"),
NO_DESCR,
path));
}
addTagProperty(sheetSet);

View File

@ -54,7 +54,6 @@ DataModelActionsFactory.srcFileInDir.text=View Source File in Directory
DataModelActionsFactory.fileInDir.text=View File in Directory
DataModelActionsFactory.viewNewWin.text=View in New Window
DataModelActionsFactory.openExtViewer.text=Open in External Viewer
DataModelActionsFactory.srfFileSameMD5.text=Search for files with the same MD5 hash
DataSourcesNode.name=Data Sources
DataSourcesNode.group_by_datasource.name=Data Source Files
DataSourcesNode.createSheet.name.name=Name

View File

@ -55,7 +55,6 @@ DataModelActionsFactory.srcFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u
DataModelActionsFactory.fileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a
DataModelActionsFactory.viewNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6\u306b\u8868\u793a
DataModelActionsFactory.openExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u306b\u8868\u793a
DataModelActionsFactory.srfFileSameMD5.text=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22
DataSourcesNode.name=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9
DataSourcesNode.group_by_datasource.name=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb
DataSourcesNode.createSheet.name.name=\u540d\u524d

View File

@ -1,95 +0,0 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018 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.datamodel;
import java.util.LinkedHashMap;
import java.util.Map;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
/**
* A dummy node used by the child factory to display while children are being
* generated
*/
public class CommonFileChildNodeLoading extends DisplayableItemNode {
public CommonFileChildNodeLoading(Children children) {
super(children);
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override
public String getItemType() {
return getClass().getName();
}
@Override
protected Sheet createSheet() {
Sheet sheet = new Sheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
Map<String, Object> map = new LinkedHashMap<>();
map.put(CommonFileChildLoadingPropertyType.File.toString(), "Loading...");
final String NO_DESCR = Bundle.AbstractFsContentNode_noDesc_text();
for (CommonFileChildLoadingPropertyType propType : CommonFileChildLoadingPropertyType.values()) {
final String propString = propType.toString();
sheetSet.put(new NodeProperty<>(propString, propString, NO_DESCR, map.get(propString)));
}
return sheet;
}
/**
* Represents the sole column for the 'dummy' loading node.
*/
@NbBundle.Messages({
"CommonFileChildLoadingPropertyType.fileColLbl=File"
})
public enum CommonFileChildLoadingPropertyType {
File(Bundle.CommonFileChildLoadingPropertyType_fileColLbl());
final private String displayString;
private CommonFileChildLoadingPropertyType(String displayString) {
this.displayString = displayString;
}
@Override
public String toString() {
return displayString;
}
}
}

View File

@ -38,7 +38,6 @@ import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.datamodel.Reports.ReportNode;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.HashSearchAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
import org.sleuthkit.datamodel.AbstractFile;
@ -76,8 +75,6 @@ public class DataModelActionsFactory {
.getMessage(DataModelActionsFactory.class, "DataModelActionsFactory.viewNewWin.text");
public static final String OPEN_IN_EXTERNAL_VIEWER = NbBundle
.getMessage(DataModelActionsFactory.class, "DataModelActionsFactory.openExtViewer.text");
public static final String SEARCH_FOR_FILES_SAME_MD5 = NbBundle
.getMessage(DataModelActionsFactory.class, "DataModelActionsFactory.srfFileSameMD5.text");
public static List<Action> getActions(File file, boolean isArtifactSource) {
List<Action> actionsList = new ArrayList<>();
@ -88,7 +85,6 @@ public class DataModelActionsFactory {
actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, fileNode));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(new HashSearchAction(SEARCH_FOR_FILES_SAME_MD5, fileNode));
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -367,7 +363,6 @@ public class DataModelActionsFactory {
actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, tagNode));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(new HashSearchAction(SEARCH_FOR_FILES_SAME_MD5, tagNode));
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -404,7 +399,6 @@ public class DataModelActionsFactory {
actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, tagNode));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(new HashSearchAction(SEARCH_FOR_FILES_SAME_MD5, tagNode));
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {

View File

@ -120,8 +120,6 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(CommonFilesNode cfn);
T visit(FileInstanceNode fin);
T visit(CommonFileChildNodeLoading cfcnl);
T visit(InstanceCountNode icn);
@ -213,11 +211,6 @@ public interface DisplayableItemNodeVisitor<T> {
return defaultVisit(icn);
}
@Override
public T visit(CommonFileChildNodeLoading cfcnl) {
return defaultVisit(cfcnl);
}
@Override
public T visit(DirectoryNode dn) {
return defaultVisit(dn);

View File

@ -34,7 +34,6 @@ import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.HashSearchAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction;
@ -166,7 +165,6 @@ public class FileNode extends AbstractFsContentNode<AbstractFile> {
actionsList.add(null); // Creates an item separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(new HashSearchAction(Bundle.FileNode_getActions_searchFilesSameMD5_text(), this));
actionsList.add(null); // Creates an item separator
actionsList.add(AddContentTagAction.getInstance());

View File

@ -36,7 +36,6 @@ import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.HashSearchAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction;
@ -107,8 +106,6 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(new HashSearchAction(
NbBundle.getMessage(this.getClass(), "LocalFileNode.getActions.searchFilesSameMd5.text"), this));
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());

View File

@ -120,3 +120,8 @@ AddExternalViewerRulePanel.exePathTextField.text=
AddExternalViewerRulePanel.exePathLabel.text=Path of the program to use for files with this type or extension
AddExternalViewerRulePanel.extRadioButton.text=Extension
DirectoryTreeTopComponent.groupByDatasourceCheckBox.text=Group by Data Source
GroupDataSourcesDialog.dataSourceCountLabel.text=jLabel1
GroupDataSourcesDialog.queryLabel.text=Would you like to group by data source for faster loading?
GroupDataSourcesDialog.yesButton.text=Yes
GroupDataSourcesDialog.noButton.text=No
GroupDataSourcesDialog.title=Group by Data Source?

View File

@ -401,10 +401,8 @@ public class DataResultFilterNode extends FilterNode {
}
Content c = ban.getLookup().lookup(File.class);
Node n = null;
boolean md5Action = false;
if (c != null) {
n = new FileNode((AbstractFile) c);
md5Action = true;
} else if ((c = ban.getLookup().lookup(Directory.class)) != null) {
n = new DirectoryNode((Directory) c);
} else if ((c = ban.getLookup().lookup(VirtualDirectory.class)) != null) {
@ -438,10 +436,6 @@ public class DataResultFilterNode extends FilterNode {
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.openInExtViewer.text"), n));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
if (md5Action) {
actionsList.add(new HashSearchAction(
NbBundle.getMessage(this.getClass(), "DataResultFilterNode.action.searchFilesSameMd5.text"), n));
}
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
actionsList.add(AddBlackboardArtifactTagAction.getInstance());

View File

@ -24,6 +24,11 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
@ -35,6 +40,7 @@ import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.prefs.PreferenceChangeEvent;
import java.util.prefs.PreferenceChangeListener;
import java.util.Properties;
import javax.swing.Action;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
@ -62,6 +68,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataExplorer;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.datamodel.ArtifactNodeSelectionInfo;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.datamodel.CreditCards;
@ -103,6 +110,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
private static final Logger LOGGER = Logger.getLogger(DirectoryTreeTopComponent.class.getName());
private AutopsyTreeChildrenFactory autopsyTreeChildrenFactory;
private Children autopsyTreeChildren;
private static final long DEFAULT_DATASOURCE_GROUPING_THRESHOLD = 5; // Threshold for prompting the user about grouping by data source
private static final String GROUPING_THRESHOLD_NAME = "GroupDataSourceThreshold";
private static final String SETTINGS_FILE = "CasePreferences.properties"; //NON-NLS
/**
* the constructor
@ -369,7 +379,51 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
public int getPersistenceType() {
return TopComponent.PERSISTENCE_NEVER;
}
/**
* Ask the user if they want to group by data source when opening a large case.
*
* @param currentCase
* @param dataSourceCount
*/
private void promptForDataSourceGrouping(Case currentCase, int dataSourceCount) {
Path settingsFile = Paths.get(currentCase.getConfigDirectory(), SETTINGS_FILE); //NON-NLS
if (settingsFile.toFile().exists()) {
// Read the setting
try (InputStream inputStream = Files.newInputStream(settingsFile)) {
Properties props = new Properties();
props.load(inputStream);
if (props.getProperty("groupByDataSource", "false").equals("true")) {
UserPreferences.setGroupItemsInTreeByDatasource(true);
groupByDatasourceCheckBox.setSelected(true);
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, "Error reading settings file", ex);
}
} else {
GroupDataSourcesDialog dialog = new GroupDataSourcesDialog(dataSourceCount);
dialog.display();
if (dialog.groupByDataSourceSelected()) {
UserPreferences.setGroupItemsInTreeByDatasource(true);
groupByDatasourceCheckBox.setSelected(true);
}
// Save the response
Properties props = new Properties();
if(dialog.groupByDataSourceSelected()) {
props.setProperty("groupByDataSource", "true");
} else {
props.setProperty("groupByDataSource", "false");
}
try (OutputStream fos = Files.newOutputStream(settingsFile)) {
props.store(fos, ""); //NON-NLS
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, "Error writing settings file", ex);
}
}
}
/**
* Called only when top component was closed on all workspaces before and
* now is opened for the first time on some workspace. The intent is to
@ -377,6 +431,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
* existing workspaces. Subclasses will usually perform initializing tasks
* here.
*/
@NbBundle.Messages({"# {0} - dataSourceCount",
"DirectoryTreeTopComponent.componentOpened.groupDataSources.text=This case contains {0} data sources. Would you like to group by data source for faster loading?",
"DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source?"})
@Override
public void componentOpened() {
// change the cursor to "waiting cursor" for this operation
@ -392,6 +449,31 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
if (null == currentCase || currentCase.hasData() == false) {
getTree().setRootVisible(false); // hide the root
} else {
// If the case contains a lot of data sources, and they aren't already grouping
// by data source, give the user the option to do so before loading the tree.
if (RuntimeProperties.runningWithGUI()) {
long threshold = DEFAULT_DATASOURCE_GROUPING_THRESHOLD;
if (ModuleSettings.settingExists(ModuleSettings.MAIN_SETTINGS, GROUPING_THRESHOLD_NAME)) {
try {
threshold = Long.parseLong(ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, GROUPING_THRESHOLD_NAME));
} catch (NumberFormatException ex) {
LOGGER.log(Level.SEVERE, "Group data sources threshold is not a number", ex);
}
} else {
ModuleSettings.setConfigSetting(ModuleSettings.MAIN_SETTINGS, GROUPING_THRESHOLD_NAME, String.valueOf(threshold));
}
try {
int dataSourceCount = currentCase.getDataSources().size();
if (! UserPreferences.groupItemsInTreeByDatasource() &&
dataSourceCount > threshold) {
promptForDataSourceGrouping(currentCase, dataSourceCount);
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error loading data sources", ex);
}
}
// if there's at least one image, load the image and open the top componen
autopsyTreeChildrenFactory = new AutopsyTreeChildrenFactory();
autopsyTreeChildren = Children.create(autopsyTreeChildrenFactory, true);

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="2"/>
<Property name="title" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="GroupDataSourcesDialog.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="queryLabel" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="dataSourceCountLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="yesButton" min="-2" pref="76" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="noButton" min="-2" pref="76" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="dataSourceCountLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="queryLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="yesButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="noButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="dataSourceCountLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="GroupDataSourcesDialog.dataSourceCountLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="queryLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="GroupDataSourcesDialog.queryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="yesButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="GroupDataSourcesDialog.yesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="yesButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="noButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="GroupDataSourcesDialog.noButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="noButtonActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,147 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 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.directorytree;
import javax.swing.JFrame;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
/**
*
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class GroupDataSourcesDialog extends javax.swing.JDialog {
boolean shouldGroupByDataSource = false;
/**
* Creates new form GroupDataSourcesDialog
*/
@NbBundle.Messages({"# {0} - dataSourceCount",
"GroupDataSourcesDialog.groupDataSources.text=This case contains {0} data sources."})
GroupDataSourcesDialog(int dataSourceCount) {
super((JFrame) WindowManager.getDefault().getMainWindow());
initComponents();
dataSourceCountLabel.setText(Bundle.GroupDataSourcesDialog_groupDataSources_text(dataSourceCount));
}
/**
* Display the dialog.
*/
void display() {
setModal(true);
setSize(getPreferredSize());
setLocationRelativeTo(this.getParent());
setAlwaysOnTop(false);
pack();
setVisible(true);
}
boolean groupByDataSourceSelected() {
return shouldGroupByDataSource;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
dataSourceCountLabel = new javax.swing.JLabel();
queryLabel = new javax.swing.JLabel();
yesButton = new javax.swing.JButton();
noButton = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setTitle(org.openide.util.NbBundle.getMessage(GroupDataSourcesDialog.class, "GroupDataSourcesDialog.title")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(dataSourceCountLabel, org.openide.util.NbBundle.getMessage(GroupDataSourcesDialog.class, "GroupDataSourcesDialog.dataSourceCountLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(queryLabel, org.openide.util.NbBundle.getMessage(GroupDataSourcesDialog.class, "GroupDataSourcesDialog.queryLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(yesButton, org.openide.util.NbBundle.getMessage(GroupDataSourcesDialog.class, "GroupDataSourcesDialog.yesButton.text")); // NOI18N
yesButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
yesButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(noButton, org.openide.util.NbBundle.getMessage(GroupDataSourcesDialog.class, "GroupDataSourcesDialog.noButton.text")); // NOI18N
noButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
noButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(queryLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(dataSourceCountLabel)
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addComponent(yesButton, javax.swing.GroupLayout.PREFERRED_SIZE, 76, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(noButton, javax.swing.GroupLayout.PREFERRED_SIZE, 76, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(dataSourceCountLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(queryLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(yesButton)
.addComponent(noButton))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void yesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_yesButtonActionPerformed
shouldGroupByDataSource = true;
dispose();
}//GEN-LAST:event_yesButtonActionPerformed
private void noButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_noButtonActionPerformed
shouldGroupByDataSource = false;
dispose();
}//GEN-LAST:event_noButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel dataSourceCountLabel;
private javax.swing.JButton noButton;
private javax.swing.JLabel queryLabel;
private javax.swing.JButton yesButton;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,52 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 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.directorytree;
import java.awt.event.ActionEvent;
import javax.annotation.concurrent.Immutable;
import javax.swing.AbstractAction;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbSearchAction;
/**
* Action to lookup the interface and call the real action in HashDatabase. The
* real action, HashDbSearchAction, implements HashSearchProvider, and should be
* the only instance of it.
*
* //TODO: HashDBSearchAction needs a public constructor and a service
* registration annotation for the lookup technique to work
*/
@Immutable
public class HashSearchAction extends AbstractAction {
private final Node contentNode;
public HashSearchAction(String title, Node contentNode) {
super(title);
this.contentNode = contentNode;
}
@Override
public void actionPerformed(ActionEvent e) {
//HashSearchProvider searcher = Lookup.getDefault().lookup(HashSearchProvider.class);
//TODO: HashDBSearchAction needs a public constructor and a service registration annotation for the above technique to work
HashDbSearchAction searcher = HashDbSearchAction.getDefault();
searcher.search(contentNode);
}
}

View File

@ -18,8 +18,6 @@
*/
package org.sleuthkit.autopsy.filesearch;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
@ -29,7 +27,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.JList;
import javax.swing.event.ListSelectionEvent;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -47,7 +44,6 @@ public class DataSourcePanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(DataSourcePanel.class.getName());
private static final long serialVersionUID = 1L;
private final Map<Long, String> dataSourceMap = new HashMap<>();
private final List<String> toolTipList = new ArrayList<>();
/**
* Creates new form DataSourcePanel
@ -57,28 +53,10 @@ public class DataSourcePanel extends javax.swing.JPanel {
this.dataSourceList.addListSelectionListener((ListSelectionEvent evt) -> {
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
});
this.dataSourceList.addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseDragged(MouseEvent evt) {
//Unused by now
}
@Override
public void mouseMoved(MouseEvent evt) {
if (evt.getSource() instanceof JList<?>) {
JList<?> dsList = (JList<?>) evt.getSource();
int index = dsList.locationToIndex(evt.getPoint());
if (index > -1) {
dsList.setToolTipText(toolTipList.get(index));
}
}
}
});
}
/**
* Get dataSourceMap with object id and data source display name. Add the data source full name to toolTipList
* Get dataSourceMap with object id and data source display name.
*
* @return The list of data source name
*/
@ -93,8 +71,7 @@ public class DataSourcePanel extends javax.swing.JPanel {
String dsName = ds.getName();
File dataSourceFullName = new File(dsName);
String displayName = dataSourceFullName.getName();
dataSourceMap.put(ds.getId(), displayName);
toolTipList.add(dsName);
dataSourceMap.put(ds.getId(), displayName);
dsList.add(displayName);
}
} catch (NoCurrentCaseException ex) {

View File

@ -556,17 +556,29 @@ class ReportHTML implements TableReportModule {
}
/**
* Add a row to the current table.
* Add a row to the current table, escaping the text to be contained in the
* row.
*
* @param row values for each cell in the row
*/
@Override
public void addRow(List<String> row) {
addRow(row, true);
}
/**
* Add a row to the current table.
*
* @param row values for each cell in the row
* @param escapeText whether or not the text of the row should be escaped,
* true for escaped, false for not escaped
*/
private void addRow(List<String> row, boolean escapeText) {
StringBuilder builder = new StringBuilder();
builder.append("\t<tr>\n"); //NON-NLS
for (String cell : row) {
String escapeHTMLCell = EscapeUtil.escapeHtml(cell);
builder.append("\t\t<td>").append(escapeHTMLCell).append("</td>\n"); //NON-NLS
String cellText = escapeText ? EscapeUtil.escapeHtml(cell) : cell;
builder.append("\t\t<td>").append(cellText).append("</td>\n"); //NON-NLS
}
builder.append("\t</tr>\n"); //NON-NLS
rowCount++;
@ -593,7 +605,7 @@ class ReportHTML implements TableReportModule {
public void addRowWithTaggedContentHyperlink(List<String> row, ContentTag contentTag) {
Content content = contentTag.getContent();
if (content instanceof AbstractFile == false) {
addRow(row);
addRow(row, true);
return;
}
AbstractFile file = (AbstractFile) content;
@ -647,7 +659,7 @@ class ReportHTML implements TableReportModule {
int pages = 1;
for (Content content : images) {
if (currentRow.size() == THUMBNAIL_COLUMNS) {
addRow(currentRow);
addRow(currentRow, false);
currentRow.clear();
}
@ -727,7 +739,7 @@ class ReportHTML implements TableReportModule {
// Finish out the row.
currentRow.add("");
}
addRow(currentRow);
addRow(currentRow, false);
}
// manually set rowCount to be the total number of images.
@ -1094,8 +1106,8 @@ class ReportHTML implements TableReportModule {
summary.append("<div class=\"title\">\n"); //NON-NLS
summary.append(writeSummaryCaseDetails());
summary.append(writeSummaryImageInfo());
summary.append(writeSummarySoftwareInfo(skCase,ingestJobs));
summary.append(writeSummaryIngestHistoryInfo(skCase,ingestJobs));
summary.append(writeSummarySoftwareInfo(skCase, ingestJobs));
summary.append(writeSummaryIngestHistoryInfo(skCase, ingestJobs));
if (generatorLogoSet) {
summary.append("<div class=\"left\">\n"); //NON-NLS
summary.append("<img src=\"generator_logo.png\" />\n"); //NON-NLS
@ -1126,14 +1138,13 @@ class ReportHTML implements TableReportModule {
}
}
}
/**
* Write the case details section of the summary for this report.
*
*
* @return StringBuilder updated html report with case details
*/
private StringBuilder writeSummaryCaseDetails(){
private StringBuilder writeSummaryCaseDetails() {
StringBuilder summary = new StringBuilder();
String caseName = currentCase.getDisplayName();
String caseNumber = currentCase.getNumber();
@ -1146,40 +1157,39 @@ class ReportHTML implements TableReportModule {
imagecount = 0;
}
summary.append("<div class=\"title\">\n"); //NON-NLS
if (agencyLogoSet) {
summary.append("<div class=\"left\">\n"); //NON-NLS
summary.append("<img src=\"");
summary.append(Paths.get(reportBranding.getAgencyLogoPath()).getFileName().toString());
summary.append("\" />\n"); //NON-NLS
summary.append("</div>\n"); //NON-NLS
}
final String align = agencyLogoSet ? "right" : "left"; //NON-NLS NON-NLS
summary.append("<div class=\"").append(align).append("\">\n"); //NON-NLS
summary.append("<table>\n"); //NON-NLS
summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.caseName")) //NON-NLS
.append("</td><td>").append(caseName).append("</td></tr>\n"); //NON-NLS NON-NLS
summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.caseNum")) //NON-NLS
.append("</td><td>").append(!caseNumber.isEmpty() ? caseNumber : NbBundle //NON-NLS
.getMessage(this.getClass(), "ReportHTML.writeSum.noCaseNum")).append("</td></tr>\n"); //NON-NLS
summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.examiner")).append("</td><td>") //NON-NLS
.append(!examiner.isEmpty() ? examiner : NbBundle
.getMessage(this.getClass(), "ReportHTML.writeSum.noExaminer"))
.append("</td></tr>\n"); //NON-NLS
summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.numImages")) //NON-NLS
.append("</td><td>").append(imagecount).append("</td></tr>\n"); //NON-NLS
summary.append("</table>\n"); //NON-NLS
if (agencyLogoSet) {
summary.append("<div class=\"left\">\n"); //NON-NLS
summary.append("<img src=\"");
summary.append(Paths.get(reportBranding.getAgencyLogoPath()).getFileName().toString());
summary.append("\" />\n"); //NON-NLS
summary.append("</div>\n"); //NON-NLS
summary.append("<div class=\"clear\"></div>\n"); //NON-NLS
summary.append("</div>\n"); //NON-NLS
return summary;
}
final String align = agencyLogoSet ? "right" : "left"; //NON-NLS NON-NLS
summary.append("<div class=\"").append(align).append("\">\n"); //NON-NLS
summary.append("<table>\n"); //NON-NLS
summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.caseName")) //NON-NLS
.append("</td><td>").append(caseName).append("</td></tr>\n"); //NON-NLS NON-NLS
summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.caseNum")) //NON-NLS
.append("</td><td>").append(!caseNumber.isEmpty() ? caseNumber : NbBundle //NON-NLS
.getMessage(this.getClass(), "ReportHTML.writeSum.noCaseNum")).append("</td></tr>\n"); //NON-NLS
summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.examiner")).append("</td><td>") //NON-NLS
.append(!examiner.isEmpty() ? examiner : NbBundle
.getMessage(this.getClass(), "ReportHTML.writeSum.noExaminer"))
.append("</td></tr>\n"); //NON-NLS
summary.append("<tr><td>").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.numImages")) //NON-NLS
.append("</td><td>").append(imagecount).append("</td></tr>\n"); //NON-NLS
summary.append("</table>\n"); //NON-NLS
summary.append("</div>\n"); //NON-NLS
summary.append("<div class=\"clear\"></div>\n"); //NON-NLS
summary.append("</div>\n"); //NON-NLS
return summary;
}
/**
* Write the Image Information section of the summary for this report.
*
*
* @return StringBuilder updated html report with Image Information
*/
private StringBuilder writeSummaryImageInfo() {
StringBuilder summary = new StringBuilder();
summary.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.imageInfoHeading"));
@ -1208,13 +1218,12 @@ class ReportHTML implements TableReportModule {
summary.append("</div>\n"); //NON-NLS
return summary;
}
/**
* Write the software information section of the summary for this report.
*
*
* @return StringBuilder updated html report with software information
*/
private StringBuilder writeSummarySoftwareInfo(SleuthkitCase skCase, List<IngestJobInfo> ingestJobs) {
StringBuilder summary = new StringBuilder();
summary.append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.softwareInfoHeading"));
@ -1244,13 +1253,12 @@ class ReportHTML implements TableReportModule {
summary.append("<div class=\"clear\"></div>\n"); //NON-NLS
return summary;
}
/**
* Write the Ingest History section of the summary for this report.
*
*
* @return StringBuilder updated html report with ingest history
*/
private StringBuilder writeSummaryIngestHistoryInfo(SleuthkitCase skCase, List<IngestJobInfo> ingestJobs) {
StringBuilder summary = new StringBuilder();
try {
@ -1304,4 +1312,4 @@ class ReportHTML implements TableReportModule {
+ thumbFile.getName();
}
}
}

View File

@ -33,7 +33,6 @@ import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.LocalDiskDSProcessor;
import org.sleuthkit.autopsy.casemodule.LocalFilesDSProcessor;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import static org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS;

View File

@ -2790,6 +2790,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
sysLogger.log(Level.INFO, "Finished ingest modules analysis for {0} ", manifestPath);
IngestJob.ProgressSnapshot jobSnapshot = ingestJob.getSnapshot();
for (IngestJob.ProgressSnapshot.DataSourceProcessingSnapshot snapshot : jobSnapshot.getDataSourceSnapshots()) {
AutoIngestJobLogger nestedJobLogger = new AutoIngestJobLogger(manifestPath, snapshot.getDataSource(), caseDirectoryPath);
if (!snapshot.isCancelled()) {
List<String> cancelledModules = snapshot.getCancelledDataSourceIngestModules();
if (!cancelledModules.isEmpty()) {
@ -2798,15 +2799,15 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen
setCaseNodeDataErrorsOccurred(caseDirectoryPath);
for (String module : snapshot.getCancelledDataSourceIngestModules()) {
sysLogger.log(Level.WARNING, String.format("%s ingest module cancelled for %s", module, manifestPath));
jobLogger.logIngestModuleCancelled(module);
nestedJobLogger.logIngestModuleCancelled(module);
}
}
jobLogger.logAnalysisCompleted();
nestedJobLogger.logAnalysisCompleted();
} else {
currentJob.setProcessingStage(AutoIngestJob.Stage.CANCELLING, Date.from(Instant.now()));
currentJob.setErrorsOccurred(true);
setCaseNodeDataErrorsOccurred(caseDirectoryPath);
jobLogger.logAnalysisCancelled();
nestedJobLogger.logAnalysisCancelled();
CancellationReason cancellationReason = snapshot.getCancellationReason();
if (CancellationReason.NOT_CANCELLED != cancellationReason && CancellationReason.USER_CANCELLED != cancellationReason) {
throw new AnalysisStartupException(String.format("Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), manifestPath));

View File

@ -106,10 +106,22 @@ public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter
try {
imageInMemory = IOUtils.toByteArray(inputStream);
} catch (IOException ex) {
logger.log(Level.WARNING, "Unable to perform object detection on " + file.getName(), ex);
logger.log(Level.WARNING, "Unable to read image to byte array for performing object detection on " + file.getParentPath() + file.getName() + " with object id of " + file.getId(), ex);
return IngestModule.ProcessResult.ERROR;
}
Mat originalImage = Highgui.imdecode(new MatOfByte(imageInMemory), Highgui.IMREAD_GRAYSCALE);
Mat originalImage;
try {
originalImage = Highgui.imdecode(new MatOfByte(imageInMemory), Highgui.IMREAD_GRAYSCALE);
} catch (CvException ex) {
//The image was something which could not be decoded by OpenCv, our isImageThumbnailSupported(file) check above failed us
logger.log(Level.WARNING, "Unable to decode image from byte array to perform object detection on " + file.getParentPath() + file.getName() + " with object id of " + file.getId(), ex); //NON-NLS
return IngestModule.ProcessResult.ERROR;
} catch (Exception unexpectedException) {
//hopefully an unnecessary generic exception catch but currently present to catch any exceptions OpenCv throws which may not be documented
logger.log(Level.SEVERE, "Unexpected Exception encountered attempting to use OpenCV to decode picture: " + file.getParentPath() + file.getName() + " with object id of " + file.getId(), unexpectedException);
return IngestModule.ProcessResult.ERROR;
}
MatOfRect detectionRectangles = new MatOfRect(); //the rectangles which reprent the coordinates on the image for where objects were detected
for (String classifierKey : classifiers.keySet()) {
//apply each classifier to the file
@ -117,7 +129,11 @@ public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter
classifiers.get(classifierKey).detectMultiScale(originalImage, detectionRectangles);
} catch (CvException ignored) {
//The image was likely an image which we are unable to generate a thumbnail for, and the classifier was likely one where that is not acceptable
logger.log(Level.INFO, String.format("Classifier '%s' could not be applied to file '%s'.", classifierKey, file.getParentPath() + file.getName())); //NON-NLS
logger.log(Level.INFO, String.format("Classifier '%s' could not be applied to file '%s'.", classifierKey, file.getParentPath() + file.getName() + " with object id of " + file.getId())); //NON-NLS
continue;
} catch (Exception unexpectedException) {
//hopefully an unnecessary generic exception catch but currently present to catch any exceptions OpenCv throws which may not be documented
logger.log(Level.SEVERE, "Unexpected Exception encountered for image " + file.getParentPath() + file.getName() + " with object id of " + file.getId() +" while trying to apply classifier " + classifierKey, unexpectedException);
continue;
}

View File

@ -352,8 +352,9 @@ public class GroupManager {
case MIME_TYPE:
if (nonNull(db)) {
HashSet<String> types = new HashSet<>();
// Use the group_concat function to get a list of files for each mime type.
// This has sifferent syntax on Postgres vs SQLite
// This has different syntax on Postgres vs SQLite
String groupConcatClause;
if (DbType.POSTGRESQL == controller.getSleuthKitCase().getDatabaseType()) {
groupConcatClause = " array_to_string(array_agg(obj_id), ',') as object_ids";
@ -361,8 +362,8 @@ public class GroupManager {
else {
groupConcatClause = " group_concat(obj_id) as object_ids";
}
String querySQL = "select " + groupConcatClause + ", mime_type from tsk_files group by mime_type ";
try (SleuthkitCase.CaseDbQuery executeQuery = controller.getSleuthKitCase().executeQuery(querySQL); //NON-NLS
String query = "select " + groupConcatClause + " , mime_type from tsk_files group by mime_type ";
try (SleuthkitCase.CaseDbQuery executeQuery = controller.getSleuthKitCase().executeQuery(query); //NON-NLS
ResultSet resultSet = executeQuery.getResultSet();) {
while (resultSet.next()) {
final String mimeType = resultSet.getString("mime_type"); //NON-NLS

View File

@ -36,7 +36,6 @@ import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.directorytree.HashSearchAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
@ -48,7 +47,6 @@ import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.LocalFile;
import org.sleuthkit.datamodel.Report;
import org.sleuthkit.datamodel.SlackFile;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.VirtualDirectory;
/**
@ -126,50 +124,45 @@ class AdHocSearchFilterNode extends FilterNode {
@Override
public List<Action> visit(File f) {
return getFileActions(true);
return getFileActions();
}
@Override
public List<Action> visit(DerivedFile f) {
return getFileActions(true);
return getFileActions();
}
@Override
public List<Action> visit(Directory d) {
return getFileActions(false);
return getFileActions();
}
@Override
public List<Action> visit(LayoutFile lf) {
//we want hashsearch enabled on carved files but not unallocated blocks
boolean enableHashSearch = (lf.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.CARVED);
return getFileActions(enableHashSearch);
return getFileActions();
}
@Override
public List<Action> visit(LocalFile lf) {
return getFileActions(true);
return getFileActions();
}
@Override
public List<Action> visit(SlackFile f) {
return getFileActions(false);
return getFileActions();
}
@Override
public List<Action> visit(VirtualDirectory dir) {
return getFileActions(false);
return getFileActions();
}
private List<Action> getFileActions(boolean enableHashSearch) {
private List<Action> getFileActions() {
List<Action> actionsList = new ArrayList<>();
actionsList.add(new NewWindowViewAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.viewInNewWinActionLbl"), AdHocSearchFilterNode.this));
actionsList.add(new ExternalViewerAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.openExternViewActLbl"), getOriginal()));
actionsList.add(null);
actionsList.add(ExtractAction.getInstance());
Action hashSearchAction = new HashSearchAction(NbBundle.getMessage(this.getClass(), "KeywordSearchFilterNode.getFileActions.searchSameMd5"), getOriginal());
hashSearchAction.setEnabled(enableHashSearch);
actionsList.add(hashSearchAction);
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
@ -185,7 +178,7 @@ class AdHocSearchFilterNode extends FilterNode {
@Override
protected List<Action> defaultVisit(Content c) {
return getFileActions(false);
return getFileActions();
}
}
}

View File

@ -172,8 +172,7 @@ abstract class AdHocSearchPanel extends javax.swing.JPanel {
}
/**
* Get dataSourceMap with object id and data source display name. Add the
* data source full name to toolTipList
* Get dataSourceMap with object id and data source display name.
*
* @return The list of data source name
*/

View File

@ -18,11 +18,11 @@
*/
package org.sleuthkit.autopsy.keywordsearch;
import java.awt.*;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
@ -32,7 +32,6 @@ import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.JCheckBox;
import javax.swing.JList;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
@ -667,11 +666,21 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
* Set the dataSourceList enabled if the dataSourceCheckBox is selected
*/
private void setComponentsEnabled() {
boolean enabled = this.dataSourceCheckBox.isSelected();
this.dataSourceList.setEnabled(enabled);
if (enabled) {
this.dataSourceList.setSelectionInterval(0, this.dataSourceList.getModel().getSize()-1);
if (getDataSourceListModel().size() > 1) {
this.dataSourceCheckBox.setEnabled(true);
boolean enabled = this.dataSourceCheckBox.isSelected();
this.dataSourceList.setEnabled(enabled);
if (enabled) {
this.dataSourceList.setSelectionInterval(0, this.dataSourceList.getModel().getSize()-1);
} else {
this.dataSourceList.setSelectedIndices(new int[0]);
}
} else {
this.dataSourceCheckBox.setEnabled(false);
this.dataSourceCheckBox.setSelected(false);
this.dataSourceList.setEnabled(false);
this.dataSourceList.setSelectedIndices(new int[0]);
}
}

View File

@ -23,8 +23,6 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
@ -32,7 +30,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.event.ListSelectionEvent;
import org.openide.util.NbBundle;
@ -374,18 +371,26 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
}
setComponentsEnabled();
firePropertyChange(Bundle.DropdownSingleTermSearchPanel_selected(), null, null);
}
/**
* Set the dataSourceList enabled if the dataSourceCheckBox is selected
*/
private void setComponentsEnabled() {
boolean enabled = this.dataSourceCheckBox.isSelected();
this.dataSourceList.setEnabled(enabled);
if (enabled) {
this.dataSourceList.setSelectionInterval(0, this.dataSourceList.getModel().getSize()-1);
if (getDataSourceListModel().size() > 1) {
this.dataSourceCheckBox.setEnabled(true);
boolean enabled = this.dataSourceCheckBox.isSelected();
this.dataSourceList.setEnabled(enabled);
if (enabled) {
this.dataSourceList.setSelectionInterval(0, this.dataSourceList.getModel().getSize()-1);
} else {
this.dataSourceList.setSelectedIndices(new int[0]);
}
} else {
this.dataSourceCheckBox.setEnabled(false);
this.dataSourceCheckBox.setSelected(false);
this.dataSourceList.setEnabled(false);
this.dataSourceList.setSelectedIndices(new int[0]);
}
}

View File

@ -28,7 +28,6 @@ import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.ingest.FileIngestModule;
@ -92,6 +91,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
private boolean startedSearching = false;
private List<ContentTextExtractor> textExtractors;
private StringsTextExtractor stringExtractor;
private TextFileExtractor txtFileExtractor;
private final KeywordSearchJobSettings settings;
private boolean initialized = false;
private long jobId;
@ -244,6 +244,8 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
stringExtractor.setScripts(KeywordSearchSettings.getStringExtractScripts());
stringExtractor.setOptions(KeywordSearchSettings.getStringExtractOptions());
txtFileExtractor = new TextFileExtractor();
textExtractors = new ArrayList<>();
//order matters, more specific extractors first
textExtractors.add(new HtmlTextExtractor());
@ -343,7 +345,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
textExtractors.clear();
textExtractors = null;
stringExtractor = null;
txtFileExtractor = null;
initialized = false;
}
@ -568,6 +570,17 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
putIngestStatus(jobId, aFile.getId(), IngestStatus.SKIPPED_ERROR_TEXTEXTRACT);
}
if ((wasTextAdded == false) && (aFile.getNameExtension().equalsIgnoreCase("txt"))) {
try {
if (Ingester.getDefault().indexText(txtFileExtractor, aFile, context)) {
putIngestStatus(jobId, aFile.getId(), IngestStatus.TEXT_INGESTED);
wasTextAdded = true;
}
} catch (IngesterException ex) {
logger.log(Level.WARNING, "Unable to index as unicode", ex);
}
}
// if it wasn't supported or had an error, default to strings
if (wasTextAdded == false) {
extractStringsAndIndex(aFile);

View File

@ -0,0 +1,80 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 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.keywordsearch;
import java.io.IOException;
import java.io.Reader;
import java.util.logging.Level;
import org.apache.tika.parser.txt.CharsetDetector;
import org.apache.tika.parser.txt.CharsetMatch;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ReadContentInputStream;
/**
* Extract text from .txt files
*/
final class TextFileExtractor extends ContentTextExtractor {
//Set a Minimum confidence value to reject matches that may not have a valid text encoding
//Values of valid text encodings were generally 100, xml code sometimes had a value around 50,
//and pictures and other files with a .txt extention were showing up with a value of 5 or less in limited testing.
//This limited information was used to select the current value as one that would filter out clearly non-text
//files while hopefully working on all files with a valid text encoding
static final private int MIN_MATCH_CONFIDENCE = 20;
static final private Logger logger = Logger.getLogger(TextFileExtractor.class.getName());
@Override
boolean isContentTypeSpecific() {
return true;
}
@Override
boolean isSupported(Content file, String detectedFormat) {
return true;
}
@Override
public Reader getReader(Content source) throws TextExtractorException {
CharsetDetector detector = new CharsetDetector();
ReadContentInputStream stream = new ReadContentInputStream(source);
try {
detector.setText(stream);
} catch (IOException ex) {
throw new TextExtractorException("Unable to get string from detected text in UnicodeTextExtractor", ex);
}
CharsetMatch match = detector.detect();
if (match.getConfidence() < MIN_MATCH_CONFIDENCE) {
throw new TextExtractorException("Text does not match any character set with a high enough confidence for UnicodeTextExtractor");
}
return match.getReader();
}
@Override
public boolean isDisabled() {
return false;
}
@Override
public void logWarning(String msg, Exception ex) {
logger.log(Level.WARNING, msg, ex);
}
}

View File

@ -1,15 +1,15 @@
/*
* Autopsy Forensic Browser
*
*
* Copyright 2012-2014 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.
@ -237,8 +237,19 @@ class SearchEngineURLQueryAnalyzer extends Extract {
try { //try to decode the url
String decoded = URLDecoder.decode(x, "UTF-8"); //NON-NLS
return decoded;
} catch (UnsupportedEncodingException uee) { //if it fails, return the encoded string
logger.log(Level.FINE, "Error during URL decoding ", uee); //NON-NLS
} catch (UnsupportedEncodingException exception) { //if it fails, return the encoded string
logger.log(Level.FINE, "Error during URL decoding, returning undecoded value:"
+ "\n\tURL: " + url
+ "\n\tUndecoded value: " + x
+ "\n\tEngine name: " + eng.getEngineName()
+ "\n\tEngine domain: " + eng.getDomainSubstring(), exception); //NON-NLS
return x;
} catch (IllegalArgumentException exception) { //if it fails, return the encoded string
logger.log(Level.SEVERE, "Illegal argument passed to URL decoding, returning undecoded value:"
+ "\n\tURL: " + url
+ "\n\tUndecoded value: " + x
+ "\n\tEngine name: " + eng.getEngineName()
+ "\n\tEngine domain: " + eng.getDomainSubstring(), exception); //NON-NLS)
return x;
}
}