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));
73 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.setLineWrap(
true);
123 descriptionTextArea.setRows(4);
124 descriptionTextArea.setText(
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.descriptionTextArea.text"));
125 descriptionTextArea.setWrapStyleWord(
true);
126 descriptionTextArea.setEnabled(
false);
127 descriptionScrollPane.setViewportView(descriptionTextArea);
129 configurationLocationButtonGroup.add(configureDriveRadioButton);
130 configureDriveRadioButton.setSelected(
true);
131 org.openide.awt.Mnemonics.setLocalizedText(configureDriveRadioButton,
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.configureDriveRadioButton.text_1"));
132 configureDriveRadioButton.addActionListener(
new java.awt.event.ActionListener() {
133 public void actionPerformed(java.awt.event.ActionEvent evt) {
134 configureDriveRadioButtonActionPerformed(evt);
138 configurationLocationButtonGroup.add(configureFolderRadioButton);
139 org.openide.awt.Mnemonics.setLocalizedText(configureFolderRadioButton,
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.configureFolderRadioButton.text_1"));
140 configureFolderRadioButton.addActionListener(
new java.awt.event.ActionListener() {
141 public void actionPerformed(java.awt.event.ActionEvent evt) {
142 configureFolderRadioButtonActionPerformed(evt);
146 driveList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
147 driveList.setEnabled(
false);
148 driveList.addMouseListener(
new java.awt.event.MouseAdapter() {
149 public void mouseReleased(java.awt.event.MouseEvent evt) {
150 driveListMouseReleasedSelection(evt);
153 driveList.addKeyListener(
new java.awt.event.KeyAdapter() {
154 public void keyReleased(java.awt.event.KeyEvent evt) {
155 driveListKeyReleasedSelection(evt);
158 driveListScrollPane.setViewportView(driveList);
160 org.openide.awt.Mnemonics.setLocalizedText(refreshButton,
org.openide.util.NbBundle.getMessage(ConfigVisualPanel1.class,
"ConfigVisualPanel1.refreshButton.text"));
161 refreshButton.setEnabled(
false);
162 refreshButton.addActionListener(
new java.awt.event.ActionListener() {
163 public void actionPerformed(java.awt.event.ActionEvent evt) {
164 refreshButtonActionPerformed(evt);
168 warningLabel.setForeground(
new java.awt.Color(255, 0, 0));
170 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
171 this.setLayout(layout);
172 layout.setHorizontalGroup(
173 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
174 .addGroup(layout.createSequentialGroup()
176 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
177 .addComponent(warningLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
178 .addComponent(descriptionScrollPane)
179 .addGroup(layout.createSequentialGroup()
180 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
181 .addGroup(layout.createSequentialGroup()
183 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
184 .addComponent(driveListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 437, javax.swing.GroupLayout.PREFERRED_SIZE)
185 .addComponent(configFileTextField, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 437, javax.swing.GroupLayout.PREFERRED_SIZE))
186 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
187 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
188 .addComponent(refreshButton, javax.swing.GroupLayout.PREFERRED_SIZE, 87, javax.swing.GroupLayout.PREFERRED_SIZE)
189 .addComponent(browseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 87, javax.swing.GroupLayout.PREFERRED_SIZE)))
190 .addComponent(configureDriveRadioButton)
191 .addComponent(configureFolderRadioButton))
192 .addGap(0, 0, Short.MAX_VALUE)))
195 layout.setVerticalGroup(
196 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
197 .addGroup(layout.createSequentialGroup()
199 .addComponent(descriptionScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
201 .addComponent(configureDriveRadioButton)
202 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
203 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
204 .addComponent(driveListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
205 .addGroup(layout.createSequentialGroup()
206 .addComponent(refreshButton)
207 .addGap(0, 87, Short.MAX_VALUE)))
208 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
209 .addComponent(configureFolderRadioButton)
210 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
211 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
212 .addComponent(configFileTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
213 .addComponent(browseButton))
214 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
215 .addComponent(warningLabel)
216 .addContainerGap(52, Short.MAX_VALUE))
221 "ConfigVisualPanel1.chooseFileTitle=Select a Logical Imager configuration"
223 private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {
224 chooseFile(Bundle.ConfigVisualPanel1_chooseFileTitle());
227 private void configureFolderRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {
231 private void configureDriveRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {
235 private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {
239 private void driveListKeyReleasedSelection(java.awt.event.KeyEvent evt) {
240 firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
243 private void driveListMouseReleasedSelection(java.awt.event.MouseEvent evt) {
244 firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
252 @Messages({
"ConfigVisualPanel1.unknown=Unknown"})
253 private String getFileSystemName(String drive) {
254 FileSystem fileSystem = FileSystems.getDefault();
255 FileSystemProvider provider = fileSystem.provider();
257 FileStore fileStore = provider.getFileStore(Paths.get(drive));
258 return fileStore.type();
259 }
catch (IOException ex) {
260 return Bundle.ConfigVisualPanel1_unknown();
268 "ConfigVisualPanel1.messageLabel.noExternalDriveFound=No drive found",
269 "# {0} - root",
"# {1} - description",
"# {2} - size with unit",
"# {3} - file system",
270 "ConfigVisualPanel1.driveListItem={0} ({1}) ({2}) - File system: {3}"
272 private void refreshDriveList() {
273 List<String> listData =
new ArrayList<>();
274 File[] roots = File.listRoots();
275 int firstRemovableDrive = -1;
277 for (File root : roots) {
281 String description = FileSystemView.getFileSystemView().getSystemTypeDescription(root);
282 long spaceInBytes = root.getTotalSpace();
284 String fileSystem = getFileSystemName(root.toString());
285 listData.add(Bundle.ConfigVisualPanel1_driveListItem(root, description, sizeWithUnit, fileSystem));
286 if (firstRemovableDrive == -1) {
288 FileStore fileStore = Files.getFileStore(root.toPath());
289 if ((
boolean) fileStore.getAttribute(
"volume:isRemovable")) {
290 firstRemovableDrive = i;
292 }
catch (IOException ignored) {
294 logger.log(Level.INFO, String.format(
"Unable to select first removable drive found %s", root.toString()));
299 driveList.setListData(listData.toArray(
new String[listData.size()]));
300 if (!listData.isEmpty()) {
302 driveList.setSelectedIndex(firstRemovableDrive == -1 ? 0 : firstRemovableDrive);
303 firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
304 driveList.requestFocusInWindow();
305 warningLabel.setText(
"");
307 warningLabel.setText(Bundle.ConfigVisualPanel1_messageLabel_noExternalDriveFound());
315 private void updateControls() {
316 browseButton.setEnabled(configureFolderRadioButton.isSelected());
317 refreshButton.setEnabled(configureDriveRadioButton.isSelected());
318 driveList.setEnabled(configureDriveRadioButton.isSelected());
319 driveListScrollPane.setEnabled(configureDriveRadioButton.isSelected());
320 firePropertyChange(UPDATE_UI_EVENT_NAME,
false,
true);
330 "ConfigVisualPanel1.fileNameExtensionFilter=Configuration JSON File",
331 "ConfigVisualPanel1.invalidConfigJson=Invalid config JSON: ",
332 "ConfigVisualPanel1.configurationError=Configuration error",})
333 private void chooseFile(String title) {
334 final String jsonExt =
".json";
335 JFileChooser fileChooser =
new JFileChooser();
336 fileChooser.setDialogTitle(title);
337 fileChooser.setDragEnabled(
false);
338 fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
339 FileFilter filter =
new FileNameExtensionFilter(Bundle.ConfigVisualPanel1_fileNameExtensionFilter(),
new String[]{
"json"});
340 fileChooser.setFileFilter(filter);
341 fileChooser.setSelectedFile(
new File(DEFAULT_CONFIG_FILE_NAME));
342 fileChooser.setMultiSelectionEnabled(
false);
343 if (fileChooser.showOpenDialog(
this) == JFileChooser.APPROVE_OPTION) {
344 String path = fileChooser.getSelectedFile().getPath();
345 if (
new File(path).exists()) {
347 loadConfigFile(path);
348 configFilename = path;
349 configFileTextField.setText(path);
350 }
catch (JsonIOException | JsonSyntaxException | IOException ex) {
351 JOptionPane.showMessageDialog(
this,
352 Bundle.ConfigVisualPanel1_invalidConfigJson() + ex.getMessage(),
353 Bundle.ConfigVisualPanel1_configurationError(),
354 JOptionPane.ERROR_MESSAGE);
357 if (!path.endsWith(jsonExt)) {
360 configFilename = path;
361 configFileTextField.setText(path);
367 private javax.swing.JButton browseButton;
368 private javax.swing.JTextField configFileTextField;
369 private javax.swing.ButtonGroup configurationLocationButtonGroup;
370 private javax.swing.JRadioButton configureDriveRadioButton;
371 private javax.swing.JRadioButton configureFolderRadioButton;
372 private javax.swing.JScrollPane descriptionScrollPane;
373 private javax.swing.JTextArea descriptionTextArea;
374 private javax.swing.JList<String> driveList;
375 private javax.swing.JScrollPane driveListScrollPane;
376 private javax.swing.JButton refreshButton;
377 private javax.swing.JLabel warningLabel;
396 "ConfigVisualPanel1.configFileIsEmpty=Configuration file {0} is empty",})
397 private LogicalImagerConfig loadConfigFile(String path)
throws FileNotFoundException, JsonIOException, JsonSyntaxException, IOException {
398 try (FileInputStream is =
new FileInputStream(path)) {
399 InputStreamReader reader =
new InputStreamReader(is, StandardCharsets.UTF_8);
400 GsonBuilder gsonBuilder =
new GsonBuilder().setPrettyPrinting();
401 gsonBuilder.registerTypeAdapter(LogicalImagerConfig.class,
new LogicalImagerConfigDeserializer());
402 Gson gson = gsonBuilder.create();
403 LogicalImagerConfig loadedConfig = gson.fromJson(reader, LogicalImagerConfig.class);
404 if (loadedConfig == null) {
406 throw new IOException(Bundle.ConfigVisualPanel1_configFileIsEmpty(path));
418 LogicalImagerConfig getConfig() {
419 String configFileName = getConfigPath();
420 if (configFileName != null &&
new File(configFileName).exists()) {
422 return loadConfigFile(configFileName);
423 }
catch (JsonIOException | JsonSyntaxException | IOException ex) {
424 return new LogicalImagerConfig();
427 return new LogicalImagerConfig();
437 String getConfigPath() {
438 if (configureFolderRadioButton.isSelected()) {
439 return configFilename;
441 String selectedStr = driveList.getSelectedValue();
442 if (selectedStr == null) {
445 return selectedStr.substring(0, 3) + DEFAULT_CONFIG_FILE_NAME;
455 static String getUpdateEventName() {
456 return UPDATE_UI_EVENT_NAME;
459 void setConfigFilename(String filename) {
460 configFileTextField.setText(filename);
468 boolean isPanelValid() {
469 return !StringUtils.isBlank(getConfigPath())
470 && !(getFileSystemName(getConfigPath().substring(0, 3)).equals(
"FAT")
471 || getFileSystemName(getConfigPath().substring(0, 3)).equals(
"FAT32"))
472 && ((configureDriveRadioButton.isSelected() && !StringUtils.isBlank(driveList.getSelectedValue()))
473 || (configureFolderRadioButton.isSelected() && (!configFileTextField.getText().isEmpty())));
481 private final ConfigVisualPanel1
panel;
503 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)