diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java index ad02d666a9..adf61a8f32 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardChooseDataSourceVisual.java @@ -39,6 +39,7 @@ import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datasourceprocessors.RawDSProcessor; /** * visual component for the first panel of add image wizard. Allows the user to @@ -92,7 +93,8 @@ final class AddImageWizardChooseDataSourceVisual extends JPanel { datasourceProcessorsMap.remove(LocalDiskDSProcessor.getType()); } coreDSPTypes.add(LocalFilesDSProcessor.getType()); - + coreDSPTypes.add(RawDSProcessor.getType()); + for (String dspType : coreDSPTypes) { typeComboBox.addItem(dspType); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java index 044559e0f8..d38eaa77ae 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java @@ -78,7 +78,6 @@ public class ImageFilePanel extends JPanel implements DocumentListener { this.contextName = context; - createTimeZoneList(); } /** @@ -89,6 +88,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener { ImageFilePanel instance = new ImageFilePanel(context, fileChooserFilters); instance.postInit(); + instance.createTimeZoneList(); return instance; } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java index 1c20079763..07cc1b159e 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java @@ -140,7 +140,6 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.accessed"), ContentUtils.getStringTime(file.getAtime(), file)); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.created"), ContentUtils.getStringTime(file.getCrtime(), file)); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.changed"), ContentUtils.getStringTime(file.getCtime(), file)); - String md5 = file.getMd5Hash(); if (md5 == null) { @@ -149,7 +148,6 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.md5"), md5); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.hashLookupResults"), file.getKnown().toString()); - addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.internalid"), Long.toString(file.getId())); if (file.getType().compareTo(TSK_DB_FILES_TYPE_ENUM.LOCAL) == 0) { addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"), file.getLocalAbsPath()); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java index 0ea877d9a5..90a9a38b85 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java @@ -130,14 +130,6 @@ public class ArtifactStringContent implements StringContent { buffer.append(""); //NON-NLS buffer.append("\n"); //NON-NLS - // add artifact ID (useful for debugging) - buffer.append(""); //NON-NLS - buffer.append(NbBundle.getMessage(this.getClass(), "ArtifactStringContent.getStr.artifactId.text")); - buffer.append(""); //NON-NLS - buffer.append(artifact.getArtifactID()); - buffer.append(""); //NON-NLS - buffer.append("\n"); //NON-NLS - buffer.append(""); //NON-NLS buffer.append("\n"); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java new file mode 100644 index 0000000000..c85eedaa06 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java @@ -0,0 +1,183 @@ +package org.sleuthkit.autopsy.datasourceprocessors; + +/* + * 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. + */ + + +import java.io.File; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskFileRange; +import org.openide.util.NbBundle.Messages; + +/* + * A runnable that adds a raw data source to a case database. + */ +final class AddRawImageTask implements Runnable { + + private static final Logger logger = Logger.getLogger(AddRawImageTask.class.getName()); + private final String deviceId; + private final String imageFilePath; + private final String timeZone; + private final long chunkSize; + private final DataSourceProcessorProgressMonitor progressMonitor; + private final DataSourceProcessorCallback callback; + private boolean criticalErrorOccurred; + private static final long TWO_GB = 2000000000L; + + /** + * Constructs a runnable that adds a raw data source to a case database. + * + * @param deviceId An ASCII-printable identifier for the + * device associated with the data source + * that is intended to be unique across + * multiple cases (e.g., a UUID). + * @param imageFilePath Path to a Raw data source file. + * @param timeZone The time zone to use when processing dates + * and times for the image, obtained from + * java.util.TimeZone.getID. + * @param breakupChunks 2GB or not breakup. + * @param progressMonitor Progress monitor for reporting + * progressMonitor during processing. + * @param callback Callback to call when processing is done. + */ + AddRawImageTask(String deviceId, String imageFilePath, String timeZone, long chunkSize, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { + this.deviceId = deviceId; + this.imageFilePath = imageFilePath; + this.timeZone = timeZone; + this.chunkSize = chunkSize; + this.callback = callback; + this.progressMonitor = progressMonitor; + } + + /** + * Adds a raw data source to a case database. + */ + @Override + public void run() { + /* + * Process the input image file. + */ + progressMonitor.setIndeterminate(true); + progressMonitor.setProgress(0); + List newDataSources = new ArrayList<>(); + List errorMessages = new ArrayList<>(); + addImageToCase(newDataSources, errorMessages); + + progressMonitor.setProgress(100); + + /** + * Return the results via the callback passed to the constructor. + */ + DataSourceProcessorCallback.DataSourceProcessorResult result; + if (criticalErrorOccurred) { + result = DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS; + } else if (!errorMessages.isEmpty()) { + result = DataSourceProcessorCallback.DataSourceProcessorResult.NONCRITICAL_ERRORS; + } else { + result = DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS; + } + callback.done(result, errorMessages, newDataSources); + criticalErrorOccurred = false; + } + + /** + * Attempts to add the input image to the case. + * + * @param newDataSources If the image is added, a data source is added to + * this list for eventual return to the caller via the + * callback. + * @param errorMessages If there are any error messages, the error messages + * are added to this list for eventual return to the + * caller via the callback. + */ + @Messages({"AddRawImageTask.progress.add.text=Adding raw image: ", + "AddRawImageTask.image.critical.error.adding=Critical error adding ", + "AddRawImageTask.for.device=for device ", + "AddRawImageTask.image.notExisting=is not existing.", + "AddRawImageTask.image.noncritical.error.adding=Non-critical error adding "}) + private void addImageToCase(List dataSources, List errorMessages) { + progressMonitor.setProgressText(Bundle.AddRawImageTask_progress_add_text() + imageFilePath); + List imageFilePaths = new ArrayList<>(); + SleuthkitCase caseDatabase = Case.getCurrentCase().getSleuthkitCase(); + caseDatabase.acquireExclusiveLock(); + + File imageFile = Paths.get(imageFilePath).toFile(); + if (!imageFile.exists()) { + errorMessages.add(Bundle.AddRawImageTask_image_critical_error_adding() + imageFilePath + Bundle.AddRawImageTask_for_device() + + deviceId + Bundle.AddRawImageTask_image_notExisting()); + criticalErrorOccurred = true; + return; + } + + imageFilePaths.add(imageFilePath); + + try { + /* + * Get Image that will be added to case + */ + Image dataSource = caseDatabase.addImageInfo(0, imageFilePaths, timeZone); //TODO: change hard coded deviceId. + dataSources.add(dataSource); + List fileRanges = new ArrayList<>(); + + /* + * Verify the size of the new image. Note that it may not be what is + * expected, but at least part of it was added to the case. + */ + String verificationError = dataSource.verifyImageSize(); + if (!verificationError.isEmpty()) { + errorMessages.add(Bundle.AddRawImageTask_image_noncritical_error_adding() + imageFilePaths + Bundle.AddRawImageTask_for_device() + deviceId + ":" + verificationError); + } + + long imageSize = dataSource.getSize(); + int sequence = 0; + //start byte and end byte + long start = 0; + if (chunkSize > 0 && imageSize >= TWO_GB) { + for (double size = TWO_GB; size < dataSource.getSize(); size += TWO_GB) { + fileRanges.add(new TskFileRange(start, TWO_GB, sequence)); + start += TWO_GB; + sequence++; + } + + } + double leftoverSize = imageSize - sequence * TWO_GB; + fileRanges.add(new TskFileRange(start, (long)leftoverSize, sequence)); + + + caseDatabase.addLayoutFiles(dataSource, fileRanges); + + } catch (TskCoreException ex) { + errorMessages.add(Bundle.AddRawImageTask_image_critical_error_adding() + imageFilePaths + Bundle.AddRawImageTask_for_device() + deviceId + ":" + ex.getLocalizedMessage()); + criticalErrorOccurred = true; + } finally { + caseDatabase.releaseExclusiveLock(); + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle.properties new file mode 100755 index 0000000000..4a8de9a48b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/Bundle.properties @@ -0,0 +1,12 @@ +# To change this license header, choose License Headers in Project Properties. +# To change this template file, choose Tools | Templates +# and open the template in the editor. + +RawDSInputPanel.pathLabel.text=Browse for an unallocated space image file: +RawDSInputPanel.errorLabel.text=Error Label +RawDSInputPanel.browseButton.text=Browse +RawDSInputPanel.pathTextField.text= +RawDSInputPanel.jBreakFileUpLabel.text=Break image up into: +RawDSInputPanel.jNoBreakupRadioButton.text=Do not break up +RawDSInputPanel.j2GBBreakupRadioButton.text=2GB chunks +RawDSInputPanel.timeZoneLabel.text=Please select the input timezone: diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.form b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.form new file mode 100755 index 0000000000..ff07eaa18c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.form @@ -0,0 +1,170 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java new file mode 100755 index 0000000000..8fb6fe5ccf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java @@ -0,0 +1,352 @@ +/* + * 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.datasourceprocessors; + +import java.io.File; +import java.util.Calendar; +import java.util.SimpleTimeZone; +import java.util.TimeZone; +import javax.swing.JFileChooser; +import javax.swing.JPanel; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.autopsy.coreutils.PathValidator; + +final class RawDSInputPanel extends JPanel implements DocumentListener { + private static final long TWO_GB = 2000000000L; + private static final long serialVersionUID = 1L; //default + private final String PROP_LASTINPUT_PATH = "LBL_LastInputFile_PATH"; + private final JFileChooser fc = new JFileChooser(); + // Externally supplied name is used to store settings + private final String contextName; + /** + * Creates new form RawDSInputPanel + */ + private RawDSInputPanel(String context) { + initComponents(); + + errorLabel.setVisible(false); + + fc.setDragEnabled(false); + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + fc.setMultiSelectionEnabled(false); + + this.contextName = context; + } + + /** + * Creates and returns an instance of a RawDSInputPanel. + */ + static synchronized RawDSInputPanel createInstance(String context) { + RawDSInputPanel instance = new RawDSInputPanel(context); + + instance.postInit(); + instance.createTimeZoneList(); + + return instance; + } + + //post-constructor initialization to properly initialize listener support + //without leaking references of uninitialized objects + private void postInit() { + pathTextField.getDocument().addDocumentListener(this); + } + + /** + * Creates the drop down list for the time zones and then makes the local + * machine time zone to be selected. + */ + private void createTimeZoneList() { + // load and add all timezone + String[] ids = SimpleTimeZone.getAvailableIDs(); + for (String id : ids) { + TimeZone zone = TimeZone.getTimeZone(id); + int offset = zone.getRawOffset() / 1000; + int hour = offset / 3600; + int minutes = (offset % 3600) / 60; + String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id); + + timeZoneComboBox.addItem(item); + } + // get the current timezone + TimeZone thisTimeZone = Calendar.getInstance().getTimeZone(); + int thisOffset = thisTimeZone.getRawOffset() / 1000; + int thisHour = thisOffset / 3600; + int thisMinutes = (thisOffset % 3600) / 60; + String formatted = String.format("(GMT%+d:%02d) %s", thisHour, thisMinutes, thisTimeZone.getID()); + + // set the selected timezone + timeZoneComboBox.setSelectedItem(formatted); + } + + /** + * 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() { + + infileTypeButtonGroup = new javax.swing.ButtonGroup(); + pathLabel = new javax.swing.JLabel(); + pathTextField = new javax.swing.JTextField(); + browseButton = new javax.swing.JButton(); + j2GBBreakupRadioButton = new javax.swing.JRadioButton(); + jBreakFileUpLabel = new javax.swing.JLabel(); + jNoBreakupRadioButton = new javax.swing.JRadioButton(); + errorLabel = new javax.swing.JLabel(); + timeZoneLabel = new javax.swing.JLabel(); + timeZoneComboBox = new javax.swing.JComboBox<>(); + + org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.pathLabel.text")); // NOI18N + + pathTextField.setText(org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.pathTextField.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.browseButton.text")); // NOI18N + browseButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + browseButtonActionPerformed(evt); + } + }); + + infileTypeButtonGroup.add(j2GBBreakupRadioButton); + j2GBBreakupRadioButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(j2GBBreakupRadioButton, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.j2GBBreakupRadioButton.text")); // NOI18N + j2GBBreakupRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + j2GBBreakupRadioButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(jBreakFileUpLabel, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.jBreakFileUpLabel.text")); // NOI18N + + infileTypeButtonGroup.add(jNoBreakupRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(jNoBreakupRadioButton, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.jNoBreakupRadioButton.text")); // NOI18N + jNoBreakupRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jNoBreakupRadioButtonActionPerformed(evt); + } + }); + + errorLabel.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.errorLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(timeZoneLabel, org.openide.util.NbBundle.getMessage(RawDSInputPanel.class, "RawDSInputPanel.timeZoneLabel.text")); // NOI18N + + timeZoneComboBox.setMaximumRowCount(30); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(pathTextField) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(browseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pathLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 218, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(timeZoneLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 168, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 199, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGap(0, 19, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jBreakFileUpLabel) + .addComponent(errorLabel) + .addGroup(layout.createSequentialGroup() + .addGap(10, 10, 10) + .addComponent(j2GBBreakupRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jNoBreakupRadioButton))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(pathLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(browseButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(timeZoneLabel) + .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(errorLabel) + .addGap(5, 5, 5) + .addComponent(jBreakFileUpLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jNoBreakupRadioButton) + .addComponent(j2GBBreakupRadioButton)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + @SuppressWarnings("deprecation") + private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed + String oldText = pathTextField.getText(); + // set the current directory of the FileChooser if the ImagePath Field is valid + File currentDir = new File(oldText); + if (currentDir.exists()) { + fc.setCurrentDirectory(currentDir); + } + + int retval = fc.showOpenDialog(this); + if (retval == JFileChooser.APPROVE_OPTION) { + String path = fc.getSelectedFile().getPath(); + pathTextField.setText(path); + } + }//GEN-LAST:event_browseButtonActionPerformed + + private void j2GBBreakupRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_j2GBBreakupRadioButtonActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_j2GBBreakupRadioButtonActionPerformed + + private void jNoBreakupRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jNoBreakupRadioButtonActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_jNoBreakupRadioButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton browseButton; + private javax.swing.JLabel errorLabel; + private javax.swing.ButtonGroup infileTypeButtonGroup; + private javax.swing.JRadioButton j2GBBreakupRadioButton; + private javax.swing.JLabel jBreakFileUpLabel; + private javax.swing.JRadioButton jNoBreakupRadioButton; + private javax.swing.JLabel pathLabel; + private javax.swing.JTextField pathTextField; + private javax.swing.JComboBox timeZoneComboBox; + private javax.swing.JLabel timeZoneLabel; + // End of variables declaration//GEN-END:variables + /** + * Get the path of the user selected image. + * + * @return the image path + */ + String getImageFilePath() { + return pathTextField.getText(); + } + + void reset() { + //reset the UI elements to default + pathTextField.setText(null); + j2GBBreakupRadioButton.setSelected(true); + } + + long getChunkSize() { + if (jNoBreakupRadioButton.isSelected()) { + return -1; + } else { //if have more choices here, the selection of each radiobutton should be checked + return TWO_GB; + } + } + + String getTimeZone() { + String tz = timeZoneComboBox.getSelectedItem().toString(); + return tz.substring(tz.indexOf(")") + 2).trim(); + } + + /** + * Should we enable the next button of the wizard? + * + * @return true if a proper image has been selected, false otherwise + */ + boolean validatePanel() { + errorLabel.setVisible(false); + String path = getImageFilePath(); + if (path == null || path.isEmpty()) { + return false; + } + + // display warning if there is one (but don't disable "next" button) + warnIfPathIsInvalid(path); + + boolean isExist = new File(path).exists(); + + return (isExist); + } + + /** + * Validates path to selected data source and displays warning if it is + * invalid. + * + * @param path Absolute path to the selected data source + */ + @Messages({"RawDSInputPanel.error.text=Path to multi-user data source is on \"C:\" drive"}) + private void warnIfPathIsInvalid(String path) { + if (!PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) { + errorLabel.setVisible(true); + errorLabel.setText(Bundle.RawDSInputPanel_error_text()); + } + } + + void storeSettings() { + String inFilePath = getImageFilePath(); + if (null != inFilePath) { + String imagePath = inFilePath.substring(0, inFilePath.lastIndexOf(File.separator) + 1); + ModuleSettings.setConfigSetting(contextName, PROP_LASTINPUT_PATH, imagePath); + } + } + + void readSettings() { + String inFilePath = ModuleSettings.getConfigSetting(contextName, PROP_LASTINPUT_PATH); + if (null != inFilePath) { + if (!inFilePath.isEmpty()) { + pathTextField.setText(inFilePath); + } + } + } + + /** + * Update functions are called by the pathTextField which has this set as + * it's DocumentEventListener. Each update function fires a property change + * to be caught by the parent panel. + * + * @param e the event, which is ignored + */ + @Override + public void insertUpdate(DocumentEvent e) { + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + } + + @Override + public void removeUpdate(DocumentEvent e) { + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + } + + @Override + public void changedUpdate(DocumentEvent e) { + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + } + + /** + * Set the focus to the pathTextField. + */ + void select() { + pathTextField.requestFocusInWindow(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java new file mode 100644 index 0000000000..68908ae7a3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSProcessor.java @@ -0,0 +1,157 @@ +/* + * 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.datasourceprocessors; + +import java.util.UUID; +import javax.swing.JPanel; +import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; + +/** + * A Raw data source processor that implements the DataSourceProcessor service + * provider interface to allow integration with the add data source wizard. + * It also provides a run method overload to allow it to be used independently + * of the wizard. + */ +@ServiceProvider(service = DataSourceProcessor.class) +public class RawDSProcessor implements DataSourceProcessor { + private final RawDSInputPanel configPanel; + private AddRawImageTask addImageTask; + + /* + * Constructs a Raw data source processor that implements the + * DataSourceProcessor service provider interface to allow integration + * with the add data source wizard. It also provides a run method + * overload to allow it to be used independently of the wizard. + */ + public RawDSProcessor() { + configPanel = RawDSInputPanel.createInstance(RawDSProcessor.class.getName()); + } + +/** + * Gets a string that describes the type of data sources this processor is + * able to add to the case database. The string is suitable for display in a + * type selection UI component (e.g., a combo box). + * + * @return A data source type display string for this data source processor. + */ + @Messages({"RawDSProcessor.dataSourceType=Unallocated Space Image File"}) + public static String getType() { + return Bundle.RawDSProcessor_dataSourceType(); + } + + /** + * Gets a string that describes the type of data sources this processor is + * able to add to the case database. The string is suitable for display in a + * type selection UI component (e.g., a combo box). + * + * @return A data source type display string for this data source processor. + */ + @Override + public String getDataSourceType() { + return Bundle.RawDSProcessor_dataSourceType(); + } + + /** + * Gets the panel that allows a user to select a data source and do any + * configuration required by the data source. The panel is less than 544 + * pixels wide and less than 173 pixels high. + * + * @return A selection and configuration panel for this data source + * processor. + */ + @Override + public JPanel getPanel() { + configPanel.readSettings(); + configPanel.select(); + return configPanel; + } + + /** + * Indicates whether the settings in the selection and configuration panel + * are valid and complete. + * + * @return True if the settings are valid and complete and the processor is + * ready to have its run method called, false otherwise. + */ + @Override + public boolean isPanelValid() { + return configPanel.validatePanel(); + } + + /** + * Adds a data source to the case database using a background task in a + * separate thread and the settings provided by the selection and + * configuration panel. Returns as soon as the background task is started. + * The background task uses a callback object to signal task completion and + * return results. + * + * This method should not be called unless isPanelValid returns true. + * + * @param progressMonitor Progress monitor that will be used by the + * background task to report progress. + * @param callback Callback that will be used by the background task + * to return results. + */ + @Override + public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { + configPanel.storeSettings(); + run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getTimeZone(), configPanel.getChunkSize(), progressMonitor, callback); + } + + /** + * Adds a data source to the case database using a background task in a + * separate thread and the given settings instead of those provided by the + * selection and configuration panel. Returns as soon as the background task + * is started and uses the callback object to signal task completion and + * return results. + * + * @param deviceId An ASCII-printable identifier for the + * device associated with the data source + * that is intended to be unique across + * multiple cases (e.g., a UUID). + * @param rawDSInputFilePath Path to a Raw data source file. + * @param isHandsetFile Indicates whether the XML file is for a + * handset or a SIM. + * @param progressMonitor Progress monitor for reporting progress + * during processing. + */ + private void run(String deviceId, String imageFilePath, String timeZone, long chunkSize, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { + addImageTask = new AddRawImageTask(deviceId, imageFilePath, timeZone, chunkSize, progressMonitor, callback); + new Thread(addImageTask).start(); + } + + + @Override + public void cancel() { + } + + /** + * Resets the selection and configuration panel for this data source + * processor. + */ + @Override + public void reset() { + configPanel.reset(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/Bundle.properties index 76c16fd56f..b454208635 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/Bundle.properties @@ -1,6 +1,5 @@ VMExtractorIngestModuleFactory.moduleDisplayName=Virtual Machine Extractor VMExtractorIngestModuleFactory.moduleDescription=Extracts virtual machine files and adds them to a case as data sources. -VMExtractorIngestModuleFactory.version=1.0 VMExtractorIngestModule.addedVirtualMachineImage.message=Added virtual machine image {0} VMExtractorIngestModule.searchingImage.message=Searching image for virtual machine files VMExtractorIngestModule.exportingToDisk.message=Exporting virtual machine files to disk diff --git a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModuleFactory.java index e26bb81e51..c525e87dad 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModuleFactory.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.modules.vmextractor; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.coreutils.Version; import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; import org.sleuthkit.autopsy.ingest.IngestModuleFactory; import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; @@ -53,7 +54,7 @@ public final class VMExtractorIngestModuleFactory extends IngestModuleFactoryAda @Override public String getModuleVersionNumber() { - return NbBundle.getMessage(this.getClass(), "VMExtractorIngestModuleFactory.version"); + return Version.getVersion(); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java index 6ed903c7b2..6f32fd2f65 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java @@ -170,6 +170,8 @@ class ReportGenerator { TableReportGenerator generator = new TableReportGenerator(artifactTypeSelections, tagNameSelections, progressPanel, tableReport); generator.execute(); tableReport.endReport(); + // finish progress, wrap up + progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE); errorList = generator.getErrorList(); }); worker.execute(); diff --git a/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java index f4afd1bd1e..d3a782948c 100755 --- a/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java @@ -114,9 +114,6 @@ class TableReportGenerator { // report on the tagged images makeThumbnailTable(); } - - // finish progress, wrap up - progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE); } /**