Autopsy 4.22.1
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 */
19package org.sleuthkit.autopsy.timeline.actions;
20
21import java.awt.Dialog;
22import java.time.Instant;
23import java.time.LocalDateTime;
24import java.time.ZoneId;
25import java.util.Arrays;
26import java.util.List;
27import java.util.Objects;
28import java.util.logging.Level;
29import javafx.application.Platform;
30import javafx.embed.swing.JFXPanel;
31import javafx.fxml.FXML;
32import javafx.scene.Scene;
33import javafx.scene.control.Alert;
34import javafx.scene.control.ButtonBase;
35import javafx.scene.control.ButtonType;
36import javafx.scene.control.ChoiceBox;
37import javafx.scene.control.ComboBox;
38import javafx.scene.control.DialogPane;
39import javafx.scene.control.TextField;
40import javafx.scene.image.Image;
41import javafx.scene.image.ImageView;
42import javafx.util.StringConverter;
43import javax.swing.JDialog;
44import javax.swing.SwingUtilities;
45import jfxtras.scene.control.LocalDateTimeTextField;
46import org.apache.commons.lang3.StringUtils;
47import org.controlsfx.control.action.Action;
48import org.controlsfx.control.textfield.TextFields;
49import org.controlsfx.tools.ValueExtractor;
50import org.controlsfx.validation.ValidationSupport;
51import org.controlsfx.validation.Validator;
52import org.openide.util.NbBundle;
53import org.sleuthkit.autopsy.coreutils.Logger;
54import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
55import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
56import org.sleuthkit.autopsy.timeline.FXMLConstructor;
57import org.sleuthkit.autopsy.timeline.TimeLineController;
58import org.sleuthkit.datamodel.Blackboard;
59import org.sleuthkit.datamodel.BlackboardArtifact;
60import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT;
61import org.sleuthkit.datamodel.BlackboardAttribute;
62import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
63import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION;
64import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TL_EVENT_TYPE;
65import org.sleuthkit.datamodel.DataSource;
66import org.sleuthkit.datamodel.SleuthkitCase;
67import org.sleuthkit.datamodel.TskCoreException;
68import org.sleuthkit.datamodel.TimelineEventType;
69
74@NbBundle.Messages({
75 "AddManualEvent.text=Add Event",
76 "AddManualEvent.longText=Manually add an event to the timeline."})
77public 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
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 @SuppressWarnings("this-escape")
231 this.controller = controller;
232 FXMLConstructor.construct(this, "EventCreationDialog.fxml"); //NON-NLS
233 if (epochMillis == null) {
234 timePicker.setLocalDateTime(LocalDateTime.now());
235 } else {
236 timePicker.setLocalDateTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), TimeLineController.getTimeZoneID()));
237 }
238 }
239
240 @FXML
241 @NbBundle.Messages({"# {0} - datasource name", "# {1} - datasource id",
242 "AddManualEvent.EventCreationDialogPane.dataSourceStringConverter.template={0} (ID: {1})",
243 "AddManualEvent.EventCreationDialogPane.initialize.dataSourcesError=Error getting datasources in case."})
244 private void initialize() {
245 assert descriptionTextField != null : "fx:id=\"descriptionTextField\" was not injected: check your FXML file 'EventCreationDialog.fxml'.";//NON-NLS
246
247 timeZoneChooser.getItems().setAll(timeZoneList);
249 TextFields.bindAutoCompletion(timeZoneChooser.getEditor(), timeZoneList);
250
251 dataSourceChooser.setConverter(new StringConverter<DataSource>() {
252 @Override
253 public String toString(DataSource dataSource) {
254 return Bundle.AddManualEvent_EventCreationDialogPane_dataSourceStringConverter_template(dataSource.getName(), dataSource.getId());
255 }
256
257 @Override
258 public DataSource fromString(String string) {
259 throw new UnsupportedOperationException(); // This method should never get called.
260 }
261 });
262 try {
263 dataSourceChooser.getItems().setAll(controller.getAutopsyCase().getSleuthkitCase().getDataSources());
264 dataSourceChooser.getSelectionModel().select(0);
265 } catch (TskCoreException ex) {
266 logger.log(Level.SEVERE, "Error getting datasources in case.", ex);//NON-NLS
267 SwingUtilities.invokeLater(() -> MessageNotifyUtil.Message.error(Bundle.AddManualEvent_EventCreationDialogPane_initialize_dataSourcesError()));
268 }
269 }
270
274 @NbBundle.Messages({
275 "AddManualEvent.validation.description=Description is required.",
276 "AddManualEvent.validation.datetime=Invalid datetime",
277 "AddManualEvent.validation.timezone=Invalid time zone",})
278 private void installValidation() {
279 validationSupport.registerValidator(descriptionTextField, false,
280 Validator.createEmptyValidator(Bundle.AddManualEvent_validation_description()));
281 validationSupport.registerValidator(timePicker, false,
282 Validator.createPredicateValidator(Objects::nonNull, Bundle.AddManualEvent_validation_description()));
283 validationSupport.registerValidator(timeZoneChooser, false,
284 Validator.createPredicateValidator((String zone) -> timeZoneList.contains(zone.trim()), Bundle.AddManualEvent_validation_timezone()));
285
286 validationSupport.initInitialDecoration();
287
288 //The ok button is only enabled if all fields are validated.
289 lookupButton(ButtonType.OK).disableProperty().bind(validationSupport.invalidProperty());
290 }
291
299 //Trim off the offset part of the string from the chooser, to get something that ZoneId can parse.
300 String zone = StringUtils.substringAfter(timeZoneChooser.getValue(), ")").trim(); //NON-NLS
301 long toEpochSecond = timePicker.getLocalDateTime().atZone(ZoneId.of(zone)).toEpochSecond();
302 return new ManualEventInfo(dataSourceChooser.getValue(), descriptionTextField.getText(), toEpochSecond);
303 }
304 }
305 }
306
310 private static class ManualEventInfo {
311
312 private final DataSource datasource;
313 private final String description;
314 private final long time;
315
316 private ManualEventInfo(DataSource datasource, String description, long time) {
317 this.datasource = datasource;
318 this.description = description;
319 this.time = time;
320 }
321 }
322}
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static String createTimeZoneString(TimeZone timeZone)
static void construct(Node node, String fxmlFileName)
JEventCreationDialog(TimeLineController controller, Long epochMillis, java.awt.Window owner)
ManualEventInfo(DataSource datasource, String description, long time)
AddManualEvent(TimeLineController controller, Long epochMillis)
void addEvent(TimeLineController controller, ManualEventInfo eventInfo)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.