19 package org.sleuthkit.autopsy.casemodule;
 
   22 import java.nio.file.Paths;
 
   23 import java.util.Calendar;
 
   24 import java.util.List;
 
   25 import java.util.logging.Level;
 
   26 import javax.swing.JFileChooser;
 
   27 import javax.swing.JPanel;
 
   28 import org.openide.util.NbBundle;
 
   43     "LocalDiskPanel.errorMessage.noOpenCaseTitle=No open case available",
 
   44     "LocalDiskPanel.errorMessage.noOpenCaseBody=LocalDiskPanel listener couldn't get the open case.",
 
   45     "LocalDiskPanel.imageWriterError.directoryNotExist=Error - directory does not exist",
 
   46     "LocalDiskPanel.imageWriterError.emptyPath=Error - enter path for VHD",
 
   47     "LocalDiskPanel.imageWriterError.isDirectory=Error - VHD path is a directory",
 
   48     "LocalDiskPanel.imageWriterError.fileExists=Error - VHD path already exists",
 
   49     "LocalDiskPanel.moduleErrorMessage.title=Module Error",
 
   50     "LocalDiskPanel.moduleErrorMessage.body=A module caused an error listening to LocalDiskPanel updates. See log to determine which module. Some data could be incomplete.",
 
   51     "LocalDiskPanel.localDiskMessage.unspecified=Unspecified" 
   53 @SuppressWarnings(
"PMD.SingularField") 
 
   54 final class LocalDiskPanel 
extends JPanel {
 
   56     private static final Logger logger = Logger.getLogger(LocalDiskPanel.class.getName());
 
   57     private static final String[] SECTOR_SIZE_CHOICES = {
"Auto Detect", 
"512", 
"1024", 
"2048", 
"4096"};
 
   58     private static LocalDiskPanel instance;
 
   59     private static final long serialVersionUID = 1L;
 
   60     private LocalDisk localDisk;
 
   61     private boolean enableNext = 
false;
 
   62     private JFileChooser fc;
 
   63     private final JFileChooserFactory chooserHelper;
 
   72         createSectorSizeList();
 
   73         chooserHelper = 
new JFileChooserFactory();
 
   79     static synchronized LocalDiskPanel getDefault() {
 
   80         if (instance == null) {
 
   81             instance = 
new LocalDiskPanel();
 
   86     @SuppressWarnings(
"unchecked")
 
   87     private 
void customInit() {
 
   88         imageWriterErrorLabel.setVisible(
false);
 
   89         imageWriterErrorLabel.setText(
"");
 
   90         if (!PlatformUtil.isWindowsOS()) {
 
   91             copyImageCheckbox.setSelected(
false);
 
   92             copyImageCheckbox.setEnabled(
false);
 
   94         pathTextField.setEnabled(copyImageCheckbox.isSelected());
 
   95         browseButton.setEnabled(copyImageCheckbox.isSelected());
 
   96         changeDatabasePathCheckbox.setEnabled(copyImageCheckbox.isSelected());
 
  104     @SuppressWarnings(
"unchecked")
 
  106     private 
void initComponents() {
 
  108         timeZoneLabel = 
new javax.swing.JLabel();
 
  109         timeZoneComboBox = 
new javax.swing.JComboBox<>();
 
  110         noFatOrphansCheckbox = 
new javax.swing.JCheckBox();
 
  111         descLabel = 
new javax.swing.JLabel();
 
  112         copyImageCheckbox = 
new javax.swing.JCheckBox();
 
  113         pathTextField = 
new javax.swing.JTextField();
 
  114         browseButton = 
new javax.swing.JButton();
 
  115         jLabel1 = 
new javax.swing.JLabel();
 
  116         imageWriterErrorLabel = 
new javax.swing.JLabel();
 
  117         changeDatabasePathCheckbox = 
new javax.swing.JCheckBox();
 
  118         sectorSizeLabel = 
new javax.swing.JLabel();
 
  119         sectorSizeComboBox = 
new javax.swing.JComboBox<>();
 
  120         selectDiskButton = 
new javax.swing.JButton();
 
  121         localDiskLabel = 
new javax.swing.JLabel();
 
  122         localDiskNameTextField = 
new javax.swing.JTextField();
 
  124         setMinimumSize(
new java.awt.Dimension(0, 420));
 
  125         setPreferredSize(
new java.awt.Dimension(485, 410));
 
  127         org.openide.awt.Mnemonics.setLocalizedText(timeZoneLabel, 
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.timeZoneLabel.text")); 
 
  129         timeZoneComboBox.setMaximumRowCount(30);
 
  131         org.openide.awt.Mnemonics.setLocalizedText(noFatOrphansCheckbox, 
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.noFatOrphansCheckbox.text")); 
 
  132         noFatOrphansCheckbox.setToolTipText(
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.noFatOrphansCheckbox.toolTipText")); 
 
  134         org.openide.awt.Mnemonics.setLocalizedText(descLabel, 
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.descLabel.text")); 
 
  136         org.openide.awt.Mnemonics.setLocalizedText(copyImageCheckbox, 
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.copyImageCheckbox.text")); 
 
  137         copyImageCheckbox.addActionListener(
new java.awt.event.ActionListener() {
 
  138             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
  139                 copyImageCheckboxActionPerformed(evt);
 
  143         pathTextField.setText(
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.pathTextField.text")); 
 
  144         pathTextField.addKeyListener(
new java.awt.event.KeyAdapter() {
 
  145             public void keyReleased(java.awt.event.KeyEvent evt) {
 
  146                 pathTextFieldKeyReleased(evt);
 
  150         org.openide.awt.Mnemonics.setLocalizedText(browseButton, 
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.browseButton.text")); 
 
  151         browseButton.addActionListener(
new java.awt.event.ActionListener() {
 
  152             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
  153                 browseButtonActionPerformed(evt);
 
  157         org.openide.awt.Mnemonics.setLocalizedText(jLabel1, 
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.jLabel1.text")); 
 
  159         imageWriterErrorLabel.setForeground(
new java.awt.Color(255, 0, 0));
 
  160         org.openide.awt.Mnemonics.setLocalizedText(imageWriterErrorLabel, 
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.imageWriterErrorLabel.text")); 
 
  162         org.openide.awt.Mnemonics.setLocalizedText(changeDatabasePathCheckbox, 
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.changeDatabasePathCheckbox.text")); 
 
  164         org.openide.awt.Mnemonics.setLocalizedText(sectorSizeLabel, 
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.sectorSizeLabel.text")); 
 
  166         org.openide.awt.Mnemonics.setLocalizedText(selectDiskButton, 
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.selectDiskButton.text")); 
 
  167         selectDiskButton.addActionListener(
new java.awt.event.ActionListener() {
 
  168             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
  169                 selectDiskButtonActionPerformed(evt);
 
  173         org.openide.awt.Mnemonics.setLocalizedText(localDiskLabel, 
org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, 
"LocalDiskPanel.localDiskLabel.text")); 
 
  175         localDiskNameTextField.setEditable(
false);
 
  177         javax.swing.GroupLayout layout = 
new javax.swing.GroupLayout(
this);
 
  178         this.setLayout(layout);
 
  179         layout.setHorizontalGroup(
 
  180             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  181             .addGroup(layout.createSequentialGroup()
 
  182                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  183                     .addGroup(layout.createSequentialGroup()
 
  184                         .addComponent(localDiskLabel)
 
  185                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
 
  186                         .addComponent(localDiskNameTextField)
 
  187                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
  188                         .addComponent(selectDiskButton))
 
  189                     .addGroup(layout.createSequentialGroup()
 
  190                         .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  191                             .addGroup(layout.createSequentialGroup()
 
  192                                 .addComponent(timeZoneLabel)
 
  193                                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
 
  194                                 .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 275, javax.swing.GroupLayout.PREFERRED_SIZE))
 
  195                             .addComponent(noFatOrphansCheckbox)
 
  196                             .addComponent(copyImageCheckbox)
 
  197                             .addGroup(layout.createSequentialGroup()
 
  199                                 .addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 342, javax.swing.GroupLayout.PREFERRED_SIZE)
 
  200                                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
  201                                 .addComponent(browseButton))
 
  202                             .addGroup(layout.createSequentialGroup()
 
  204                                 .addComponent(sectorSizeLabel)
 
  205                                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
 
  206                                 .addComponent(sectorSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
 
  207                             .addGroup(layout.createSequentialGroup()
 
  209                                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  210                                     .addComponent(descLabel)
 
  211                                     .addComponent(changeDatabasePathCheckbox)
 
  212                                     .addGroup(layout.createSequentialGroup()
 
  214                                         .addComponent(jLabel1))
 
  215                                     .addComponent(imageWriterErrorLabel))))
 
  216                         .addGap(0, 39, Short.MAX_VALUE)))
 
  219         layout.setVerticalGroup(
 
  220             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  221             .addGroup(layout.createSequentialGroup()
 
  222                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
 
  223                     .addComponent(localDiskLabel)
 
  224                     .addComponent(selectDiskButton)
 
  225                     .addComponent(localDiskNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
 
  227                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
 
  228                     .addComponent(timeZoneLabel)
 
  229                     .addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
 
  231                 .addComponent(noFatOrphansCheckbox)
 
  232                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
  233                 .addComponent(descLabel)
 
  235                 .addComponent(copyImageCheckbox)
 
  236                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
  237                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
 
  238                     .addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
 
  239                     .addComponent(browseButton))
 
  241                 .addComponent(changeDatabasePathCheckbox)
 
  242                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
  243                 .addComponent(jLabel1)
 
  244                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
 
  245                 .addComponent(imageWriterErrorLabel)
 
  247                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
 
  248                     .addComponent(sectorSizeLabel)
 
  249                     .addComponent(sectorSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
 
  250                 .addContainerGap(127, Short.MAX_VALUE))
 
  254     private void copyImageCheckboxActionPerformed(java.awt.event.ActionEvent evt) {
 
  255         pathTextField.setEnabled(copyImageCheckbox.isSelected());
 
  256         browseButton.setEnabled(copyImageCheckbox.isSelected());
 
  257         changeDatabasePathCheckbox.setEnabled(copyImageCheckbox.isSelected());
 
  258         changeDatabasePathCheckbox.setSelected(copyImageCheckbox.isSelected());
 
  262     private void pathTextFieldKeyReleased(java.awt.event.KeyEvent evt) {
 
  266     private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {
 
  267         fc = chooserHelper.getChooser();
 
  268         String oldText = pathTextField.getText();
 
  270         File currentFile = 
new File(oldText);
 
  271         if ((currentFile.getParentFile() != null) && (currentFile.getParentFile().exists())) {
 
  272             fc.setSelectedFile(currentFile);
 
  275         int retval = fc.showOpenDialog(
this);
 
  276         if (retval == JFileChooser.APPROVE_OPTION) {
 
  277             String path = fc.getSelectedFile().getPath();
 
  278             pathTextField.setText(path);
 
  283     private void selectDiskButtonActionPerformed(java.awt.event.ActionEvent evt) {
 
  284         LocalDiskSelectionDialog selectionDialog = 
new LocalDiskSelectionDialog();
 
  285         selectionDialog.display();
 
  286         LocalDisk selectedLocalDisk = selectionDialog.getLocalDiskSelection();
 
  287         if (selectedLocalDisk != null) {
 
  288             localDisk = selectedLocalDisk;
 
  289             localDiskNameTextField.setText(selectedLocalDisk.getName());
 
  293                 setPotentialImageWriterPath(localDisk);
 
  294                 firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), 
false, 
true);
 
  295             } 
catch (NoCurrentCaseException ex) {
 
  296                 logger.log(Level.SEVERE, 
"Exception while getting open case.", ex); 
 
  297                 MessageNotifyUtil.Notify.show(Bundle.LocalDiskPanel_errorMessage_noOpenCaseTitle(),
 
  298                         Bundle.LocalDiskPanel_errorMessage_noOpenCaseBody(),
 
  299                         MessageNotifyUtil.MessageType.ERROR);
 
  300             } 
catch (Exception ex) {
 
  301                 logger.log(Level.SEVERE, 
"LocalDiskPanel listener threw exception", ex); 
 
  302                 MessageNotifyUtil.Notify.show(Bundle.LocalDiskPanel_moduleErrorMessage_title(),
 
  303                         Bundle.LocalDiskPanel_moduleErrorMessage_body(),
 
  304                         MessageNotifyUtil.MessageType.ERROR);
 
  310     private javax.swing.JButton browseButton;
 
  311     private javax.swing.JCheckBox changeDatabasePathCheckbox;
 
  312     private javax.swing.JCheckBox copyImageCheckbox;
 
  313     private javax.swing.JLabel descLabel;
 
  314     private javax.swing.JLabel imageWriterErrorLabel;
 
  315     private javax.swing.JLabel jLabel1;
 
  316     private javax.swing.JLabel localDiskLabel;
 
  317     private javax.swing.JTextField localDiskNameTextField;
 
  318     private javax.swing.JCheckBox noFatOrphansCheckbox;
 
  319     private javax.swing.JTextField pathTextField;
 
  320     private javax.swing.JComboBox<String> sectorSizeComboBox;
 
  321     private javax.swing.JLabel sectorSizeLabel;
 
  322     private javax.swing.JButton selectDiskButton;
 
  323     private javax.swing.JComboBox<String> timeZoneComboBox;
 
  324     private javax.swing.JLabel timeZoneLabel;
 
  327     private void fireUpdateEvent() {
 
  329             firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), 
false, 
true);
 
  330         } 
catch (Exception e) {
 
  331             logger.log(Level.SEVERE, 
"LocalDiskPanel listener threw exception", e); 
 
  332             MessageNotifyUtil.Notify.show(Bundle.LocalDiskPanel_moduleErrorMessage_title(),
 
  333                     Bundle.LocalDiskPanel_moduleErrorMessage_body(),
 
  334                     MessageNotifyUtil.MessageType.ERROR);
 
  341     void resetLocalDiskSelection() {
 
  343         localDiskNameTextField.setText(Bundle.LocalDiskPanel_localDiskMessage_unspecified());
 
  352     String getContentPath() {
 
  353         if (localDisk != null) {
 
  354             return localDisk.getPath();
 
  365     int getSectorSize() {
 
  366         int sectorSizeSelectionIndex = sectorSizeComboBox.getSelectedIndex();
 
  368         if (sectorSizeSelectionIndex == 0) {
 
  372         return Integer.valueOf((String) sectorSizeComboBox.getSelectedItem());
 
  375     String getTimeZone() {
 
  376         String tz = timeZoneComboBox.getSelectedItem().toString();
 
  377         return tz.substring(tz.indexOf(
")") + 2).trim();
 
  381     boolean getNoFatOrphans() {
 
  382         return noFatOrphansCheckbox.isSelected();
 
  385     private static String getDefaultImageWriterFolder() throws NoCurrentCaseException {
 
  386         return Paths.get(Case.getCurrentCaseThrows().getModuleDirectory(), 
"Image Writer").toString();
 
  389     private void setPotentialImageWriterPath(LocalDisk disk) 
throws NoCurrentCaseException {
 
  391         File subDirectory = Paths.get(getDefaultImageWriterFolder()).toFile();
 
  392         if (!subDirectory.exists()) {
 
  393             subDirectory.mkdirs();
 
  396         String path = disk.getName();
 
  400         path = path.replaceAll(
"[^0-9A-Za-z _()]", 
""); 
 
  401         if (path.isEmpty() || path.startsWith(
" ")) {
 
  402             path = 
"localDisk" + path;
 
  405         path += 
" " + System.currentTimeMillis();
 
  407         pathTextField.setText(Paths.get(getDefaultImageWriterFolder(), path).toString());
 
  410     private boolean imageWriterPathIsValid() {
 
  411         if ((!copyImageCheckbox.isSelected()) || localDisk == null) {
 
  412             imageWriterErrorLabel.setVisible(
false);
 
  413             imageWriterErrorLabel.setText(
"");
 
  417         if (pathTextField.getText().isEmpty()) {
 
  418             imageWriterErrorLabel.setVisible(
true);
 
  419             imageWriterErrorLabel.setText(Bundle.LocalDiskPanel_imageWriterError_emptyPath());
 
  423         File f = 
new File(pathTextField.getText());
 
  424         if (((f.getParentFile() != null) && (!f.getParentFile().exists()))
 
  425                 || (f.getParentFile() == null)) {
 
  426             imageWriterErrorLabel.setVisible(
true);
 
  427             imageWriterErrorLabel.setText(Bundle.LocalDiskPanel_imageWriterError_directoryNotExist());
 
  430         if (f.isDirectory()) {
 
  431             imageWriterErrorLabel.setVisible(
true);
 
  432             imageWriterErrorLabel.setText(Bundle.LocalDiskPanel_imageWriterError_isDirectory());
 
  436             imageWriterErrorLabel.setVisible(
true);
 
  437             imageWriterErrorLabel.setText(Bundle.LocalDiskPanel_imageWriterError_fileExists());
 
  440         imageWriterErrorLabel.setVisible(
false);
 
  441         imageWriterErrorLabel.setText(
"");
 
  445     boolean getImageWriterEnabled() {
 
  446         return copyImageCheckbox.isSelected();
 
  449     ImageWriterSettings getImageWriterSettings() {
 
  450         return new ImageWriterSettings(pathTextField.getText(), changeDatabasePathCheckbox.isSelected());
 
  459     boolean validatePanel() {
 
  460         if (!imageWriterPathIsValid()) {
 
  470     public void createTimeZoneList() {
 
  471         List<String> timeZoneList = TimeZoneUtils.createTimeZoneList();
 
  472         for (String timeZone : timeZoneList) {
 
  473             timeZoneComboBox.addItem(timeZone);
 
  477         timeZoneComboBox.setSelectedItem(TimeZoneUtils.createTimeZoneString(Calendar.getInstance().getTimeZone()));
 
  484     private void createSectorSizeList() {
 
  485         for (String choice : SECTOR_SIZE_CHOICES) {
 
  486             sectorSizeComboBox.addItem(choice);
 
  488         sectorSizeComboBox.setSelectedIndex(0);