Autopsy  4.17.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
AddManualEvent.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.timeline.actions;
20 
21 import java.awt.Dialog;
22 import java.time.Instant;
23 import java.time.LocalDateTime;
24 import java.time.ZoneId;
25 import static java.util.Arrays.asList;
26 import java.util.List;
27 import java.util.Objects;
28 import java.util.logging.Level;
29 import javafx.application.Platform;
30 import javafx.embed.swing.JFXPanel;
31 import javafx.fxml.FXML;
32 import javafx.scene.Scene;
33 import javafx.scene.control.Alert;
34 import javafx.scene.control.ButtonBase;
35 import javafx.scene.control.ButtonType;
36 import javafx.scene.control.ChoiceBox;
37 import javafx.scene.control.ComboBox;
38 import javafx.scene.control.DialogPane;
39 import javafx.scene.control.TextField;
40 import javafx.scene.image.Image;
41 import javafx.scene.image.ImageView;
42 import javafx.util.StringConverter;
43 import javax.swing.JDialog;
44 import javax.swing.SwingUtilities;
45 import jfxtras.scene.control.LocalDateTimeTextField;
46 import org.apache.commons.lang3.StringUtils;
47 import org.controlsfx.control.action.Action;
48 import org.controlsfx.control.textfield.TextFields;
49 import org.controlsfx.tools.ValueExtractor;
50 import org.controlsfx.validation.ValidationSupport;
51 import org.controlsfx.validation.Validator;
52 import org.openide.util.NbBundle;
58 import org.sleuthkit.datamodel.Blackboard;
59 import org.sleuthkit.datamodel.BlackboardArtifact;
60 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT;
61 import org.sleuthkit.datamodel.BlackboardAttribute;
62 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
63 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION;
64 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TL_EVENT_TYPE;
65 import org.sleuthkit.datamodel.DataSource;
66 import org.sleuthkit.datamodel.SleuthkitCase;
67 import org.sleuthkit.datamodel.TskCoreException;
68 import org.sleuthkit.datamodel.TimelineEventType;
69 
74 @NbBundle.Messages({
75  "AddManualEvent.text=Add Event",
76  "AddManualEvent.longText=Manually add an event to the timeline."})
77 public class AddManualEvent extends Action {
78 
79  private final static Logger logger = Logger.getLogger(AddManualEvent.class.getName());
80  private static final String MANUAL_CREATION = "Manual Creation"; //NON-NLS
81  private static final Image ADD_EVENT_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/add.png", 16, 16, true, true, true); // NON-NLS
82 
87  static {
88  ValueExtractor.addObservableValueExtractor(LocalDateTimeTextField.class::isInstance,
89  control -> ((LocalDateTimeTextField) control).localDateTimeProperty());
90  }
91 
101  public AddManualEvent(TimeLineController controller) {
102  this(controller, null);
103  }
104 
114  public AddManualEvent(TimeLineController controller, Long epochMillis) {
115  super(Bundle.AddManualEvent_text());
116  setGraphic(new ImageView(ADD_EVENT_IMAGE));
117  setLongText(Bundle.AddManualEvent_longText());
118 
119  setEventHandler(actionEvent -> SwingUtilities.invokeLater(() -> {
120  JEventCreationDialog dialog = new JEventCreationDialog(controller, epochMillis, SwingUtilities.windowForComponent(controller.getTopComponent()));
121  dialog.setVisible(true);
122  //actual event creation happens in the ok button listener.
123  }));
124  }
125 
136  @NbBundle.Messages({
137  "AddManualEvent.createArtifactFailed=Failed to create artifact for event.",
138  "AddManualEvent.postArtifactFailed=Failed to post artifact to blackboard."})
139  private void addEvent(TimeLineController controller, ManualEventInfo eventInfo) throws IllegalArgumentException {
140  SleuthkitCase sleuthkitCase = controller.getEventsModel().getSleuthkitCase();
141 
142  try {
143  //Use the current examiners name plus a fixed string as the source / module name.
144  String source = MANUAL_CREATION + ": " + sleuthkitCase.getCurrentExaminer().getLoginName();
145 
146  BlackboardArtifact artifact = sleuthkitCase.newBlackboardArtifact(TSK_TL_EVENT, eventInfo.datasource.getId());
147  artifact.addAttributes(asList(
148  new BlackboardAttribute(
149  TSK_TL_EVENT_TYPE, source,
150  TimelineEventType.USER_CREATED.getTypeID()),
151  new BlackboardAttribute(
152  TSK_DESCRIPTION, source,
153  eventInfo.description),
154  new BlackboardAttribute(
155  TSK_DATETIME, source,
156  eventInfo.time)
157  ));
158  try {
159  sleuthkitCase.getBlackboard().postArtifact(artifact, source);
160  } catch (Blackboard.BlackboardException ex) {
161  logger.log(Level.SEVERE, "Error posting artifact to the blackboard.", ex); //NON-NLS
162  new Alert(Alert.AlertType.ERROR, Bundle.AddManualEvent_postArtifactFailed(), ButtonType.OK).showAndWait();
163  }
164  } catch (TskCoreException ex) {
165  logger.log(Level.SEVERE, "Error creatig new artifact.", ex); //NON-NLS
166  new Alert(Alert.AlertType.ERROR, Bundle.AddManualEvent_createArtifactFailed(), ButtonType.OK).showAndWait();
167  }
168  }
169 
174  private final class JEventCreationDialog extends JDialog {
175 
176  private final JFXPanel jfxPanel = new JFXPanel();
177 
178  private JEventCreationDialog(TimeLineController controller, Long epochMillis, java.awt.Window owner) {
179  super(owner, Bundle.AddManualEvent_text(), Dialog.ModalityType.DOCUMENT_MODAL);
180  setIconImages(owner.getIconImages());
181  setResizable(false);
182  add(jfxPanel);
183 
184  // make and configure the JavaFX components.
185  Platform.runLater(() -> {
186  // Custom DialogPane defined below.
187  EventCreationDialogPane customPane = new EventCreationDialogPane(controller, epochMillis);
188  //cancel button just closes the dialog
189  ((ButtonBase) customPane.lookupButton(ButtonType.CANCEL)).setOnAction(event -> dispose());
190  //configure ok button to pull ManualEventInfo object and add it to case.
191  ((ButtonBase) customPane.lookupButton(ButtonType.OK)).setOnAction(event -> {
192  ManualEventInfo manualEventInfo = customPane.getManualEventInfo();
193  if (manualEventInfo != null) {
194  addEvent(controller, manualEventInfo);
195  }
196  dispose(); //close and dispose the dialog.
197  });
198 
199  jfxPanel.setScene(new Scene(customPane));
200  customPane.installValidation();
201  SwingUtilities.invokeLater(() -> {
202  //size and position dialog on EDT
203  pack();
204  setLocationRelativeTo(owner);
205  });
206  });
207  }
208 
213  private class EventCreationDialogPane extends DialogPane {
214 
215  @FXML
216  private ChoiceBox<DataSource> dataSourceChooser;
217  @FXML
218  private TextField descriptionTextField;
219  @FXML
220  private ComboBox<String> timeZoneChooser;
221  @FXML
222  private LocalDateTimeTextField timePicker;
223 
224  private final List<String> timeZoneList = TimeZoneUtils.createTimeZoneList();
225  private final ValidationSupport validationSupport = new ValidationSupport();
227 
228  private EventCreationDialogPane(TimeLineController controller, Long epochMillis) {
229  this.controller = controller;
230  FXMLConstructor.construct(this, "EventCreationDialog.fxml"); //NON-NLS
231  if (epochMillis == null) {
232  timePicker.setLocalDateTime(LocalDateTime.now());
233  } else {
234  timePicker.setLocalDateTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), TimeLineController.getTimeZoneID()));
235  }
236  }
237 
238  @FXML
239  @NbBundle.Messages({"# {0} - datasource name", "# {1} - datasource id",
240  "AddManualEvent.EventCreationDialogPane.dataSourceStringConverter.template={0} (ID: {1})",
241  "AddManualEvent.EventCreationDialogPane.initialize.dataSourcesError=Error getting datasources in case."})
242  private void initialize() {
243  assert descriptionTextField != null : "fx:id=\"descriptionTextField\" was not injected: check your FXML file 'EventCreationDialog.fxml'.";//NON-NLS
244 
245  timeZoneChooser.getItems().setAll(timeZoneList);
246  timeZoneChooser.getSelectionModel().select(TimeZoneUtils.createTimeZoneString(TimeLineController.getTimeZone()));
247  TextFields.bindAutoCompletion(timeZoneChooser.getEditor(), timeZoneList);
248 
249  dataSourceChooser.setConverter(new StringConverter<DataSource>() {
250  @Override
251  public String toString(DataSource dataSource) {
252  return Bundle.AddManualEvent_EventCreationDialogPane_dataSourceStringConverter_template(dataSource.getName(), dataSource.getId());
253  }
254 
255  @Override
256  public DataSource fromString(String string) {
257  throw new UnsupportedOperationException(); // This method should never get called.
258  }
259  });
260  try {
261  dataSourceChooser.getItems().setAll(controller.getAutopsyCase().getSleuthkitCase().getDataSources());
262  dataSourceChooser.getSelectionModel().select(0);
263  } catch (TskCoreException ex) {
264  logger.log(Level.SEVERE, "Error getting datasources in case.", ex);//NON-NLS
265  SwingUtilities.invokeLater(() -> MessageNotifyUtil.Message.error(Bundle.AddManualEvent_EventCreationDialogPane_initialize_dataSourcesError()));
266  }
267  }
268 
272  @NbBundle.Messages({
273  "AddManualEvent.validation.description=Description is required.",
274  "AddManualEvent.validation.datetime=Invalid datetime",
275  "AddManualEvent.validation.timezone=Invalid time zone",})
276  private void installValidation() {
277  validationSupport.registerValidator(descriptionTextField, false,
278  Validator.createEmptyValidator(Bundle.AddManualEvent_validation_description()));
279  validationSupport.registerValidator(timePicker, false,
280  Validator.createPredicateValidator(Objects::nonNull, Bundle.AddManualEvent_validation_description()));
281  validationSupport.registerValidator(timeZoneChooser, false,
282  Validator.createPredicateValidator((String zone) -> timeZoneList.contains(zone.trim()), Bundle.AddManualEvent_validation_timezone()));
283 
284  validationSupport.initInitialDecoration();
285 
286  //The ok button is only enabled if all fields are validated.
287  lookupButton(ButtonType.OK).disableProperty().bind(validationSupport.invalidProperty());
288  }
289 
297  //Trim off the offset part of the string from the chooser, to get something that ZoneId can parse.
298  String zone = StringUtils.substringAfter(timeZoneChooser.getValue(), ")").trim(); //NON-NLS
299  long toEpochSecond = timePicker.getLocalDateTime().atZone(ZoneId.of(zone)).toEpochSecond();
300  return new ManualEventInfo(dataSourceChooser.getValue(), descriptionTextField.getText(), toEpochSecond);
301  }
302  }
303  }
304 
308  private static class ManualEventInfo {
309 
310  private final DataSource datasource;
311  private final String description;
312  private final long time;
313 
314  private ManualEventInfo(DataSource datasource, String description, long time) {
315  this.datasource = datasource;
316  this.description = description;
317  this.time = time;
318  }
319  }
320 }
JEventCreationDialog(TimeLineController controller, Long epochMillis, java.awt.Window owner)
AddManualEvent(TimeLineController controller, Long epochMillis)
ManualEventInfo(DataSource datasource, String description, long time)
static String createTimeZoneString(TimeZone timeZone)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void construct(Node node, String fxmlFileName)
void addEvent(TimeLineController controller, ManualEventInfo eventInfo)

Copyright © 2012-2021 Basis Technology. Generated on: Tue Jan 19 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.