19 package org.sleuthkit.autopsy.logicalimager.configuration;
21 import com.google.gson.Gson;
22 import com.google.gson.GsonBuilder;
23 import com.google.gson.JsonIOException;
24 import com.google.gson.JsonSyntaxException;
26 import java.io.FileInputStream;
27 import java.io.FileNotFoundException;
28 import java.io.IOException;
29 import java.io.InputStreamReader;
30 import java.nio.charset.StandardCharsets;
31 import java.nio.file.FileStore;
32 import java.nio.file.FileSystem;
33 import java.nio.file.FileSystems;
34 import java.nio.file.Files;
35 import java.nio.file.Paths;
36 import java.nio.file.spi.FileSystemProvider;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.logging.Level;
40 import javax.swing.JFileChooser;
41 import javax.swing.JOptionPane;
42 import javax.swing.JPanel;
43 import javax.swing.SwingUtilities;
44 import javax.swing.event.DocumentEvent;
45 import javax.swing.event.DocumentListener;
46 import javax.swing.filechooser.FileFilter;
47 import javax.swing.filechooser.FileNameExtensionFilter;
48 import javax.swing.filechooser.FileSystemView;
49 import org.apache.commons.lang.StringUtils;
50 import org.openide.util.NbBundle;
51 import org.openide.util.NbBundle.Messages;
58 @SuppressWarnings(
"PMD.SingularField")
59 final class ConfigVisualPanel1
extends JPanel {
62 private static final long serialVersionUID = 1L;
63 private static final String DEFAULT_CONFIG_FILE_NAME =
"logical-imager-config.json";
64 private static final String UPDATE_UI_EVENT_NAME =
"UPDATE_UI";
65 private String configFilename;
70 ConfigVisualPanel1() {
72 configFileTextField.getDocument().addDocumentListener(
new MyDocumentListener(
this));
74 SwingUtilities.invokeLater(() -> {
80 "ConfigVisualPanel1.selectConfigurationFile=Select location"
83 public String getName() {
84 return Bundle.ConfigVisualPanel1_selectConfigurationFile();
93 private void initComponents() {
95 configurationLocationButtonGroup =
new javax.swing.ButtonGroup();
96 configFileTextField =
new javax.swing.JTextField();
97 browseButton =
new javax.swing.JButton();
98 descriptionScrollPane =
new javax.swing.JScrollPane();
99 descriptionTextArea =
new javax.swing.JTextArea();
100 configureDriveRadioButton =
new javax.swing.JRadioButton();
101 configureFolderRadioButton =
new javax.swing.JRadioButton();
102 driveListScrollPane =
new javax.swing.JScrollPane();
103 driveList =
new javax.swing.JList<>();
104 refreshButton =
new javax.swing.JButton();
105 warningLabel =
new javax.swing.JLabel();
107 configFileTextField.setEditable(
false);
108 configFileTextField.setEnabled(
false);
110 org.openide.awt.Mnemonics.setLocalizedText(browseButton,
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.browseButton.text"));
111 browseButton.setToolTipText(
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.browseButton.toolTipText"));
112 browseButton.setEnabled(
false);
113 browseButton.addActionListener(
new java.awt.event.ActionListener() {
114 public void actionPerformed(java.awt.event.ActionEvent evt) {
115 browseButtonActionPerformed(evt);
119 descriptionTextArea.setEditable(
false);
120 descriptionTextArea.setBackground(
new java.awt.Color(240, 240, 240));
121 descriptionTextArea.setColumns(20);
122 descriptionTextArea.setFont(
new java.awt.Font(
"Tahoma", 0, 11));
123 descriptionTextArea.setLineWrap(
true);
124 descriptionTextArea.setRows(4);
125 descriptionTextArea.setText(
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.descriptionTextArea.text"));
126 descriptionTextArea.setWrapStyleWord(
true);
127 descriptionTextArea.setEnabled(
false);
128 descriptionScrollPane.setViewportView(descriptionTextArea);
130 configurationLocationButtonGroup.add(configureDriveRadioButton);
131 configureDriveRadioButton.setSelected(
true);
132 org.openide.awt.Mnemonics.setLocalizedText(configureDriveRadioButton,
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.configureDriveRadioButton.text_1"));
133 configureDriveRadioButton.addActionListener(
new java.awt.event.ActionListener() {
134 public void actionPerformed(java.awt.event.ActionEvent evt) {
135 configureDriveRadioButtonActionPerformed(evt);
139 configurationLocationButtonGroup.add(configureFolderRadioButton);
140 org.openide.awt.Mnemonics.setLocalizedText(configureFolderRadioButton,
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.configureFolderRadioButton.text_1"));
141 configureFolderRadioButton.addActionListener(
new java.awt.event.ActionListener() {
142 public void actionPerformed(java.awt.event.ActionEvent evt) {
143 configureFolderRadioButtonActionPerformed(evt);
147 driveList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
148 driveList.setEnabled(
false);
149 driveList.addMouseListener(
new java.awt.event.MouseAdapter() {
150 public void mouseReleased(java.awt.event.MouseEvent evt) {
151 driveListMouseReleasedSelection(evt);
154 driveList.addKeyListener(
new java.awt.event.KeyAdapter() {
155 public void keyReleased(java.awt.event.KeyEvent evt) {
156 driveListKeyReleasedSelection(evt);
159 driveListScrollPane.setViewportView(driveList);
161 org.openide.awt.Mnemonics.setLocalizedText(refreshButton,
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.refreshButton.text"));
162 refreshButton.setEnabled(
false);
163 refreshButton.addActionListener(
new java.awt.event.ActionListener() {
164 public void actionPerformed(java.awt.event.ActionEvent evt) {
165 refreshButtonActionPerformed(evt);
169 warningLabel.setForeground(
new java.awt.Color(255, 0, 0));
171 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
172 this.setLayout(layout);
173 layout.setHorizontalGroup(
174 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
175 .addGroup(layout.createSequentialGroup()
177 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
178 .addComponent(warningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
179 .addComponent(descriptionScrollPane)
180 .addGroup(layout.createSequentialGroup()
181 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
182 .addGroup(layout.createSequentialGroup()
184 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
185 .addComponent(driveListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 437, javax.swing.GroupLayout.PREFERRED_SIZE)
186 .addComponent(configFileTextField, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 437, javax.swing.GroupLayout.PREFERRED_SIZE))
187 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
188 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
189 .addComponent(refreshButton, javax.swing.GroupLayout.PREFERRED_SIZE, 87, javax.swing.GroupLayout.PREFERRED_SIZE)
190 .addComponent(browseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 87, javax.swing.GroupLayout.PREFERRED_SIZE)))
191 .addComponent(configureDriveRadioButton)
192 .addComponent(configureFolderRadioButton))
193 .addGap(0, 0, Short.MAX_VALUE)))
196 layout.setVerticalGroup(
197 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
198 .addGroup(layout.createSequentialGroup()
200 .addComponent(descriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
202 .addComponent(configureDriveRadioButton)
203 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
204 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
205 .addComponent(driveListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
206 .addGroup(layout.createSequentialGroup()
207 .addComponent(refreshButton)
208 .addGap(0, 95, Short.MAX_VALUE)))
209 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
210 .addComponent(configureFolderRadioButton)
211 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
212 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
213 .addComponent(configFileTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
214 .addComponent(browseButton))
215 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
216 .addComponent(warningLabel)
217 .addContainerGap(60, Short.MAX_VALUE))
222 "ConfigVisualPanel1.chooseFileTitle=Select a Logical Imager configuration"
224 private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {
225 chooseFile(Bundle.ConfigVisualPanel1_chooseFileTitle());
228 private void configureFolderRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {
232 private void configureDriveRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {
236 private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {
240 private void driveListKeyReleasedSelection(java.awt.event.KeyEvent evt) {
241 firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
244 private void driveListMouseReleasedSelection(java.awt.event.MouseEvent evt) {
245 firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
253 @Messages({
"ConfigVisualPanel1.unknown=Unknown"})
254 private String getFileSystemName(String drive) {
255 FileSystem fileSystem = FileSystems.getDefault();
256 FileSystemProvider provider = fileSystem.provider();
258 FileStore fileStore = provider.getFileStore(Paths.get(drive));
259 return fileStore.type();
260 }
catch (IOException ex) {
261 return Bundle.ConfigVisualPanel1_unknown();
269 "ConfigVisualPanel1.messageLabel.noExternalDriveFound=No drive found",
270 "# {0} - root",
"# {1} - description",
"# {2} - size with unit",
"# {3} - file system",
271 "ConfigVisualPanel1.driveListItem={0} ({1}) ({2}) - File system: {3}"
273 private void refreshDriveList() {
274 List<String> listData =
new ArrayList<>();
275 File[] roots = File.listRoots();
276 int firstRemovableDrive = -1;
278 for (File root : roots) {
282 String description = FileSystemView.getFileSystemView().getSystemTypeDescription(root);
283 long spaceInBytes = root.getTotalSpace();
285 String fileSystem = getFileSystemName(root.toString());
286 listData.add(Bundle.ConfigVisualPanel1_driveListItem(root, description, sizeWithUnit, fileSystem));
287 if (firstRemovableDrive == -1) {
289 FileStore fileStore = Files.getFileStore(root.toPath());
290 if ((
boolean) fileStore.getAttribute(
"volume:isRemovable")) {
291 firstRemovableDrive = i;
293 }
catch (IOException ignored) {
295 logger.log(Level.INFO, String.format(
"Unable to select first removable drive found %s", root.toString()));
300 driveList.setListData(listData.toArray(
new String[listData.size()]));
301 if (!listData.isEmpty()) {
303 driveList.setSelectedIndex(firstRemovableDrive == -1 ? 0 : firstRemovableDrive);
304 firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
305 driveList.requestFocusInWindow();
306 warningLabel.setText(
"");
308 warningLabel.setText(Bundle.ConfigVisualPanel1_messageLabel_noExternalDriveFound());
316 private void updateControls() {
317 browseButton.setEnabled(configureFolderRadioButton.isSelected());
318 refreshButton.setEnabled(configureDriveRadioButton.isSelected());
319 driveList.setEnabled(configureDriveRadioButton.isSelected());
320 driveListScrollPane.setEnabled(configureDriveRadioButton.isSelected());
321 firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
331 "ConfigVisualPanel1.fileNameExtensionFilter=Configuration JSON File",
332 "ConfigVisualPanel1.invalidConfigJson=Invalid config JSON: ",
333 "ConfigVisualPanel1.configurationError=Configuration error",})
334 private void chooseFile(String title) {
335 final String jsonExt =
".json";
336 JFileChooser fileChooser =
new JFileChooser();
337 fileChooser.setDialogTitle(title);
338 fileChooser.setDragEnabled(
false);
339 fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
340 FileFilter filter =
new FileNameExtensionFilter(Bundle.ConfigVisualPanel1_fileNameExtensionFilter(),
new String[]{
"json"});
341 fileChooser.setFileFilter(filter);
342 fileChooser.setSelectedFile(
new File(DEFAULT_CONFIG_FILE_NAME));
343 fileChooser.setMultiSelectionEnabled(
false);
344 if (fileChooser.showOpenDialog(
this) == JFileChooser.APPROVE_OPTION) {
345 String path = fileChooser.getSelectedFile().getPath();
346 if (
new File(path).exists()) {
348 loadConfigFile(path);
349 configFilename = path;
350 configFileTextField.setText(path);
351 }
catch (JsonIOException | JsonSyntaxException | IOException ex) {
352 JOptionPane.showMessageDialog(
this,
353 Bundle.ConfigVisualPanel1_invalidConfigJson() + ex.getMessage(),
354 Bundle.ConfigVisualPanel1_configurationError(),
355 JOptionPane.ERROR_MESSAGE);
358 if (!path.endsWith(jsonExt)) {
361 configFilename = path;
362 configFileTextField.setText(path);
368 private javax.swing.JButton browseButton;
369 private javax.swing.JTextField configFileTextField;
370 private javax.swing.ButtonGroup configurationLocationButtonGroup;
371 private javax.swing.JRadioButton configureDriveRadioButton;
372 private javax.swing.JRadioButton configureFolderRadioButton;
373 private javax.swing.JScrollPane descriptionScrollPane;
374 private javax.swing.JTextArea descriptionTextArea;
375 private javax.swing.JList<String> driveList;
376 private javax.swing.JScrollPane driveListScrollPane;
377 private javax.swing.JButton refreshButton;
378 private javax.swing.JLabel warningLabel;
397 "ConfigVisualPanel1.configFileIsEmpty=Configuration file {0} is empty",})
398 private LogicalImagerConfig loadConfigFile(String path)
throws FileNotFoundException, JsonIOException, JsonSyntaxException, IOException {
399 try (FileInputStream is =
new FileInputStream(path)) {
400 InputStreamReader reader =
new InputStreamReader(is, StandardCharsets.UTF_8);
401 GsonBuilder gsonBuilder =
new GsonBuilder().setPrettyPrinting();
402 gsonBuilder.registerTypeAdapter(LogicalImagerConfig.class,
new LogicalImagerConfigDeserializer());
403 Gson gson = gsonBuilder.create();
404 LogicalImagerConfig loadedConfig = gson.fromJson(reader, LogicalImagerConfig.class);
405 if (loadedConfig == null) {
407 throw new IOException(Bundle.ConfigVisualPanel1_configFileIsEmpty(path));
419 LogicalImagerConfig getConfig() {
420 String configFileName = getConfigPath();
421 if (configFileName != null &&
new File(configFileName).exists()) {
423 return loadConfigFile(configFileName);
424 }
catch (JsonIOException | JsonSyntaxException | IOException ex) {
425 return new LogicalImagerConfig();
428 return new LogicalImagerConfig();
438 String getConfigPath() {
439 if (configureFolderRadioButton.isSelected()) {
440 return configFilename;
442 String selectedStr = driveList.getSelectedValue();
443 if (selectedStr == null) {
446 return selectedStr.substring(0, 3) + DEFAULT_CONFIG_FILE_NAME;
456 static String getUpdateEventName() {
457 return UPDATE_UI_EVENT_NAME;
460 void setConfigFilename(String filename) {
461 configFileTextField.setText(filename);
469 boolean isPanelValid() {
470 return !StringUtils.isBlank(getConfigPath())
471 && !(getFileSystemName(getConfigPath().substring(0, 3)).equals(
"FAT")
472 || getFileSystemName(getConfigPath().substring(0, 3)).equals(
"FAT32"))
473 && ((configureDriveRadioButton.isSelected() && !StringUtils.isBlank(driveList.getSelectedValue()))
474 || (configureFolderRadioButton.isSelected() && (!configFileTextField.getText().isEmpty())));
482 private final ConfigVisualPanel1
panel;
504 panel.firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
void insertUpdate(DocumentEvent e)
static boolean isNetworkDrive(String driveLetter)
void removeUpdate(DocumentEvent e)
static String humanReadableByteCount(long bytes, boolean si)
final ConfigVisualPanel1 panel
synchronized static Logger getLogger(String name)
void changedUpdate(DocumentEvent e)