diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageAction.java index ae57423b5b..f6a102338e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageAction.java @@ -34,6 +34,10 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.openide.DialogDisplayer; import org.openide.WizardDescriptor; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.awt.ActionReferences; +import org.openide.awt.ActionRegistration; import org.openide.util.ChangeSupport; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; @@ -53,6 +57,10 @@ import org.sleuthkit.datamodel.Image; */ // TODO: need annotation because there's a "Lookup.getDefault().lookup(AddImageAction.class)" // used in AddImageWizardPanel1 (among other places). It really shouldn't be done like that. +@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.casemodule.AddImageAction") +@ActionRegistration(displayName = "#CTL_AddImage", lazy = false) +@ActionReferences(value = { + @ActionReference(path = "Toolbars/Case", position = 100)}) @ServiceProvider(service = AddImageAction.class) public final class AddImageAction extends CallableSystemAction implements Presenter.Toolbar { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java index c9a0504c59..86cc38c251 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseCloseAction.java @@ -36,11 +36,19 @@ import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.windows.WindowManager; import java.awt.Cursor; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.awt.ActionReferences; +import org.openide.awt.ActionRegistration; /** * The action to close the current Case. This class should be disabled on * creation and it will be enabled on new case creation or case opened. */ +@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.casemodule.CaseCloseAction") +@ActionRegistration(displayName = "#CTL_CaseCloseAct", lazy = false) +@ActionReferences(value = { + @ActionReference(path = "Toolbars/Case", position = 104)}) public final class CaseCloseAction extends CallableSystemAction implements Presenter.Toolbar { JButton toolbarButton = new JButton(); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseConverter.java b/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseConverter.java index 95d94d1f23..e39f0d5ebb 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseConverter.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseConverter.java @@ -26,6 +26,7 @@ import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.text.SimpleDateFormat; @@ -39,6 +40,7 @@ import org.sleuthkit.autopsy.core.UserPreferencesException; import org.sleuthkit.datamodel.CaseDbConnectionInfo; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.autopsy.coreutils.NetworkUtils; +import org.sleuthkit.datamodel.TskData; /** * Import a case from single-user to multi-user. @@ -489,9 +491,18 @@ public class SingleUserCaseConverter { if (value > biggestPK) { biggestPK = value; } - outputStatement.executeUpdate("INSERT INTO tsk_files_path (obj_id, path) VALUES (" //NON-NLS + + // If the entry contains an encoding type, copy it. Otherwise use NONE. + // The test on column count can be removed if we upgrade the database before conversion. + int encoding = TskData.EncodingType.NONE.getType(); + ResultSetMetaData rsMetaData = inputResultSet.getMetaData(); + if(rsMetaData.getColumnCount() == 3){ + encoding = inputResultSet.getInt(3); + } + outputStatement.executeUpdate("INSERT INTO tsk_files_path (obj_id, path, encoding_type) VALUES (" //NON-NLS + value + ", '" - + SleuthkitCase.escapeSingleQuotes(inputResultSet.getString(2)) + "')"); //NON-NLS + + SleuthkitCase.escapeSingleQuotes(inputResultSet.getString(2)) + "', " + + encoding + ")"); //NON-NLS } catch (SQLException ex) { if (ex.getErrorCode() != 0) { // 0 if the entry already exists throw new SQLException(ex); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java index 2787924d93..e10e3d7ad5 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java @@ -44,6 +44,7 @@ import org.sleuthkit.datamodel.LocalFilesDataSource; import org.sleuthkit.datamodel.TskDataException; import org.apache.commons.lang3.StringUtils; import org.sleuthkit.datamodel.CarvingResult; +import org.sleuthkit.datamodel.TskData; /** * A manager that provides methods for retrieving files from the current case @@ -302,6 +303,7 @@ public class FileManager implements Closeable { * currently unused. * @param otherDetails Other details of the derivation method or tool, * currently unused. + * @param encodingType Type of encoding used on the file * * @return A DerivedFile object representing the derived file. * @@ -314,13 +316,14 @@ public class FileManager implements Closeable { long ctime, long crtime, long atime, long mtime, boolean isFile, AbstractFile parentFile, - String rederiveDetails, String toolName, String toolVersion, String otherDetails) throws TskCoreException { + String rederiveDetails, String toolName, String toolVersion, String otherDetails, + TskData.EncodingType encodingType) throws TskCoreException { if (null == caseDb) { throw new TskCoreException("File manager has been closed"); } return caseDb.addDerivedFile(fileName, localPath, size, ctime, crtime, atime, mtime, - isFile, parentFile, rederiveDetails, toolName, toolVersion, otherDetails); + isFile, parentFile, rederiveDetails, toolName, toolVersion, otherDetails, encodingType); } /** @@ -404,7 +407,7 @@ public class FileManager implements Closeable { VirtualDirectory rootDirectory = dataSource.getRootDirectory(); List filesAdded = new ArrayList<>(); for (java.io.File localFile : localFiles) { - AbstractFile fileAdded = addLocalFile(trans, rootDirectory, localFile, progressUpdater); + AbstractFile fileAdded = addLocalFile(trans, rootDirectory, localFile, TskData.EncodingType.NONE, progressUpdater); if (null != fileAdded) { filesAdded.add(fileAdded); } else { @@ -491,6 +494,7 @@ public class FileManager implements Closeable { * @param localFile The local/logical file or directory. * @param addProgressUpdater notifier to receive progress notifications on * folders added, or null if not used + * @param encodingType Type of encoding used when storing the file * * @returns File object of file added or new virtualdirectory for the * directory. @@ -502,7 +506,8 @@ public class FileManager implements Closeable { * @throws TskCoreException If there is a problem completing a database * operation. */ - private AbstractFile addLocalFile(CaseDbTransaction trans, VirtualDirectory parentDirectory, java.io.File localFile, FileAddProgressUpdater progressUpdater) throws TskCoreException { + private AbstractFile addLocalFile(CaseDbTransaction trans, VirtualDirectory parentDirectory, java.io.File localFile, + TskData.EncodingType encodingType, FileAddProgressUpdater progressUpdater) throws TskCoreException { if (localFile.isDirectory()) { /* * Add the directory as a virtual directory. @@ -524,7 +529,7 @@ public class FileManager implements Closeable { } else { return caseDb.addLocalFile(localFile.getName(), localFile.getAbsolutePath(), localFile.length(), 0, 0, 0, 0, - localFile.isFile(), parentDirectory, trans); + localFile.isFile(), encodingType, parentDirectory, trans); } } @@ -619,5 +624,74 @@ public class FileManager implements Closeable { } return caseDb.addCarvedFiles(filesToAdd); } + + /** + * Adds a derived file to the case. + * + * @param fileName The name of the file. + * @param localPath The local path of the file, relative to the case + * folder and including the file name. + * @param size The size of the file in bytes. + * @param ctime The change time of the file. + * @param crtime The create time of the file + * @param atime The accessed time of the file. + * @param mtime The modified time of the file. + * @param isFile True if a file, false if a directory. + * @param parentFile The parent file from which the file was derived. + * @param rederiveDetails The details needed to re-derive file (will be + * specific to the derivation method), currently + * unused. + * @param toolName The name of the derivation method or tool, + * currently unused. + * @param toolVersion The version of the derivation method or tool, + * currently unused. + * @param otherDetails Other details of the derivation method or tool, + * currently unused. + * + * @return A DerivedFile object representing the derived file. + * + * @throws TskCoreException if there is a problem adding the file to the + * case database. + * + * @Deprecated Use the version with explicit EncodingType instead + */ + @Deprecated + public synchronized DerivedFile addDerivedFile(String fileName, + String localPath, + long size, + long ctime, long crtime, long atime, long mtime, + boolean isFile, + AbstractFile parentFile, + String rederiveDetails, String toolName, String toolVersion, String otherDetails) throws TskCoreException { + return addDerivedFile(fileName, localPath, size, ctime, crtime, atime, mtime, isFile, parentFile, + rederiveDetails, toolName, toolVersion, otherDetails, TskData.EncodingType.NONE); + } + + /** + * Adds a file or directory of logical/local files data source to the case + * database, recursively adding the contents of directories. + * + * @param trans A case database transaction. + * @param parentDirectory The root virtual direcotry of the data source. + * @param localFile The local/logical file or directory. + * @param addProgressUpdater notifier to receive progress notifications on + * folders added, or null if not used + * + * @returns File object of file added or new virtualdirectory for the + * directory. + * @param progressUpdater Called after each file/directory is added to + * the case database. + * + * @return An AbstractFile representation of the local/logical file. + * + * @throws TskCoreException If there is a problem completing a database + * operation. + * + * @Deprecated Use the version with explicit EncodingType instead + */ + @Deprecated + private AbstractFile addLocalFile(CaseDbTransaction trans, VirtualDirectory parentDirectory, java.io.File localFile, FileAddProgressUpdater progressUpdater) throws TskCoreException { + return addLocalFile(trans, parentDirectory, localFile, TskData.EncodingType.NONE, progressUpdater); + } } diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java index 0cfaddf97b..784556e7a4 100755 --- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java +++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java @@ -65,6 +65,7 @@ public final class UserPreferences { private static final int DEFAULT_PROCESS_TIMEOUT_HR = 60; private static final String DEFAULT_PORT_STRING = "61616"; private static final int DEFAULT_PORT_INT = 61616; + private static final String APP_NAME = "AppName"; // Prevent instantiation. private UserPreferences() { @@ -284,6 +285,23 @@ public final class UserPreferences { preferences.putBoolean(PROCESS_TIME_OUT_ENABLED, enabled); } + /** + * Get the display name for this program + * @return Name of this program + */ + public static String getAppName(){ + return preferences.get(APP_NAME, "Autopsy"); + } + + /** + * Set the display name for this program + * + * @param name Display name + */ + public static void setAppName(String name){ + preferences.put(APP_NAME, name); + } + /** * Provides ability to convert text to hex text. diff --git a/Core/src/org/sleuthkit/autopsy/core/layer.xml b/Core/src/org/sleuthkit/autopsy/core/layer.xml index cd72a061f9..36c3e09c1e 100644 --- a/Core/src/org/sleuthkit/autopsy/core/layer.xml +++ b/Core/src/org/sleuthkit/autopsy/core/layer.xml @@ -212,13 +212,13 @@ - + - + @@ -364,34 +364,40 @@ - - - - - - - - + + + + + + - + diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java index 24255057d0..9fc6b4927f 100644 --- a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java @@ -44,6 +44,7 @@ import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskDataException; /** @@ -101,7 +102,8 @@ public final class ExternalResultsImporter { DerivedFile derivedFile = fileManager.addDerivedFile(localFile.getName(), relativePath, localFile.length(), 0, 0, 0, 0, // Do not currently have file times for derived files from external processes. true, parentFile, - "", "", "", ""); // Not currently providing derivation info for derived files from external processes. + "", "", "", "", // Not currently providing derivation info for derived files from external processes. + TskData.EncodingType.NONE); // Don't allow external encoded files IngestServices.getInstance().fireModuleContentEvent(new ModuleContentEvent(derivedFile)); } else { String errorMessage = NbBundle.getMessage(this.getClass(), diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java index 4ddd67ac6f..d2bc1a9820 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java @@ -486,6 +486,8 @@ public class IngestManager { /** * Starts an ingest job that will process a collection of data sources. + * This is intended to be used in an auto-ingest context and will fail + * if no ingest modules are enabled. * * @param dataSources The data sources to process. * @param settings The settings for the ingest job. @@ -499,8 +501,9 @@ public class IngestManager { if (job.hasIngestPipeline()) { return this.startIngestJob(job); // Start job } + return new IngestJobStartResult(null, new IngestManagerException("No ingest pipeline created, likely due to no ingest modules being enabled."), null); } - return new IngestJobStartResult(null, new IngestManagerException("Job creation is not enabled."), null); + return new IngestJobStartResult(null, new IngestManagerException("No case open"), null); } /** diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ImageExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ImageExtractor.java index dd7ae145fe..efc5ec6504 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ImageExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/ImageExtractor.java @@ -46,8 +46,10 @@ import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.ingest.ModuleContentEvent; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.EncodedFileOutputStream; import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; class ImageExtractor { @@ -185,7 +187,7 @@ class ImageExtractor { try { listOfExtractedImageAbstractFiles.add(fileManager.addDerivedFile(extractedImage.getFileName(), extractedImage.getLocalPath(), extractedImage.getSize(), extractedImage.getCtime(), extractedImage.getCrtime(), extractedImage.getAtime(), extractedImage.getAtime(), - true, abstractFile, null, EmbeddedFileExtractorModuleFactory.getModuleName(), null, null)); + true, abstractFile, null, EmbeddedFileExtractorModuleFactory.getModuleName(), null, null, TskData.EncodingType.XOR1)); } catch (TskCoreException ex) { logger.log(Level.WARNING, NbBundle.getMessage(this.getClass(), "EmbeddedFileExtractorIngestModule.ImageExtractor.extractImage.addToDB.exception.msg"), ex); //NON-NLS } @@ -591,7 +593,7 @@ class ImageExtractor { * specified location. */ private void writeExtractedImage(String outputPath, byte[] data) { - try (FileOutputStream fos = new FileOutputStream(outputPath)) { + try (EncodedFileOutputStream fos = new EncodedFileOutputStream(new FileOutputStream(outputPath), TskData.EncodingType.XOR1)) { fos.write(data); } catch (IOException ex) { logger.log(Level.WARNING, "Could not write to the provided location: " + outputPath, ex); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index b4d8109988..e6923d9674 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -18,9 +18,7 @@ */ package org.sleuthkit.autopsy.modules.embeddedfileextractor; -import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -60,6 +58,7 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.DerivedFile; +import org.sleuthkit.datamodel.EncodedFileOutputStream; import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -149,7 +148,6 @@ class SevenZipExtractor { return true; } } - return false; } catch (TskCoreException ex) { logger.log(Level.WARNING, "Error executing FileTypeDetector.getFileType()", ex); // NON-NLS @@ -624,8 +622,8 @@ class SevenZipExtractor { UnpackStream(String localAbsPath) { this.localAbsPath = localAbsPath; try { - output = new BufferedOutputStream(new FileOutputStream(localAbsPath)); - } catch (FileNotFoundException ex) { + output = new EncodedFileOutputStream(new FileOutputStream(localAbsPath), TskData.EncodingType.XOR1); + } catch (IOException ex) { logger.log(Level.SEVERE, "Error writing extracted file: " + localAbsPath, ex); //NON-NLS } @@ -869,7 +867,8 @@ class SevenZipExtractor { try { DerivedFile df = fileManager.addDerivedFile(fileName, node.getLocalRelPath(), node.getSize(), node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(), - node.isIsFile(), node.getParent().getFile(), "", EmbeddedFileExtractorModuleFactory.getModuleName(), "", ""); + node.isIsFile(), node.getParent().getFile(), "", EmbeddedFileExtractorModuleFactory.getModuleName(), + "", "", TskData.EncodingType.XOR1); node.setFile(df); } catch (TskCoreException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.form b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.form index d4dc408210..f8e456c728 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.form @@ -19,28 +19,39 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -65,6 +76,13 @@ + + + + + + + @@ -159,5 +177,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.java index cedd760146..15c6dfcaeb 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/AddFileTypePanel.java @@ -85,7 +85,9 @@ class AddFileTypePanel extends javax.swing.JPanel { * type is given. */ @Messages({"AddMimeTypePanel.emptySigList.message=Must have at least one signature.", - "AddMimeTypePanel.emptySigList.title=Invalid Signature List"}) + "AddMimeTypePanel.emptySigList.title=Invalid Signature List", + "AddMimeTypePanel.emptySetName.message=Interesting files set name is required if alert is requested.", + "AddMimeTypePanel.emptySetName.title=Missing Interesting Files Set Name"}) FileType getFileType() { String typeName = mimeTypeTextField.getText(); if (typeName.isEmpty()) { @@ -108,8 +110,20 @@ class AddFileTypePanel extends javax.swing.JPanel { for (int i = 0; i < this.signaturesListModel.getSize(); i++) { sigList.add(this.signaturesListModel.elementAt(i)); } - return new FileType(typeName, sigList); + String setName = ""; + if (this.postHitCheckBox.isSelected()) { + if (this.setNameTextField.getText().isEmpty()) { + JOptionPane.showMessageDialog(null, + Bundle.AddMimeTypePanel_emptySetName_message(), + Bundle.AddMimeTypePanel_emptySetName_title(), + JOptionPane.ERROR_MESSAGE); + + return null; + } + setName = this.setNameTextField.getText(); + } + return new FileType(typeName, sigList, this.postHitCheckBox.isSelected(), setName); } /** @@ -163,6 +177,9 @@ class AddFileTypePanel extends javax.swing.JPanel { mimeTypeTextField = new javax.swing.JTextField(); addSigButton = new javax.swing.JButton(); jLabel1 = new javax.swing.JLabel(); + postHitCheckBox = new javax.swing.JCheckBox(); + setNameLabel = new javax.swing.JLabel(); + setNameTextField = new javax.swing.JTextField(); editSigButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/edit16.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(editSigButton, org.openide.util.NbBundle.getMessage(AddFileTypePanel.class, "AddFileTypePanel.editSigButton.text")); // NOI18N @@ -203,6 +220,19 @@ class AddFileTypePanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(AddFileTypePanel.class, "AddFileTypePanel.jLabel1.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(postHitCheckBox, org.openide.util.NbBundle.getMessage(AddFileTypePanel.class, "AddFileTypePanel.postHitCheckBox.text")); // NOI18N + postHitCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + postHitCheckBoxActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(setNameLabel, org.openide.util.NbBundle.getMessage(AddFileTypePanel.class, "AddFileTypePanel.setNameLabel.text")); // NOI18N + setNameLabel.setEnabled(postHitCheckBox.isSelected()); + + setNameTextField.setText(org.openide.util.NbBundle.getMessage(AddFileTypePanel.class, "AddFileTypePanel.setNameTextField.text")); // NOI18N + setNameTextField.setEnabled(postHitCheckBox.isSelected()); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -211,22 +241,30 @@ class AddFileTypePanel extends javax.swing.JPanel { .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(jLabel1) - .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(mimeTypeLabel) + .addGap(18, 18, 18) + .addComponent(mimeTypeTextField)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGap(0, 0, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(addSigButton) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(addSigButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(editSigButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(deleteSigButton)) + .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 393, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addGap(28, 28, 28) + .addComponent(setNameLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(editSigButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(deleteSigButton)) - .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 393, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(setNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 144, javax.swing.GroupLayout.PREFERRED_SIZE)))) .addGroup(layout.createSequentialGroup() - .addComponent(mimeTypeLabel) - .addGap(18, 18, 18) - .addComponent(mimeTypeTextField))) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel1) + .addComponent(postHitCheckBox)) + .addGap(0, 0, Short.MAX_VALUE))) .addContainerGap()) ); layout.setVerticalGroup( @@ -245,7 +283,13 @@ class AddFileTypePanel extends javax.swing.JPanel { .addComponent(addSigButton) .addComponent(editSigButton) .addComponent(deleteSigButton)) - .addContainerGap()) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(postHitCheckBox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(setNameLabel) + .addComponent(setNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); }// //GEN-END:initComponents @@ -280,6 +324,13 @@ class AddFileTypePanel extends javax.swing.JPanel { } }//GEN-LAST:event_addSigButtonActionPerformed + private void postHitCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_postHitCheckBoxActionPerformed + if (evt.getSource().equals(postHitCheckBox)) { + this.setNameLabel.setEnabled(postHitCheckBox.isSelected()); + this.setNameTextField.setEnabled(postHitCheckBox.isSelected()); + } + }//GEN-LAST:event_postHitCheckBoxActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton addSigButton; @@ -289,6 +340,9 @@ class AddFileTypePanel extends javax.swing.JPanel { private javax.swing.JScrollPane jScrollPane1; private javax.swing.JLabel mimeTypeLabel; private javax.swing.JTextField mimeTypeTextField; + private javax.swing.JCheckBox postHitCheckBox; + private javax.swing.JLabel setNameLabel; + private javax.swing.JTextField setNameTextField; private javax.swing.JList signatureList; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties index 2e4e6187f6..4dfed1d15b 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/Bundle.properties @@ -24,7 +24,7 @@ FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.message=Offset must be a FileTypeIdGlobalSettingsPanel.JOptionPane.invalidOffset.title=Invalid Offset FileTypeIdGlobalSettingsPanel.JOptionPane.invalidRawSignatureBytes.message=The signature has one or more invalid hexadecimal digits. FileTypeIdGlobalSettingsPanel.JOptionPane.invalidSignatureBytes.title=Invalid Signature -FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.message=Interesting files set name is required if alert is requests. +FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.message= FileTypeIdGlobalSettingsPanel.JOptionPane.invalidInterestingFilesSetName.title=Missing Interesting Files Set Name FileTypeIdGlobalSettingsPanel.JOptionPane.storeFailed.title=Save Failed FileTypeIdGlobalSettingsPanel.JOptionPane.loadFailed.title=Load Failed @@ -50,3 +50,6 @@ AddFileTypePanel.deleteSigButton.text=Delete Signature AddFileTypePanel.jLabel1.text=Signatures AddFileTypePanel.editSigButton.text=Edit Signature AddFileTypePanel.addSigButton.text=Add Signature +AddFileTypePanel.postHitCheckBox.text=Alert as an "Interesting File" when found +AddFileTypePanel.setNameLabel.text=Set Name +AddFileTypePanel.setNameTextField.text= diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java index c8765461a4..e0645bff40 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileType.java @@ -43,6 +43,8 @@ class FileType implements Serializable { private static final long serialVersionUID = 1L; private final String mimeType; private final List signatures; + private final boolean createInterestingFileHit; + private final String interestingFilesSetName; /** * Creates a representation of a file type characterized by file signatures. @@ -53,11 +55,27 @@ class FileType implements Serializable { * @throws IllegalArgumentException If an empty list of signatures is given. */ FileType(String mimeType, List signatures) throws IllegalArgumentException { + this(mimeType, signatures, false, ""); + } + + /** + * Creates a representation of a file type characterized by file signatures. + * + * @param mimeType The mime type to associate with this file type. + * @param signatures The signatures that characterize this file type. + * @param createInterestingFileHit Create interesting file hit for file type? + * @param setName Name of the interesting file set in which to create hit. + * + * @throws IllegalArgumentException If an empty list of signatures is given. + */ + FileType(String mimeType, List signatures, boolean createInterestingFileHit, String setName) throws IllegalArgumentException { if (signatures.isEmpty()) { throw new IllegalArgumentException("Must have at least one signature."); } this.mimeType = mimeType; this.signatures = new ArrayList<>(signatures); + this.createInterestingFileHit = createInterestingFileHit; + this.interestingFilesSetName = setName; } /** @@ -69,6 +87,26 @@ class FileType implements Serializable { return mimeType; } + /** + * Gets the name of the interesting files set associated with this file + * type. + * + * @return The interesting files set name. + */ + String getInterestingFilesSetName() { + return interestingFilesSetName; + } + + /** + * Should an interesting files hit be created for this file type? + * + * @return true if an interesting files hit should be created, otherwise + * false + */ + boolean createInterestingFileHit() { + return createInterestingFileHit; + } + /** * Gets the signatures associated with this file type. * diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java index 487335d828..f0b8e068c1 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java @@ -21,10 +21,15 @@ package org.sleuthkit.autopsy.modules.filetypeid; import java.util.ArrayList; import java.util.List; import java.util.SortedSet; +import java.util.logging.Level; import org.apache.tika.Tika; import org.apache.tika.mime.MediaType; import org.apache.tika.mime.MimeTypes; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.services.Blackboard; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; @@ -38,6 +43,7 @@ import org.sleuthkit.datamodel.TskData; */ public class FileTypeDetector { + private static final Logger logger = Logger.getLogger(FileTypeDetector.class.getName()); private static final Tika tika = new Tika(); private static final int BUFFER_SIZE = 64 * 1024; private final byte buffer[] = new byte[BUFFER_SIZE]; @@ -304,6 +310,32 @@ public class FileTypeDetector { private String detectUserDefinedType(AbstractFile file) throws TskCoreException { for (FileType fileType : userDefinedFileTypes) { if (fileType.matches(file)) { + if (fileType.createInterestingFileHit()) { + BlackboardArtifact artifact; + artifact = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); + BlackboardAttribute setNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, FileTypeIdModuleFactory.getModuleName(), fileType.getInterestingFilesSetName()); + artifact.addAttribute(setNameAttribute); + + /* + * Use the MIME type as the category attribute, i.e., the + * rule that determined this file belongs to the interesting + * files set. + */ + BlackboardAttribute ruleNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, FileTypeIdModuleFactory.getModuleName(), fileType.getMimeType()); + artifact.addAttribute(ruleNameAttribute); + + /* + * Index the artifact for keyword search. + */ + try { + Case.getCurrentCase().getServices().getBlackboard().indexArtifact(artifact); + } catch (Blackboard.BlackboardException ex) { + logger.log(Level.SEVERE, String.format("Unable to index blackboard artifact %d", artifact.getArtifactID()), ex); //NON-NLS + MessageNotifyUtil.Notify.error( + NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), artifact.getDisplayName()); + } + } + return fileType.getMimeType(); } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModule.java b/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModule.java index 3cc97c3e10..697d58c2e7 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModule.java @@ -171,7 +171,7 @@ public class STIXReportModule implements GeneralReportModule { } try { processFile(file.getAbsolutePath(), progressPanel, output); - } catch (TskCoreException ex) { + } catch (TskCoreException | JAXBException ex) { logger.log(Level.SEVERE, String.format("Unable to process STIX file %s", file), ex); //NON-NLS MessageNotifyUtil.Notify.show("STIXReportModule", //NON-NLS ex.getLocalizedMessage(), @@ -213,10 +213,11 @@ public class STIXReportModule implements GeneralReportModule { * @param stixFile - Name of the file * @param progressPanel - Progress panel (for updating) * + * @throws JAXBException * @throws TskCoreException */ private void processFile(String stixFile, ReportProgressPanel progressPanel, BufferedWriter output) throws - TskCoreException { + JAXBException, TskCoreException { // Load the STIX file STIXPackage stix; @@ -244,23 +245,18 @@ public class STIXReportModule implements GeneralReportModule { * * @return Unmarshalled file contents * - * @throws TskCoreException + * @throws JAXBException */ - private STIXPackage loadSTIXFile(String stixFileName) throws TskCoreException { - try { - // Create STIXPackage object from xml. - File file = new File(stixFileName); - JAXBContext jaxbContext = JAXBContext.newInstance("org.mitre.stix.stix_1:org.mitre.stix.common_1:org.mitre.stix.indicator_2:" //NON-NLS - + "org.mitre.cybox.objects:org.mitre.cybox.cybox_2:org.mitre.cybox.common_2"); //NON-NLS - Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); - STIXPackage stix = (STIXPackage) jaxbUnmarshaller.unmarshal(file); - return stix; - } catch (JAXBException ex) { - logger.log(Level.SEVERE, String.format("Unable to load STIX file %s", stixFileName), ex.getLocalizedMessage()); //NON-NLS - throw new TskCoreException("Error loading STIX file (" + ex.toString() + ")"); //NON-NLS - } + private STIXPackage loadSTIXFile(String stixFileName) throws JAXBException { + // Create STIXPackage object from xml. + File file = new File(stixFileName); + JAXBContext jaxbContext = JAXBContext.newInstance("org.mitre.stix.stix_1:org.mitre.stix.common_1:org.mitre.stix.indicator_2:" //NON-NLS + + "org.mitre.cybox.objects:org.mitre.cybox.cybox_2:org.mitre.cybox.common_2"); //NON-NLS + Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); + STIXPackage stix = (STIXPackage) jaxbUnmarshaller.unmarshal(file); + return stix; } - + /** * Do the initial processing of the list of observables. For each * observable, save it in a map using the ID as key. diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index 6bd503cd4c..ea32f57537 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -1,4 +1,4 @@ - /* +/* * * Autopsy Forensic Browser * @@ -722,8 +722,7 @@ class ReportHTML implements TableReportModule { */ public String saveContent(AbstractFile file, String dirName) { // clean up the dir name passed in - String dirName2 = dirName.replace("/", "_"); - dirName2 = dirName2.replace("\\", "_"); + String dirName2 = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(dirName); // Make a folder for the local file with the same tagName as the tag. StringBuilder localFilePath = new StringBuilder(); // full path @@ -843,13 +842,13 @@ class ReportHTML implements TableReportModule { StringBuilder index = new StringBuilder(); final String reportTitle = reportBranding.getReportTitle(); String iconPath = reportBranding.getAgencyLogoPath(); - if (iconPath == null){ + if (iconPath == null) { // use default Autopsy icon if custom icon is not set iconPath = "favicon.ico"; } index.append("\n").append(reportTitle).append(" ").append( NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.title", currentCase.getName())).append( - "\n"); //NON-NLS + "\n"); //NON-NLS index.append("\n"); //NON-NLS index.append("\n"); //NON-NLS @@ -1048,10 +1047,10 @@ class ReportHTML implements TableReportModule { .append("").append(caseName).append("\n"); //NON-NLS NON-NLS summary.append("").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.caseNum")) //NON-NLS .append("").append(!caseNumber.isEmpty() ? caseNumber : NbBundle //NON-NLS - .getMessage(this.getClass(), "ReportHTML.writeSum.noCaseNum")).append("\n"); //NON-NLS + .getMessage(this.getClass(), "ReportHTML.writeSum.noCaseNum")).append("\n"); //NON-NLS summary.append("").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.examiner")).append("") //NON-NLS .append(!examiner.isEmpty() ? examiner : NbBundle - .getMessage(this.getClass(), "ReportHTML.writeSum.noExaminer")) + .getMessage(this.getClass(), "ReportHTML.writeSum.noExaminer")) .append("\n"); //NON-NLS summary.append("").append(NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.numImages")) //NON-NLS .append("").append(imagecount).append("\n"); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportWizardAction.java b/Core/src/org/sleuthkit/autopsy/report/ReportWizardAction.java index e0a5eb6788..574459fe11 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportWizardAction.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportWizardAction.java @@ -47,7 +47,8 @@ import org.sleuthkit.datamodel.BlackboardArtifact; @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.report.ReportWizardAction") @ActionRegistration(displayName = "#CTL_ReportWizardAction", lazy = false) @ActionReferences(value = { - @ActionReference(path = "Menu/Tools", position = 80)}) + @ActionReference(path = "Menu/Tools", position = 103), + @ActionReference(path = "Toolbars/Case", position = 103)}) public final class ReportWizardAction extends CallableSystemAction implements Presenter.Toolbar, ActionListener { private final JButton toolbarButton = new JButton(); diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java new file mode 100755 index 0000000000..3348fdcafd --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java @@ -0,0 +1,132 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.report.taggedhashes; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.services.TagsManager; +import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; +import org.sleuthkit.autopsy.report.GeneralReportModule; +import org.sleuthkit.autopsy.report.ReportProgressPanel; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Instances of this class plug in to the reporting infrastructure to provide a + * convenient way to add content hashes to hash set databases. + */ +@ServiceProvider(service = GeneralReportModule.class) +public class AddTaggedHashesToHashDb implements GeneralReportModule { + + private AddTaggedHashesToHashDbConfigPanel configPanel; + + public AddTaggedHashesToHashDb() { + } + + @Override + public String getName() { + return "Add Tagged Hashes"; + } + + @Override + public String getDescription() { + return "Adds hashes of tagged files to a hash database."; + } + + @Override + public String getRelativeFilePath() { + return ""; + } + + @Override + public void generateReport(String reportPath, ReportProgressPanel progressPanel) { + progressPanel.setIndeterminate(true); + progressPanel.start(); + progressPanel.updateStatusLabel("Adding hashes..."); + + HashDb hashSet = configPanel.getSelectedHashDatabase(); + if (hashSet != null) { + progressPanel.updateStatusLabel("Adding hashes to " + hashSet.getHashSetName() + " hash set..."); + + TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + List tagNames = configPanel.getSelectedTagNames(); + ArrayList failedExports = new ArrayList<>(); + for (TagName tagName : tagNames) { + if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) { + break; + } + + progressPanel.updateStatusLabel("Adding " + tagName.getDisplayName() + " hashes to " + hashSet.getHashSetName() + " hash set..."); + try { + List tags = tagsManager.getContentTagsByTagName(tagName); + for (ContentTag tag : tags) { + // TODO: Currently only AbstractFiles have md5 hashes. Here only files matter. + Content content = tag.getContent(); + if (content instanceof AbstractFile) { + if (null != ((AbstractFile) content).getMd5Hash()) { + try { + hashSet.addHashes(tag.getContent(), Case.getCurrentCase().getName()); + } catch (TskCoreException ex) { + Logger.getLogger(AddTaggedHashesToHashDb.class.getName()).log(Level.SEVERE, "Error adding hash for obj_id = " + tag.getContent().getId() + " to hash database " + hashSet.getHashSetName(), ex); + failedExports.add(tag.getContent().getName()); + } + } else { + JOptionPane.showMessageDialog(null, "Unable to add the " + (tags.size() > 1 ? "files" : "file") + " to the hash database. Hashes have not been calculated. Please configure and run an appropriate ingest module.", "Add to Hash Database Error", JOptionPane.ERROR_MESSAGE); + break; + } + } + } + } catch (TskCoreException ex) { + Logger.getLogger(AddTaggedHashesToHashDb.class.getName()).log(Level.SEVERE, "Error adding to hash database", ex); + JOptionPane.showMessageDialog(null, "Error getting selected tags for case.", "Hash Export Error", JOptionPane.ERROR_MESSAGE); + } + } + if (!failedExports.isEmpty()) { + StringBuilder errorMessage = new StringBuilder("Failed to export hashes for the following files: "); + for (int i = 0; i < failedExports.size(); ++i) { + errorMessage.append(failedExports.get(i)); + if (failedExports.size() > 1 && i < failedExports.size() - 1) { + errorMessage.append(","); + } + if (i == failedExports.size() - 1) { + errorMessage.append("."); + } + } + JOptionPane.showMessageDialog(null, errorMessage.toString(), "Hash Export Error", JOptionPane.ERROR_MESSAGE); + } + } + progressPanel.setIndeterminate(false); + progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE); + } + + @Override + public JPanel getConfigurationPanel() { + configPanel = new AddTaggedHashesToHashDbConfigPanel(); + return configPanel; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.form b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.form new file mode 100755 index 0000000000..377dad7826 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.form @@ -0,0 +1,148 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java new file mode 100755 index 0000000000..93dad76f89 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java @@ -0,0 +1,326 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.report.taggedhashes; + +import java.awt.Component; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.ListCellRenderer; +import javax.swing.ListModel; +import javax.swing.event.ListDataListener; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; +import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager; +import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupSettingsPanel; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Instances of this class are used to configure the report module plug in that + * provides a convenient way to add content hashes to hash set databases. + */ +class AddTaggedHashesToHashDbConfigPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + private final static String NO_DATABASES_TEXT = "No updateable hash sets"; + private List tagNames; + private final Map tagNameSelections = new LinkedHashMap<>(); + private final TagNamesListModel tagsNamesListModel = new TagNamesListModel(); + private final TagsNamesListCellRenderer tagsNamesRenderer = new TagsNamesListCellRenderer(); + private final Map hashSets = new HashMap<>(); + private HashDb selectedHashSet = null; + + AddTaggedHashesToHashDbConfigPanel() { + initComponents(); + customizeComponents(); + } + + private void customizeComponents() { + populateTagNameComponents(); + populateHashSetComponents(); + } + + private void populateTagNameComponents() { + // Get the tag names in use for the current case. + try { + tagNames = Case.getCurrentCase().getServices().getTagsManager().getTagNamesInUse(); + } catch (TskCoreException ex) { + Logger.getLogger(AddTaggedHashesToHashDbConfigPanel.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); + JOptionPane.showMessageDialog(null, "Error getting tag names for case.", "Tag Names Not Found", JOptionPane.ERROR_MESSAGE); + } + + // Mark the tag names as unselected. Note that tagNameSelections is a + // LinkedHashMap so that order is preserved and the tagNames and tagNameSelections + // containers are "parallel" containers. + for (TagName tagName : tagNames) { + tagNameSelections.put(tagName.getDisplayName(), Boolean.FALSE); + } + + // Set up the tag names JList component to be a collection of check boxes + // for selecting tag names. The mouse click listener updates tagNameSelections + // to reflect user choices. + tagNamesListBox.setModel(tagsNamesListModel); + tagNamesListBox.setCellRenderer(tagsNamesRenderer); + tagNamesListBox.setVisibleRowCount(-1); + tagNamesListBox.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent evt) { + JList list = (JList) evt.getSource(); + int index = list.locationToIndex(evt.getPoint()); + String value = tagsNamesListModel.getElementAt(index); + tagNameSelections.put(value, !tagNameSelections.get(value)); + list.repaint(); + } + }); + } + + private void populateHashSetComponents() { + // Clear the components because this method is called both during construction + // and when the user changes the hash set configuration. + hashSets.clear(); + hashSetsComboBox.removeAllItems(); + + // Get the updateable hash databases and add their hash set names to the + // JComboBox component. + List updateableHashSets = HashDbManager.getInstance().getUpdateableHashSets(); + if (!updateableHashSets.isEmpty()) { + for (HashDb hashDb : updateableHashSets) { + hashSets.put(hashDb.getHashSetName(), hashDb); + hashSetsComboBox.addItem(hashDb.getHashSetName()); + } + hashSetsComboBox.setEnabled(true); + } else { + hashSetsComboBox.addItem(NO_DATABASES_TEXT); + hashSetsComboBox.setEnabled(false); + } + } + + /** + * Gets the subset of the tag names in use selected by the user. + * + * @return A list, possibly empty, of TagName data transfer objects (DTOs). + */ + List getSelectedTagNames() { + List selectedTagNames = new ArrayList<>(); + for (TagName tagName : tagNames) { + if (tagNameSelections.get(tagName.getDisplayName())) { + selectedTagNames.add(tagName); + } + } + return selectedTagNames; + } + + /** + * Gets the hash set database selected by the user. + * + * @return A HashDb object representing the database or null. + */ + HashDb getSelectedHashDatabase() { + return selectedHashSet; + } + + // This class is a list model for the tag names JList component. + private class TagNamesListModel implements ListModel { + + @Override + public int getSize() { + return tagNames.size(); + } + + @Override + public String getElementAt(int index) { + return tagNames.get(index).getDisplayName(); + } + + @Override + public void addListDataListener(ListDataListener l) { + } + + @Override + public void removeListDataListener(ListDataListener l) { + } + } + + // This class renders the items in the tag names JList component as JCheckbox components. + private class TagsNamesListCellRenderer extends JCheckBox implements ListCellRenderer { + private static final long serialVersionUID = 1L; + + @Override + public Component getListCellRendererComponent(JList list, String value, int index, boolean isSelected, boolean cellHasFocus) { + if (value != null) { + setEnabled(list.isEnabled()); + setSelected(tagNameSelections.get(value)); + setFont(list.getFont()); + setBackground(list.getBackground()); + setForeground(list.getForeground()); + setText(value); + return this; + } + return new JLabel(); + } + } + + /** + * 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") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jScrollPane1 = new javax.swing.JScrollPane(); + tagNamesListBox = new javax.swing.JList<>(); + selectAllButton = new javax.swing.JButton(); + deselectAllButton = new javax.swing.JButton(); + jLabel1 = new javax.swing.JLabel(); + hashSetsComboBox = new javax.swing.JComboBox<>(); + configureHashDatabasesButton = new javax.swing.JButton(); + jLabel2 = new javax.swing.JLabel(); + + jScrollPane1.setViewportView(tagNamesListBox); + + org.openide.awt.Mnemonics.setLocalizedText(selectAllButton, org.openide.util.NbBundle.getMessage(AddTaggedHashesToHashDbConfigPanel.class, "AddTaggedHashesToHashDbConfigPanel.selectAllButton.text")); // NOI18N + selectAllButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + selectAllButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(deselectAllButton, org.openide.util.NbBundle.getMessage(AddTaggedHashesToHashDbConfigPanel.class, "AddTaggedHashesToHashDbConfigPanel.deselectAllButton.text")); // NOI18N + deselectAllButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deselectAllButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(AddTaggedHashesToHashDbConfigPanel.class, "AddTaggedHashesToHashDbConfigPanel.jLabel1.text")); // NOI18N + + hashSetsComboBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + hashSetsComboBoxActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(configureHashDatabasesButton, org.openide.util.NbBundle.getMessage(AddTaggedHashesToHashDbConfigPanel.class, "AddTaggedHashesToHashDbConfigPanel.configureHashDatabasesButton.text")); // NOI18N + configureHashDatabasesButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + configureHashDatabasesButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(AddTaggedHashesToHashDbConfigPanel.class, "AddTaggedHashesToHashDbConfigPanel.jLabel2.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel2) + .addComponent(jLabel1) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane1) + .addGroup(layout.createSequentialGroup() + .addComponent(hashSetsComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 159, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(configureHashDatabasesButton))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(deselectAllButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(selectAllButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(selectAllButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(deselectAllButton)) + .addComponent(jScrollPane1)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jLabel2) + .addGap(4, 4, 4) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(hashSetsComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(configureHashDatabasesButton)) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + private void selectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectAllButtonActionPerformed + for (TagName tagName : tagNames) { + tagNameSelections.put(tagName.getDisplayName(), Boolean.TRUE); + } + tagNamesListBox.repaint(); + }//GEN-LAST:event_selectAllButtonActionPerformed + + private void hashSetsComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hashSetsComboBoxActionPerformed + String key = (String)hashSetsComboBox.getSelectedItem(); + selectedHashSet = hashSets.get(key); + }//GEN-LAST:event_hashSetsComboBoxActionPerformed + + private void deselectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deselectAllButtonActionPerformed + for (TagName tagName : tagNames) { + tagNameSelections.put(tagName.getDisplayName(), Boolean.FALSE); + } + tagNamesListBox.repaint(); + }//GEN-LAST:event_deselectAllButtonActionPerformed + + private void configureHashDatabasesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_configureHashDatabasesButtonActionPerformed + HashLookupSettingsPanel configPanel = new HashLookupSettingsPanel(); + configPanel.load(); + if (JOptionPane.showConfirmDialog(null, configPanel, "Hash Set Configuration", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE) == JOptionPane.OK_OPTION) { + configPanel.store(); + populateHashSetComponents(); + } else { + configPanel.cancel(); + populateHashSetComponents(); + } + }//GEN-LAST:event_configureHashDatabasesButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton configureHashDatabasesButton; + private javax.swing.JButton deselectAllButton; + private javax.swing.JComboBox hashSetsComboBox; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JButton selectAllButton; + private javax.swing.JList tagNamesListBox; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/Bundle.properties b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/Bundle.properties new file mode 100755 index 0000000000..82da454a0c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/Bundle.properties @@ -0,0 +1,7 @@ +HashDbConfigDialog.okButton.text=OK +HashDbConfigDialog.cancelButton.text=Cancel +AddTaggedHashesToHashDbConfigPanel.selectAllButton.text=Select All +AddTaggedHashesToHashDbConfigPanel.jLabel2.text=Export to hash set: +AddTaggedHashesToHashDbConfigPanel.configureHashDatabasesButton.text=Configure Hash Sets... +AddTaggedHashesToHashDbConfigPanel.jLabel1.text=Export hashes of files tagged as: +AddTaggedHashesToHashDbConfigPanel.deselectAllButton.text=Deselect All diff --git a/Core/src/org/sleuthkit/autopsy/report/testfixtures/CustomArtifactsCreatorIngestModule.java b/Core/src/org/sleuthkit/autopsy/report/testfixtures/CustomArtifactsCreatorIngestModule.java new file mode 100644 index 0000000000..b3dafaa1d6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/testfixtures/CustomArtifactsCreatorIngestModule.java @@ -0,0 +1,115 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.report.testfixtures; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import javax.xml.bind.DatatypeConverter; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.services.Blackboard; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.FileIngestModuleAdapter; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskCoreException; +import org.openide.util.NbBundle; + +/** + * A file ingest module that associates custom artifacts and attributes with + * files for test purposes. + */ +@NbBundle.Messages({ + "ErrorCreatingCustomBlackBoardType=Error creating custom blackboard type." +}) +final class CustomArtifactsCreatorIngestModule extends FileIngestModuleAdapter { + + private static final Logger logger = Logger.getLogger(CustomArtifactsCreatorIngestModule.class.getName()); + private static final String moduleName = CustomArtifactsCreatorIngestModuleFactory.getModuleName(); + private static final String ARTIFACT_TYPE_NAME = "AUT_ARTIFACT"; + private static final String ARTIFACT_DISPLAY_NAME = "Autopsy Artifact"; + private static final String INT_ATTR_TYPE_NAME = "AUT_INT_ATTRIBUTE"; + private static final String INT_ATTR_DISPLAY_NAME = "Autopsy Integer"; + private static final String DOUBLE_ATTR_TYPE_NAME = "AUT_DOUBLE_ATTRIBUTE"; + private static final String DOUBLE_ATTR_DISPLAY_NAME = "Autopsy Double"; + private static final String LONG_ATTR_TYPE_NAME = "AUT_LONG_ATTRIBUTE"; + private static final String LONG_ATTR_DISPLAY_NAME = "Autopsy Long"; + private static final String DATETIME_ATTR_TYPE_NAME = "AUT_DATETIME_ATTRIBUTE"; + private static final String DATETIME_ATTR_DISPLAY_NAME = "Autopsy Datetime"; + private static final String BYTES_ATTR_TYPE_NAME = "AUT_BYTES_ATTRIBUTE"; + private static final String BYTES_ATTR_DISPLAY_NAME = "Autopsy Bytes"; + private static final String STRING_ATTR_TYPE_NAME = "AUT_STRING_ATTRIBUTE"; + private static final String STRING_ATTR_DISPLAY_NAME = "Autopsy String"; + private BlackboardArtifact.Type artifactType; + private BlackboardAttribute.Type intAttrType; + private BlackboardAttribute.Type doubleAttrType; + private BlackboardAttribute.Type longAttributeType; + private BlackboardAttribute.Type dateTimeAttrType; + private BlackboardAttribute.Type bytesAttrType; + private BlackboardAttribute.Type stringAttrType; + + @Override + public void startUp(IngestJobContext context) throws IngestModuleException { + Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); + try { + artifactType = blackboard.getOrAddArtifactType(ARTIFACT_TYPE_NAME, ARTIFACT_DISPLAY_NAME); + intAttrType = blackboard.getOrAddAttributeType(INT_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.INTEGER, INT_ATTR_DISPLAY_NAME); + doubleAttrType = blackboard.getOrAddAttributeType(DOUBLE_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE, DOUBLE_ATTR_DISPLAY_NAME); + longAttributeType = blackboard.getOrAddAttributeType(LONG_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG, LONG_ATTR_DISPLAY_NAME); + dateTimeAttrType = blackboard.getOrAddAttributeType(DATETIME_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME, DATETIME_ATTR_DISPLAY_NAME); + bytesAttrType = blackboard.getOrAddAttributeType(BYTES_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE, BYTES_ATTR_DISPLAY_NAME); + stringAttrType = blackboard.getOrAddAttributeType(STRING_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, STRING_ATTR_DISPLAY_NAME); + } catch (Blackboard.BlackboardException ex) { + throw new IngestModuleException(Bundle.ErrorCreatingCustomBlackBoardType(), ex); + } + } + + @Override + public ProcessResult process(AbstractFile file) { + /* + * Skip directories and virtual files. + */ + if (file.isDir() || file.isVirtual()) { + return ProcessResult.OK; + } + + /* + * Add a custom artifact with one custom attribute of each value type. + */ + try { + BlackboardArtifact artifact = file.newArtifact(artifactType.getTypeID()); + List attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(intAttrType, moduleName, 0)); + attributes.add(new BlackboardAttribute(doubleAttrType, moduleName, 0.0)); + attributes.add(new BlackboardAttribute(longAttributeType, moduleName, 0L)); + attributes.add(new BlackboardAttribute(dateTimeAttrType, moduleName, 60L)); + attributes.add(new BlackboardAttribute(bytesAttrType, moduleName, DatatypeConverter.parseHexBinary("ABCD"))); + attributes.add(new BlackboardAttribute(stringAttrType, moduleName, "Zero")); + artifact.addAttributes(attributes); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Failed to process file (obj_id = %d)", file.getId()), ex); + return ProcessResult.ERROR; + } + + return ProcessResult.OK; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/report/testfixtures/CustomArtifactsCreatorIngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/report/testfixtures/CustomArtifactsCreatorIngestModuleFactory.java new file mode 100644 index 0000000000..766a3260dd --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/testfixtures/CustomArtifactsCreatorIngestModuleFactory.java @@ -0,0 +1,64 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.report.testfixtures; + +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.coreutils.Version; +import org.sleuthkit.autopsy.ingest.FileIngestModule; +import org.sleuthkit.autopsy.ingest.IngestModuleFactory; +import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; + +/** + * A factory for file ingest modules that associates custom artifacts and + * attributes with files for test purposes. Uncomment the service provider + * annotation to activate this test fixture. + */ +//@ServiceProvider(service = IngestModuleFactory.class) +public final class CustomArtifactsCreatorIngestModuleFactory extends IngestModuleFactoryAdapter { + + @Override + public String getModuleDisplayName() { + return getModuleName(); + } + + @Override + public String getModuleDescription() { + return "Associates custom artifacts and attributes with files for test purposes."; + } + + @Override + public String getModuleVersionNumber() { + return Version.getVersion(); + } + + @Override + public boolean isFileIngestModuleFactory() { + return true; + } + + @Override + public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) { + return new CustomArtifactsCreatorIngestModule(); + } + + static String getModuleName() { + return "Custom Artifacts Creator"; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java index 66e98c3567..04fd16c71d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java @@ -46,7 +46,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline") @ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false) @ActionReferences(value = { - @ActionReference(path = "Menu/Tools", position = 100), + @ActionReference(path = "Menu/Tools", position = 102), @ActionReference(path = "Toolbars/Case", position = 102)}) public final class OpenTimelineAction extends CallableSystemAction implements Presenter.Toolbar { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties index f53aec5a85..1df8a62921 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/Bundle.properties @@ -11,3 +11,4 @@ ImageGalleryOptionsPanel.descriptionLabel.text=To minimize its startup tim ImageGalleryOptionsPanel.furtherDescriptionArea.text=If Image Gallery is disabled, only the fact that an update is needed is recorded. If Image Gallery is enabled after ingest, it will do one bulk update based on the results from ingest. If Image Gallery is disabled, you will be prompted to enable it when attempting to open its window. ImageGalleryOptionsPanel.unavailableDuringInjestLabel.text=This setting is unavailable during ingest. ImageGalleryOptionsPanel.groupCategorizationWarningBox.text=Don't show a warning when overwriting categories, by acting on an entire group. +CTL_OpenAction=Open Image/Video \ No newline at end of file diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/Bundle.properties b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/Bundle.properties new file mode 100755 index 0000000000..b1b078c67d --- /dev/null +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/Bundle.properties @@ -0,0 +1 @@ +CTL_AddImage=View Images/Videos \ No newline at end of file diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java index a8085acbfd..1b182d0900 100755 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java @@ -1,32 +1,37 @@ /* - * Autopsy Forensic Browser - * - * Copyright 2013 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +* Autopsy Forensic Browser +* +* Copyright 2013 Basis Technology Corp. +* Contact: carrier sleuthkit org +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ package org.sleuthkit.autopsy.imagegallery.actions; import java.awt.Component; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JOptionPane; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; +import org.openide.awt.ActionReferences; import org.openide.awt.ActionRegistration; import org.openide.util.HelpCtx; import org.openide.util.NbBundle.Messages; import org.openide.util.actions.CallableSystemAction; +import org.openide.util.actions.Presenter; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.core.Installer; @@ -35,65 +40,83 @@ import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; import org.sleuthkit.autopsy.imagegallery.ImageGalleryModule; import org.sleuthkit.autopsy.imagegallery.ImageGalleryTopComponent; -@ActionID(category = "Tools", - id = "org.sleuthkit.autopsy.imagegallery.OpenAction") -@ActionReference(path = "Menu/Tools" /* , position = 333 */) -@ActionRegistration( // iconBase = "org/sleuthkit/autopsy/imagegallery/images/lightbulb.png", - lazy = false, - displayName = "#CTL_OpenAction") +@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.imagegallery.OpenAction") +@ActionReferences(value = { + @ActionReference(path = "Menu/Tools", position = 101), + @ActionReference(path = "Toolbars/Case", position = 101) +}) +@ActionRegistration(displayName = "#CTL_OpenAction", lazy = false) @Messages({"CTL_OpenAction=View Images/Videos", - "OpenAction.stale.confDlg.msg=The image / video database may be out of date. " + - "Do you want to update and listen for further ingest results?\n" + - "Choosing 'yes' will update the database and enable listening to future ingests.", - "OpenAction.stale.confDlg.title=Image Gallery"}) -public final class OpenAction extends CallableSystemAction { - + "OpenAction.stale.confDlg.msg=The image / video database may be out of date. " + + "Do you want to update and listen for further ingest results?\n" + + "Choosing 'yes' will update the database and enable listening to future ingests.", + "OpenAction.stale.confDlg.title=Image Gallery"}) +public final class OpenAction extends CallableSystemAction implements Presenter.Toolbar { + private static final String VIEW_IMAGES_VIDEOS = Bundle.CTL_OpenAction(); - private static final boolean fxInited = Installer.isJavaFxInited(); - private static final Logger LOGGER = Logger.getLogger(OpenAction.class.getName()); - + private JButton toolbarButton = new JButton(); + private final PropertyChangeListener pcl; + public OpenAction() { super(); + toolbarButton.addActionListener(actionEvent -> performAction()); + pcl = (PropertyChangeEvent evt) -> { + if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { + setEnabled(Case.isCaseOpen()); + } + }; + Case.addPropertyChangeListener(pcl); + this.setEnabled(false); } - + @Override public boolean isEnabled() { return Case.isCaseOpen() && fxInited && Case.getCurrentCase().hasData(); } - + /** Returns the toolbar component of this action * * @return component the toolbar button */ @Override public Component getToolbarPresenter() { - JButton toolbarButton = new JButton(this); - toolbarButton.setText(VIEW_IMAGES_VIDEOS); - toolbarButton.addActionListener(this); - + ImageIcon icon = new ImageIcon(getClass().getResource("btn_icon_image_gallery_26.png")); //NON-NLS + toolbarButton.setIcon(icon); + toolbarButton.setText(this.getName()); return toolbarButton; } - + + /** + * Set this action to be enabled/disabled + * + * @param value whether to enable this action or not + */ + @Override + public void setEnabled(boolean value) { + super.setEnabled(value); + toolbarButton.setEnabled(value); + } + @Override @SuppressWarnings("fallthrough") public void performAction() { - + //check case if (!Case.isCaseOpen()) { return; } final Case currentCase = Case.getCurrentCase(); - + if (ImageGalleryModule.isDrawableDBStale(currentCase)) { //drawable db is stale, ask what to do int answer = JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(), Bundle.OpenAction_stale_confDlg_msg(), Bundle.OpenAction_stale_confDlg_title(), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); - + switch (answer) { case JOptionPane.YES_OPTION: ImageGalleryController.getDefault().setListeningEnabled(true); - //fall through + //fall through case JOptionPane.NO_OPTION: ImageGalleryTopComponent.openTopComponent(); break; @@ -105,17 +128,17 @@ public final class OpenAction extends CallableSystemAction { ImageGalleryTopComponent.openTopComponent(); } } - + @Override public String getName() { return VIEW_IMAGES_VIDEOS; } - + @Override public HelpCtx getHelpCtx() { return HelpCtx.DEFAULT_HELP; } - + @Override public boolean asynchronous() { return false; // run on edt diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/btn_icon_image_gallery_26.png b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/btn_icon_image_gallery_26.png new file mode 100755 index 0000000000..eea6d50835 Binary files /dev/null and b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/btn_icon_image_gallery_26.png differ diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/layer.xml b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/layer.xml index 4b37d6ecd8..bde41b641f 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/layer.xml +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/layer.xml @@ -17,7 +17,7 @@ + ======================================================= --> @@ -31,7 +31,7 @@ ====================================================== --> - + diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EmailMessage.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EmailMessage.java index 65039be614..a2b83ead3b 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EmailMessage.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/EmailMessage.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.thunderbirdparser; import java.util.ArrayList; import java.util.Date; import java.util.List; +import org.sleuthkit.datamodel.TskData; /** * A Record to hold generic information about email messages, regardless of the @@ -191,6 +192,8 @@ class EmailMessage { private long aTime = 0L; private long mTime = 0L; + + private TskData.EncodingType encodingType = TskData.EncodingType.NONE; String getName() { return name; @@ -275,5 +278,14 @@ class EmailMessage { this.mTime = mTime.getTime() / 1000; } } + + void setEncodingType(TskData.EncodingType encodingType){ + this.encodingType = encodingType; + } + + TskData.EncodingType getEncodingType(){ + return encodingType; + } + } } diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java index 56aea2f826..351c830280 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java @@ -57,6 +57,8 @@ import org.apache.tika.parser.txt.CharsetDetector; import org.apache.tika.parser.txt.CharsetMatch; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.EncodedFileOutputStream; /** * A parser that extracts information about email messages and attachments from @@ -285,11 +287,11 @@ class MboxParser { String uniqueFilename = fileID + "-" + index + "-" + email.getSentDate() + "-" + filename; String outPath = outputDirPath + uniqueFilename; - FileOutputStream fos; + EncodedFileOutputStream fos; BinaryBody bb; try { - fos = new FileOutputStream(outPath); - } catch (FileNotFoundException ex) { + fos = new EncodedFileOutputStream(new FileOutputStream(outPath), TskData.EncodingType.XOR1); + } catch (IOException ex) { addErrorMessage( NbBundle.getMessage(this.getClass(), "MboxParser.handleAttch.errMsg.failedToCreateOnDisk", outPath)); @@ -322,6 +324,7 @@ class MboxParser { attach.setLocalPath(ThunderbirdMboxFileIngestModule.getRelModuleOutputPath() + File.separator + uniqueFilename); attach.setSize(new File(outPath).length()); + attach.setEncodingType(TskData.EncodingType.XOR1); email.addAttachment(attach); } diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java index 5d91dd1c9b..c3b31512be 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java @@ -37,7 +37,9 @@ import org.sleuthkit.autopsy.ingest.IngestMonitor; import org.sleuthkit.autopsy.ingest.IngestServices; import static org.sleuthkit.autopsy.thunderbirdparser.ThunderbirdMboxFileIngestModule.getRelModuleOutputPath; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.EncodedFileOutputStream; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; /** * Parser for extracting emails from pst/ost Mircosoft Outlook data files. @@ -234,6 +236,7 @@ class PstParser { attachment.setmTime(mTime); attachment.setLocalPath(relPath); attachment.setSize(attach.getFilesize()); + attachment.setEncodingType(TskData.EncodingType.XOR1); email.addAttachment(attachment); } catch (PSTException | IOException | NullPointerException ex) { /** @@ -260,7 +263,8 @@ class PstParser { * @throws PSTException */ private void saveAttachmentToDisk(PSTAttachment attach, String outPath) throws IOException, PSTException { - try (InputStream attachmentStream = attach.getFileInputStream(); FileOutputStream out = new FileOutputStream(outPath)) { + try (InputStream attachmentStream = attach.getFileInputStream(); + EncodedFileOutputStream out = new EncodedFileOutputStream(new FileOutputStream(outPath), TskData.EncodingType.XOR1)) { // 8176 is the block size used internally and should give the best performance int bufferSize = 8176; byte[] buffer = new byte[bufferSize]; diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java index b641a2fe03..58d0050edc 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java @@ -331,11 +331,12 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { long cTime = attach.getcTime(); String relPath = attach.getLocalPath(); long size = attach.getSize(); + TskData.EncodingType encodingType = attach.getEncodingType(); try { DerivedFile df = fileManager.addDerivedFile(filename, relPath, size, cTime, crTime, aTime, mTime, true, abstractFile, "", - EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), ""); + EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType); files.add(df); } catch (TskCoreException ex) { postErrorMessage(