/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import javax.swing.ComboBoxModel;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.ListCellRenderer;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListDataListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.LocalDisk;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/**
* ImageTypePanel for adding a local disk or partition such as PhysicalDrive0 or
* C:.
*/
final class LocalDiskPanel extends JPanel {
private static final Logger logger = Logger.getLogger(LocalDiskPanel.class.getName());
private static LocalDiskPanel instance;
private static final long serialVersionUID = 1L;
private List<LocalDisk> disks;
private LocalDiskModel model;
private boolean enableNext = false;
/**
* Creates new form LocalDiskPanel
*/
public LocalDiskPanel() {
this.disks = new ArrayList<>();
initComponents();
customInit();
createTimeZoneList();
}
/**
* Get the default instance of this panel.
*/
public static synchronized LocalDiskPanel getDefault() {
if (instance == null) {
instance = new LocalDiskPanel();
}
return instance;
}
@SuppressWarnings("unchecked")
private void customInit() {
model = new LocalDiskModel();
diskComboBox.setModel(model);
diskComboBox.setRenderer(model);
errorLabel.setVisible(false);
errorLabel.setText("");
diskComboBox.setEnabled(false);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
diskLabel = new javax.swing.JLabel();
diskComboBox = new javax.swing.JComboBox<>();
errorLabel = new javax.swing.JLabel();
timeZoneLabel = new javax.swing.JLabel();
timeZoneComboBox = new javax.swing.JComboBox<>();
noFatOrphansCheckbox = new javax.swing.JCheckBox();
descLabel = new javax.swing.JLabel();
setMinimumSize(new java.awt.Dimension(0, 65));
setPreferredSize(new java.awt.Dimension(485, 65));
diskLabel.setFont(diskLabel.getFont().deriveFont(diskLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
org.openide.awt.Mnemonics.setLocalizedText(diskLabel, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.diskLabel.text")); // NOI18N
diskComboBox.setFont(diskComboBox.getFont().deriveFont(diskComboBox.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
errorLabel.setFont(errorLabel.getFont().deriveFont(errorLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
errorLabel.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.errorLabel.text")); // NOI18N
timeZoneLabel.setFont(timeZoneLabel.getFont().deriveFont(timeZoneLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
org.openide.awt.Mnemonics.setLocalizedText(timeZoneLabel, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.timeZoneLabel.text")); // NOI18N
timeZoneComboBox.setFont(timeZoneComboBox.getFont().deriveFont(timeZoneComboBox.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
timeZoneComboBox.setMaximumRowCount(30);
noFatOrphansCheckbox.setFont(noFatOrphansCheckbox.getFont().deriveFont(noFatOrphansCheckbox.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
org.openide.awt.Mnemonics.setLocalizedText(noFatOrphansCheckbox, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.noFatOrphansCheckbox.text")); // NOI18N
noFatOrphansCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.noFatOrphansCheckbox.toolTipText")); // NOI18N
descLabel.setFont(descLabel.getFont().deriveFont(descLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
org.openide.awt.Mnemonics.setLocalizedText(descLabel, org.openide.util.NbBundle.getMessage(LocalDiskPanel.class, "LocalDiskPanel.descLabel.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()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(diskLabel)
.addComponent(diskComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 345, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(errorLabel)
.addGroup(layout.createSequentialGroup()
.addComponent(timeZoneLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(timeZoneComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(noFatOrphansCheckbox)
.addGroup(layout.createSequentialGroup()
.addGap(21, 21, 21)
.addComponent(descLabel)))
.addGap(0, 102, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(diskLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(diskComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(13, 13, 13)
.addComponent(errorLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.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))
.addGap(18, 18, 18)
.addComponent(noFatOrphansCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(descLabel)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel descLabel;
private javax.swing.JComboBox<LocalDisk> diskComboBox;
private javax.swing.JLabel diskLabel;
private javax.swing.JLabel errorLabel;
private javax.swing.JCheckBox noFatOrphansCheckbox;
private javax.swing.JComboBox<String> timeZoneComboBox;
private javax.swing.JLabel timeZoneLabel;
// End of variables declaration//GEN-END:variables
/**
* Return the currently selected disk path.
*
* @return String selected disk path
*/
public String getContentPaths() {
if (disks.size() > 0) {
LocalDisk selected = (LocalDisk) diskComboBox.getSelectedItem();
return selected.getPath();
} else {
return "";
}
}
/**
* Set the selected disk.
*/
public void setContentPath(String s) {
for (int i = 0; i < disks.size(); i++) {
if (disks.get(i).getPath().equals(s)) {
diskComboBox.setSelectedIndex(i);
}
}
}
public String getTimeZone() {
String tz = timeZoneComboBox.getSelectedItem().toString();
return tz.substring(tz.indexOf(")") + 2).trim();
}
boolean getNoFatOrphans() {
return noFatOrphansCheckbox.isSelected();
}
/**
* Should we enable the wizard's next button? Always return true because we
* control the possible selections.
*
* @return true
*/
public boolean validatePanel() {
return enableNext;
}
public void reset() {
//nothing to reset
}
/**
* Set the focus to the diskComboBox and refreshes the list of disks.
*/
public void select() {
diskComboBox.requestFocusInWindow();
model.loadDisks();
}
/**
* Creates the drop down list for the time zones and then makes the local
* machine time zone to be selected.
*/
public 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); //NON-NLS
/*
* DateFormat dfm = new SimpleDateFormat("z");
* dfm.setTimeZone(zone); boolean hasDaylight =
* zone.useDaylightTime(); String first = dfm.format(new Date(2010,
* 1, 1)); String second = dfm.format(new Date(2011, 6, 6)); int mid
* = hour * -1; String result = first + Integer.toString(mid);
* if(hasDaylight){ result = result + second; }
* timeZoneComboBox.addItem(item + " (" + result + ")");
*/
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()); //NON-NLS
// set the selected timezone
timeZoneComboBox.setSelectedItem(formatted);
}
@SuppressWarnings("rawtypes")
private class LocalDiskModel implements ComboBoxModel, ListCellRenderer {
private Object selected;
private boolean ready = false;
private volatile boolean loadingDisks = false;
List<LocalDisk> physicalDrives = new ArrayList<>();
List<LocalDisk> partitions = new ArrayList<>();
//private String SELECT = "Select a local disk:";
private final String LOADING = NbBundle.getMessage(this.getClass(), "LocalDiskPanel.localDiskModel.loading.msg");
private final String NO_DRIVES = NbBundle.getMessage(this.getClass(), "LocalDiskPanel.localDiskModel.nodrives.msg");
LocalDiskThread worker = null;
private void loadDisks() {
// if there is a worker already building the lists, then cancel it first.
if (loadingDisks && worker != null) {
worker.cancel(false);
}
// Clear the lists
errorLabel.setText("");
disks = new ArrayList<>();
physicalDrives = new ArrayList<>();
partitions = new ArrayList<>();
diskComboBox.setEnabled(false);
ready = false;
enableNext = false;
loadingDisks = true;
worker = new LocalDiskThread();
worker.execute();
}
@Override
public void setSelectedItem(Object anItem) {
if (ready) {
selected = (LocalDisk) anItem;
enableNext = true;
try {
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
} catch (Exception e) {
logger.log(Level.SEVERE, "LocalDiskPanel listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "LocalDiskPanel.moduleErr"),
NbBundle.getMessage(this.getClass(), "LocalDiskPanel.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
}
}
@Override
public Object getSelectedItem() {
if (ready) {
if (disks.isEmpty()) {
return NO_DRIVES;
}
return selected;
} else {
return LOADING;
}
}
@Override
public int getSize() {
if (ready) {
if (disks.isEmpty()) {
return 1;
}
return disks.size();
} else {
return 1;
}
}
@Override
public Object getElementAt(int index) {
if (ready) {
if (disks.isEmpty()) {
return NO_DRIVES;
}
return disks.get(index);
} else {
return LOADING;
}
}
@Override
public void addListDataListener(ListDataListener l) {
}
@Override
public void removeListDataListener(ListDataListener l) {
}
@SuppressWarnings("rawtypes")
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JPanel panel = new JPanel(new BorderLayout());
JLabel label = new JLabel();
if ((index == physicalDrives.size() - 1) && (physicalDrives.size() > 0)) {
panel.add(new JSeparator(JSeparator.HORIZONTAL), BorderLayout.SOUTH);
}
if (isSelected) {
label.setBackground(list.getSelectionBackground());
label.setForeground(list.getSelectionForeground());
} else {
label.setBackground(list.getBackground());
label.setForeground(list.getForeground());
}
if (value != null) {
String localDiskString = value.toString();
label.setText(value.toString());
if ((localDiskString.equals(LOADING)) || (localDiskString.equals(NO_DRIVES))) {
label.setFont(label.getFont().deriveFont(Font.ITALIC));
label.setBackground(Color.GRAY);
}
}
label.setOpaque(true);
label.setBorder(new EmptyBorder(2, 2, 2, 2));
panel.add(label, BorderLayout.CENTER);
return panel;
}
class LocalDiskThread extends SwingWorker<Object, Void> {
private final Logger logger = Logger.getLogger(LocalDiskThread.class.getName());
@Override
protected Object doInBackground() throws Exception {
// Populate the lists
physicalDrives = PlatformUtil.getPhysicalDrives();
partitions = PlatformUtil.getPartitions();
return null;
}
private void displayErrors() {
if (physicalDrives.isEmpty() && partitions.isEmpty()) {
if (PlatformUtil.isWindowsOS()) {
errorLabel.setText(
NbBundle.getMessage(this.getClass(), "LocalDiskPanel.errLabel.disksNotDetected.text"));
errorLabel.setToolTipText(NbBundle.getMessage(this.getClass(),
"LocalDiskPanel.errLabel.disksNotDetected.toolTipText"));
} else {
errorLabel.setText(
NbBundle.getMessage(this.getClass(), "LocalDiskPanel.errLabel.drivesNotDetected.text"));
errorLabel.setToolTipText(NbBundle.getMessage(this.getClass(),
"LocalDiskPanel.errLabel.drivesNotDetected.toolTipText"));
}
diskComboBox.setEnabled(false);
} else if (physicalDrives.isEmpty()) {
errorLabel.setText(
NbBundle.getMessage(this.getClass(), "LocalDiskPanel.errLabel.someDisksNotDetected.text"));
errorLabel.setToolTipText(NbBundle.getMessage(this.getClass(),
"LocalDiskPanel.errLabel.someDisksNotDetected.toolTipText"));
}
}
@Override
protected void done() {
try {
super.get(); //block and get all exceptions thrown while doInBackground()
} catch (CancellationException ex) {
logger.log(Level.INFO, "Loading local disks was canceled, which should not be possible."); //NON-NLS
} catch (InterruptedException ex) {
logger.log(Level.INFO, "Loading local disks was interrupted."); //NON-NLS
} catch (Exception ex) {
logger.log(Level.SEVERE, "Fatal error when loading local disks", ex); //NON-NLS
} finally {
if (!this.isCancelled()) {
enableNext = false;
displayErrors();
worker = null;
loadingDisks = false;
disks.addAll(physicalDrives);
disks.addAll(partitions);
if (disks.size() > 0) {
diskComboBox.setEnabled(true);
diskComboBox.setSelectedIndex(0);
}
ready = true;
} else {
logger.log(Level.INFO, "Loading local disks was canceled, which should not be possible."); //NON-NLS
}
}
}
}
}
}