Autopsy  4.20.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 java.util.Arrays;
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  List<BlackboardAttribute> attributes = Arrays.asList(
146  new BlackboardAttribute(
147  TSK_TL_EVENT_TYPE, source,
148  TimelineEventType.USER_CREATED.getTypeID()),
149  new BlackboardAttribute(
150  TSK_DESCRIPTION, source,
151  eventInfo.description),
152  new BlackboardAttribute(
153  TSK_DATETIME, source,
154  eventInfo.time)
155  );
156 
157  BlackboardArtifact artifact = eventInfo.datasource.newDataArtifact(new BlackboardArtifact.Type(TSK_TL_EVENT), attributes, null);
158 
159  try {
160  sleuthkitCase.getBlackboard().postArtifact(artifact, source, null);
161  } catch (Blackboard.BlackboardException ex) {
162  logger.log(Level.SEVERE, "Error posting artifact to the blackboard.", ex); //NON-NLS
163  new Alert(Alert.AlertType.ERROR, Bundle.AddManualEvent_postArtifactFailed(), ButtonType.OK).showAndWait();
164  }
165  } catch (TskCoreException ex) {
166  logger.log(Level.SEVERE, "Error creatig new artifact.", ex); //NON-NLS
167  new Alert(Alert.AlertType.ERROR, Bundle.AddManualEvent_createArtifactFailed(), ButtonType.OK).showAndWait();
168  }
169  }
170 
175  private final class JEventCreationDialog extends JDialog {
176 
177  private final JFXPanel jfxPanel = new JFXPanel();
178 
179  private JEventCreationDialog(TimeLineController controller, Long epochMillis, java.awt.Window owner) {
180  super(owner, Bundle.AddManualEvent_text(), Dialog.ModalityType.DOCUMENT_MODAL);
181  setIconImages(owner.getIconImages());
182  setResizable(false);
183  add(jfxPanel);
184 
185  // make and configure the JavaFX components.
186  Platform.runLater(() -> {
187  // Custom DialogPane defined below.
188  EventCreationDialogPane customPane = new EventCreationDialogPane(controller, epochMillis);
189  //cancel button just closes the dialog
190  ((ButtonBase) customPane.lookupButton(ButtonType.CANCEL)).setOnAction(event -> dispose());
191  //configure ok button to pull ManualEventInfo object and add it to case.
192  ((ButtonBase) customPane.lookupButton(ButtonType.OK)).setOnAction(event -> {
193  ManualEventInfo manualEventInfo = customPane.getManualEventInfo();
194  if (manualEventInfo != null) {
195  addEvent(controller, manualEventInfo);
196  }
197  dispose(); //close and dispose the dialog.
198  });
199 
200  jfxPanel.setScene(new Scene(customPane));
201  customPane.installValidation();
202  SwingUtilities.invokeLater(() -> {
203  //size and position dialog on EDT
204  pack();
205  setLocationRelativeTo(owner);
206  });
207  });
208  }
209 
214  private class EventCreationDialogPane extends DialogPane {
215 
216  @FXML
217  private ChoiceBox<DataSource> dataSourceChooser;
218  @FXML
219  private TextField descriptionTextField;
220  @FXML
221  private ComboBox<String> timeZoneChooser;
222  @FXML
223  private LocalDateTimeTextField timePicker;
224 
225  private final List<String> timeZoneList = TimeZoneUtils.createTimeZoneList();
226  private final ValidationSupport validationSupport = new ValidationSupport();
228 
229  private EventCreationDialogPane(TimeLineController controller, Long epochMillis) {
230  this.controller = controller;
231  FXMLConstructor.construct(this, "EventCreationDialog.fxml"); //NON-NLS
232  if (epochMillis == null) {
233  timePicker.setLocalDateTime(LocalDateTime.now());
234  } else {
235  timePicker.setLocalDateTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), TimeLineController.getTimeZoneID()));
236  }
237  }
238 
239  @FXML
240  @NbBundle.Messages({"# {0} - datasource name", "# {1} - datasource id",
241  "AddManualEvent.EventCreationDialogPane.dataSourceStringConverter.template={0} (ID: {1})",
242  "AddManualEvent.EventCreationDialogPane.initialize.dataSourcesError=Error getting datasources in case."})
243  private void initialize() {
244  assert descriptionTextField != null : "fx:id=\"descriptionTextField\" was not injected: check your FXML file 'EventCreationDialog.fxml'.";//NON-NLS
245 
246  timeZoneChooser.getItems().setAll(timeZoneList);
247  timeZoneChooser.getSelectionModel().select(TimeZoneUtils.createTimeZoneString(TimeLineController.getTimeZone()));
248  TextFields.bindAutoCompletion(timeZoneChooser.getEditor(), timeZoneList);
249 
250  dataSourceChooser.setConverter(new StringConverter<DataSource>() {
251  @Override
252  public String toString(DataSource dataSource) {
253  return Bundle.AddManualEvent_EventCreationDialogPane_dataSourceStringConverter_template(dataSource.getName(), dataSource.getId());
254  }
255 
256  @Override
257  public DataSource fromString(String string) {
258  throw new UnsupportedOperationException(); // This method should never get called.
259  }
260  });
261  try {
262  dataSourceChooser.getItems().setAll(controller.getAutopsyCase().getSleuthkitCase().getDataSources());
263  dataSourceChooser.getSelectionModel().select(0);
264  } catch (TskCoreException ex) {
265  logger.log(Level.SEVERE, "Error getting datasources in case.", ex);//NON-NLS
266  SwingUtilities.invokeLater(() -> MessageNotifyUtil.Message.error(Bundle.AddManualEvent_EventCreationDialogPane_initialize_dataSourcesError()));
267  }
268  }
269 
273  @NbBundle.Messages({
274  "AddManualEvent.validation.description=Description is required.",
275  "AddManualEvent.validation.datetime=Invalid datetime",
276  "AddManualEvent.validation.timezone=Invalid time zone",})
277  private void installValidation() {
278  validationSupport.registerValidator(descriptionTextField, false,
279  Validator.createEmptyValidator(Bundle.AddManualEvent_validation_description()));
280  validationSupport.registerValidator(timePicker, false,
281  Validator.createPredicateValidator(Objects::nonNull, Bundle.AddManualEvent_validation_description()));
282  validationSupport.registerValidator(timeZoneChooser, false,
283  Validator.createPredicateValidator((String zone) -> timeZoneList.contains(zone.trim()), Bundle.AddManualEvent_validation_timezone()));
284 
285  validationSupport.initInitialDecoration();
286 
287  //The ok button is only enabled if all fields are validated.
288  lookupButton(ButtonType.OK).disableProperty().bind(validationSupport.invalidProperty());
289  }
290 
298  //Trim off the offset part of the string from the chooser, to get something that ZoneId can parse.
299  String zone = StringUtils.substringAfter(timeZoneChooser.getValue(), ")").trim(); //NON-NLS
300  long toEpochSecond = timePicker.getLocalDateTime().atZone(ZoneId.of(zone)).toEpochSecond();
301  return new ManualEventInfo(dataSourceChooser.getValue(), descriptionTextField.getText(), toEpochSecond);
302  }
303  }
304  }
305 
309  private static class ManualEventInfo {
310 
311  private final DataSource datasource;
312  private final String description;
313  private final long time;
314 
315  private ManualEventInfo(DataSource datasource, String description, long time) {
316  this.datasource = datasource;
317  this.description = description;
318  this.time = time;
319  }
320  }
321 }
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-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.