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;
59 @SuppressWarnings(
"PMD.SingularField")
60 final class ConfigVisualPanel1
extends JPanel {
63 private static final long serialVersionUID = 1L;
64 private static final String DEFAULT_CONFIG_FILE_NAME =
"logical-imager-config.json";
65 private static final String UPDATE_UI_EVENT_NAME =
"UPDATE_UI";
66 private String configFilename;
72 ConfigVisualPanel1() {
74 configFileTextField.getDocument().addDocumentListener(
new MyDocumentListener(
this));
75 SwingUtilities.invokeLater(() -> {
83 "ConfigVisualPanel1.selectConfigurationFile=Select location"
86 public String getName() {
87 return Bundle.ConfigVisualPanel1_selectConfigurationFile();
96 private void initComponents() {
98 configurationLocationButtonGroup =
new javax.swing.ButtonGroup();
99 configFileTextField =
new javax.swing.JTextField();
100 browseButton =
new javax.swing.JButton();
101 descriptionScrollPane =
new javax.swing.JScrollPane();
102 descriptionTextArea =
new javax.swing.JTextArea();
103 configureDriveRadioButton =
new javax.swing.JRadioButton();
104 configureFolderRadioButton =
new javax.swing.JRadioButton();
105 driveListScrollPane =
new javax.swing.JScrollPane();
106 driveList =
new javax.swing.JList<>();
107 refreshButton =
new javax.swing.JButton();
108 warningLabel =
new javax.swing.JLabel();
110 configFileTextField.setEditable(
false);
111 configFileTextField.setEnabled(
false);
113 org.openide.awt.Mnemonics.setLocalizedText(browseButton,
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.browseButton.text"));
114 browseButton.setToolTipText(
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.browseButton.toolTipText"));
115 browseButton.setEnabled(
false);
116 browseButton.addActionListener(
new java.awt.event.ActionListener() {
117 public void actionPerformed(java.awt.event.ActionEvent evt) {
118 browseButtonActionPerformed(evt);
122 descriptionTextArea.setEditable(
false);
123 descriptionTextArea.setBackground(
new java.awt.Color(240, 240, 240));
124 descriptionTextArea.setColumns(20);
125 descriptionTextArea.setLineWrap(
true);
126 descriptionTextArea.setRows(4);
127 descriptionTextArea.setText(
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.descriptionTextArea.text"));
128 descriptionTextArea.setWrapStyleWord(
true);
129 descriptionTextArea.setEnabled(
false);
130 descriptionScrollPane.setViewportView(descriptionTextArea);
132 configurationLocationButtonGroup.add(configureDriveRadioButton);
133 configureDriveRadioButton.setSelected(
true);
134 org.openide.awt.Mnemonics.setLocalizedText(configureDriveRadioButton,
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.configureDriveRadioButton.text_1"));
135 configureDriveRadioButton.addActionListener(
new java.awt.event.ActionListener() {
136 public void actionPerformed(java.awt.event.ActionEvent evt) {
137 configureDriveRadioButtonActionPerformed(evt);
141 configurationLocationButtonGroup.add(configureFolderRadioButton);
142 org.openide.awt.Mnemonics.setLocalizedText(configureFolderRadioButton,
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.configureFolderRadioButton.text_1"));
143 configureFolderRadioButton.addActionListener(
new java.awt.event.ActionListener() {
144 public void actionPerformed(java.awt.event.ActionEvent evt) {
145 configureFolderRadioButtonActionPerformed(evt);
149 driveList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
150 driveList.setEnabled(
false);
151 driveList.addMouseListener(
new java.awt.event.MouseAdapter() {
152 public void mouseReleased(java.awt.event.MouseEvent evt) {
153 driveListMouseReleasedSelection(evt);
156 driveList.addKeyListener(
new java.awt.event.KeyAdapter() {
157 public void keyReleased(java.awt.event.KeyEvent evt) {
158 driveListKeyReleasedSelection(evt);
161 driveListScrollPane.setViewportView(driveList);
163 org.openide.awt.Mnemonics.setLocalizedText(refreshButton,
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.refreshButton.text"));
164 refreshButton.setEnabled(
false);
165 refreshButton.addActionListener(
new java.awt.event.ActionListener() {
166 public void actionPerformed(java.awt.event.ActionEvent evt) {
167 refreshButtonActionPerformed(evt);
171 warningLabel.setForeground(
new java.awt.Color(255, 0, 0));
173 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
174 this.setLayout(layout);
175 layout.setHorizontalGroup(
176 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
177 .addGroup(layout.createSequentialGroup()
179 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
180 .addComponent(warningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
181 .addComponent(descriptionScrollPane)
182 .addGroup(layout.createSequentialGroup()
183 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
184 .addGroup(layout.createSequentialGroup()
186 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
187 .addComponent(driveListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 437, javax.swing.GroupLayout.PREFERRED_SIZE)
188 .addComponent(configFileTextField, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 437, javax.swing.GroupLayout.PREFERRED_SIZE))
189 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
190 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
191 .addComponent(refreshButton, javax.swing.GroupLayout.PREFERRED_SIZE, 87, javax.swing.GroupLayout.PREFERRED_SIZE)
192 .addComponent(browseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 87, javax.swing.GroupLayout.PREFERRED_SIZE)))
193 .addComponent(configureDriveRadioButton)
194 .addComponent(configureFolderRadioButton))
195 .addGap(0, 0, Short.MAX_VALUE)))
198 layout.setVerticalGroup(
199 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
200 .addGroup(layout.createSequentialGroup()
202 .addComponent(descriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
204 .addComponent(configureDriveRadioButton)
205 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
206 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
207 .addComponent(driveListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
208 .addGroup(layout.createSequentialGroup()
209 .addComponent(refreshButton)
210 .addGap(0, 87, Short.MAX_VALUE)))
211 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
212 .addComponent(configureFolderRadioButton)
213 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
214 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
215 .addComponent(configFileTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
216 .addComponent(browseButton))
217 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
218 .addComponent(warningLabel)
219 .addContainerGap(52, Short.MAX_VALUE))
224 "ConfigVisualPanel1.chooseFileTitle=Select a Logical Imager configuration"
226 private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {
227 chooseFile(Bundle.ConfigVisualPanel1_chooseFileTitle());
230 private void configureFolderRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {
234 private void configureDriveRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {
238 private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {
242 private void driveListKeyReleasedSelection(java.awt.event.KeyEvent evt) {
243 firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
246 private void driveListMouseReleasedSelection(java.awt.event.MouseEvent evt) {
247 firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
255 @Messages({
"ConfigVisualPanel1.unknown=Unknown"})
256 private String getFileSystemName(String drive) {
257 FileSystem fileSystem = FileSystems.getDefault();
258 FileSystemProvider provider = fileSystem.provider();
260 FileStore fileStore = provider.getFileStore(Paths.get(drive));
261 return fileStore.type();
262 }
catch (IOException ex) {
263 return Bundle.ConfigVisualPanel1_unknown();
271 "ConfigVisualPanel1.messageLabel.noExternalDriveFound=No drive found",
272 "# {0} - root",
"# {1} - description",
"# {2} - size with unit",
"# {3} - file system",
273 "ConfigVisualPanel1.driveListItem={0} ({1}) ({2}) - File system: {3}"
275 private void refreshDriveList() {
276 List<String> listData =
new ArrayList<>();
277 File[] roots = File.listRoots();
278 int firstRemovableDrive = -1;
280 for (File root : roots) {
284 String description = FileSystemView.getFileSystemView().getSystemTypeDescription(root);
285 long spaceInBytes = root.getTotalSpace();
287 String fileSystem = getFileSystemName(root.toString());
288 listData.add(Bundle.ConfigVisualPanel1_driveListItem(root, description, sizeWithUnit, fileSystem));
289 if (firstRemovableDrive == -1) {
291 FileStore fileStore = Files.getFileStore(root.toPath());
292 if ((
boolean) fileStore.getAttribute(
"volume:isRemovable")) {
293 firstRemovableDrive = i;
295 }
catch (IOException ignored) {
297 logger.log(Level.INFO, String.format(
"Unable to select first removable drive found %s", root.toString()));
302 driveList.setListData(listData.toArray(
new String[listData.size()]));
303 if (!listData.isEmpty()) {
305 driveList.setSelectedIndex(firstRemovableDrive == -1 ? 0 : firstRemovableDrive);
306 firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
307 driveList.requestFocusInWindow();
308 warningLabel.setText(
"");
310 warningLabel.setText(Bundle.ConfigVisualPanel1_messageLabel_noExternalDriveFound());
318 private void updateControls() {
319 browseButton.setEnabled(configureFolderRadioButton.isSelected());
320 refreshButton.setEnabled(configureDriveRadioButton.isSelected());
321 driveList.setEnabled(configureDriveRadioButton.isSelected());
322 driveListScrollPane.setEnabled(configureDriveRadioButton.isSelected());
323 firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
333 "ConfigVisualPanel1.fileNameExtensionFilter=Configuration JSON File",
334 "ConfigVisualPanel1.invalidConfigJson=Invalid config JSON: ",
335 "ConfigVisualPanel1.configurationError=Configuration error",})
336 private void chooseFile(String title) {
337 final String jsonExt =
".json";
338 JFileChooser fileChooser = chooserHelper.
getChooser();
339 fileChooser.setDialogTitle(title);
340 fileChooser.setDragEnabled(
false);
341 fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
342 FileFilter filter =
new FileNameExtensionFilter(Bundle.ConfigVisualPanel1_fileNameExtensionFilter(),
new String[]{
"json"});
343 fileChooser.setFileFilter(filter);
344 fileChooser.setSelectedFile(
new File(DEFAULT_CONFIG_FILE_NAME));
345 fileChooser.setMultiSelectionEnabled(
false);
346 if (fileChooser.showOpenDialog(
this) == JFileChooser.APPROVE_OPTION) {
347 String path = fileChooser.getSelectedFile().getPath();
348 if (
new File(path).exists()) {
350 loadConfigFile(path);
351 configFilename = path;
352 configFileTextField.setText(path);
353 }
catch (JsonIOException | JsonSyntaxException | IOException ex) {
354 JOptionPane.showMessageDialog(
this,
355 Bundle.ConfigVisualPanel1_invalidConfigJson() + ex.getMessage(),
356 Bundle.ConfigVisualPanel1_configurationError(),
357 JOptionPane.ERROR_MESSAGE);
360 if (!path.endsWith(jsonExt)) {
363 configFilename = path;
364 configFileTextField.setText(path);
370 private javax.swing.JButton browseButton;
371 private javax.swing.JTextField configFileTextField;
372 private javax.swing.ButtonGroup configurationLocationButtonGroup;
373 private javax.swing.JRadioButton configureDriveRadioButton;
374 private javax.swing.JRadioButton configureFolderRadioButton;
375 private javax.swing.JScrollPane descriptionScrollPane;
376 private javax.swing.JTextArea descriptionTextArea;
377 private javax.swing.JList<String> driveList;
378 private javax.swing.JScrollPane driveListScrollPane;
379 private javax.swing.JButton refreshButton;
380 private javax.swing.JLabel warningLabel;
399 "ConfigVisualPanel1.configFileIsEmpty=Configuration file {0} is empty",})
400 private LogicalImagerConfig loadConfigFile(String path)
throws FileNotFoundException, JsonIOException, JsonSyntaxException, IOException {
401 try (FileInputStream is =
new FileInputStream(path)) {
402 InputStreamReader reader =
new InputStreamReader(is, StandardCharsets.UTF_8);
403 GsonBuilder gsonBuilder =
new GsonBuilder().setPrettyPrinting();
404 gsonBuilder.registerTypeAdapter(LogicalImagerConfig.class,
new LogicalImagerConfigDeserializer());
405 Gson gson = gsonBuilder.create();
406 LogicalImagerConfig loadedConfig = gson.fromJson(reader, LogicalImagerConfig.class);
407 if (loadedConfig == null) {
409 throw new IOException(Bundle.ConfigVisualPanel1_configFileIsEmpty(path));
421 LogicalImagerConfig getConfig() {
422 String configFileName = getConfigPath();
423 if (configFileName != null &&
new File(configFileName).exists()) {
425 return loadConfigFile(configFileName);
426 }
catch (JsonIOException | JsonSyntaxException | IOException ex) {
427 return new LogicalImagerConfig();
430 return new LogicalImagerConfig();
440 String getConfigPath() {
441 if (configureFolderRadioButton.isSelected()) {
442 return configFilename;
444 String selectedStr = driveList.getSelectedValue();
445 if (selectedStr == null) {
448 return selectedStr.substring(0, 3) + DEFAULT_CONFIG_FILE_NAME;
458 static String getUpdateEventName() {
459 return UPDATE_UI_EVENT_NAME;
462 void setConfigFilename(String filename) {
463 configFileTextField.setText(filename);
471 boolean isPanelValid() {
472 return !StringUtils.isBlank(getConfigPath())
473 && !(getFileSystemName(getConfigPath().substring(0, 3)).equals(
"FAT")
474 || getFileSystemName(getConfigPath().substring(0, 3)).equals(
"FAT32"))
475 && ((configureDriveRadioButton.isSelected() && !StringUtils.isBlank(driveList.getSelectedValue()))
476 || (configureFolderRadioButton.isSelected() && (!configFileTextField.getText().isEmpty())));
484 private final ConfigVisualPanel1
panel;
506 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)
JFileChooser getChooser()
final ConfigVisualPanel1 panel
synchronized static Logger getLogger(String name)
void changedUpdate(DocumentEvent e)