Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
DATExtractor.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2020-2021 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.modules.drones;
20 
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.FileReader;
24 import java.io.IOException;
25 import java.nio.file.Paths;
26 import java.time.ZoneOffset;
27 import java.time.ZonedDateTime;
28 import java.time.format.DateTimeFormatter;
29 import java.time.format.DateTimeParseException;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.logging.Level;
35 import java.util.logging.Logger;
36 import org.openide.util.NbBundle;
37 import org.openide.util.NbBundle.Messages;
42 import org.sleuthkit.datamodel.AbstractFile;
43 import org.sleuthkit.datamodel.Content;
44 import org.sleuthkit.datamodel.blackboardutils.GeoArtifactsHelper;
45 import org.sleuthkit.datamodel.TskCoreException;
46 import org.sleuthkit.datamodel.Blackboard.BlackboardException;
47 import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints;
48 import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints.TrackPoint;
49 
57 final class DATExtractor extends DroneExtractor {
58 
59  private static final Logger logger = Logger.getLogger(DATExtractor.class.getName());
60 
61  private static final String HEADER_LONG = "IMU_ATTI(0):Longitude"; //NON-NLS
62  private static final String HEADER_LAT = "IMU_ATTI(0):Latitude"; //NON-NLS
63  private static final String HEADER_VELOCITY = "IMU_ATTI(0):velComposite"; //NON-NLS
64  private static final String HEADER_DATETILE = "GPS:dateTimeStamp"; //NON-NLS
65  private static final String HEADER_ALTITUDE = "GPS(0):heightMSL"; //NON-NLS
66  private static final String HEADER_DISTANCE_FROM_HP = "IMU_ATTI(0):distanceHP"; //NON-NLS
67  private static final String HEADER_DISTANCE_TRAVELED = "IMU_ATTI(0):distanceTravelled"; //NON-NLS
68 
74  DATExtractor() throws DroneIngestException {
75  super();
76  }
77 
78  @Messages({
79  "DATExtractor_process_message=Processing DJI DAT file: %s"
80  })
81  @Override
82  void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) throws DroneIngestException {
83  List<AbstractFile> datFiles = findDATFiles(dataSource);
84 
85  DATDumper dumper = new DATDumper();
86 
87  try {
88  for (AbstractFile DATFile : datFiles) {
89  if (context.dataSourceIngestIsCancelled()) {
90  break;
91  }
92 
93  progressBar.progress(String.format(Bundle.DATExtractor_process_message(), DATFile.getName()));
94 
95  // Copy the DAT file into the case temp folder
96  File tempDATFile = getTemporaryFile(context, DATFile);
97 
98  // Create a path for the csv file
99  String csvFilePath = getCSVPathForDAT(DATFile);
100 
101  try {
102  if (!dumper.isDATFile(tempDATFile.getAbsolutePath())) {
103  logger.log(Level.WARNING, String.format("%s is not a valid DAT file", DATFile.getName())); //NON-NLS
104  continue;
105  }
106  // Dump the DAT file to a csv file
107  dumper.dumpDATFile(tempDATFile.getAbsolutePath(), csvFilePath, true);
108 
109  if (context.dataSourceIngestIsCancelled()) {
110  break;
111  }
112 
113  // Process the csv file
114  GeoTrackPoints trackPoints = processCSVFile(context, DATFile, csvFilePath);
115 
116  if (trackPoints != null && !trackPoints.isEmpty()) {
117  (new GeoArtifactsHelper(getSleuthkitCase(), getName(), "DatCon", DATFile, context.getJobId())).addTrack(DATFile.getName(), trackPoints, null);
118  } else {
119  logger.log(Level.INFO, String.format("No trackpoints with valid longitude or latitude found in %s", DATFile.getName())); //NON-NLS
120  }
121 
122  } catch (TskCoreException | BlackboardException ex) {
123  logger.log(Level.WARNING, String.format("Exception thrown while processing DAT file %s", DATFile.getName()), ex); //NON-NLS
124  } finally {
125  tempDATFile.delete();
126  (new File(csvFilePath)).delete();
127  }
128  }
129  } finally {
130  FileUtil.deleteDir(getExtractorTempPath().toFile());
131  }
132  }
133 
134  @NbBundle.Messages({
135  "DATFileExtractor_Extractor_Name=DAT File Extractor"
136  })
137 
138  @Override
139  String getName() {
140  return Bundle.DATFileExtractor_Extractor_Name();
141  }
142 
153  private List<AbstractFile> findDATFiles(Content dataSource) throws DroneIngestException {
154  List<AbstractFile> fileList = new ArrayList<>();
155 
156  FileManager fileManager = getCurrentCase().getServices().getFileManager();
157 
158  // findFiles use the SQL wildcard # in the file name
159  try {
160  fileList = fileManager.findFiles(dataSource, "FLY___.DAT"); //NON-NLS
161  } catch (TskCoreException ex) {
162  throw new DroneIngestException("Unable to find drone DAT files.", ex); //NON-NLS
163  }
164 
165  return fileList;
166  }
167 
175  private String getCSVPathForDAT(AbstractFile file) {
176  String tempFileName = file.getName() + file.getId() + ".csv"; //NON-NLS
177  return Paths.get(getExtractorTempPath().toString(), tempFileName).toString();
178  }
179 
191  private GeoTrackPoints processCSVFile(IngestJobContext context, AbstractFile DATFile, String csvFilePath) throws DroneIngestException {
192  GeoTrackPoints trackPoints = new GeoTrackPoints();
193  try (BufferedReader reader = new BufferedReader(new FileReader(new File(csvFilePath)))) {
194  // First read in the header line and process
195  String line = reader.readLine();
196  Map<String, Integer> headerMap = makeHeaderMap(line.split(",")); //NON-NLS
197 
198  while ((line = reader.readLine()) != null) {
199  if (context.dataSourceIngestIsCancelled()) {
200  return null;
201  }
202 
203  String[] values = line.split(","); //NON-NLS
204  TrackPoint point = createTrackPoint(headerMap, values);
205  if (point != null) {
206  trackPoints.addPoint(point);
207  }
208  }
209 
210  } catch (IOException ex) {
211  throw new DroneIngestException(String.format("Failed to read DAT csvFile %s created for AbstractFile: %s", csvFilePath, DATFile.getId()), ex); //NON-NLS
212  }
213 
214  return trackPoints;
215  }
216 
224  private Map<String, Integer> makeHeaderMap(String[] headers) {
225  Map<String, Integer> map = new HashMap<>();
226 
227  for (int index = 0; index < headers.length; index++) {
228  map.put(headers[index], index);
229 
230  }
231 
232  return map;
233  }
234 
249  private TrackPoint createTrackPoint(Map<String, Integer> columnLookup, String[] values) throws DroneIngestException {
250 
251  Double latitude = getDoubleValue(columnLookup.get(HEADER_LAT), values);
252  Double longitude = getDoubleValue(columnLookup.get(HEADER_LONG), values);
253 
254  if (longitude == null || latitude == null) {
255  // Assume the row is not valid\has junk
256  return null;
257  }
258 
259  return new TrackPoint(latitude,
260  longitude,
261  getDoubleValue(columnLookup.get(HEADER_ALTITUDE), values),
262  null,
263  getDoubleValue(columnLookup.get(HEADER_VELOCITY), values),
264  getDoubleValue(columnLookup.get(HEADER_DISTANCE_FROM_HP), values),
265  getDoubleValue(columnLookup.get(HEADER_DISTANCE_TRAVELED), values),
266  getDateTimeValue(columnLookup, values));
267  }
268 
279  private Long getDateTimeValue(Map<String, Integer> headerMap, String[] values) {
280  Integer index = headerMap.get(HEADER_DATETILE);
281  if (index == null || index == -1 || index > values.length) {
282  return null;
283  }
284 
285  String value = values[index];
286  if (value == null || value.isEmpty()) {
287  return null;
288  }
289 
290  try {
291  ZonedDateTime zdt = ZonedDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME);
292  return zdt.toLocalDateTime().toEpochSecond(ZoneOffset.UTC);
293  } catch (DateTimeParseException ex) {
294  return null;
295  }
296  }
297 
307  private Double getDoubleValue(Integer index, String[] values) {
308  if (index == null || index == -1 || index > values.length) {
309  return null;
310  }
311 
312  String value = values[index];
313  if (value == null || value.isEmpty()) {
314  return null;
315  }
316 
317  try {
318  return Double.parseDouble(value);
319  } catch (NumberFormatException ex) {
320  return null;
321  }
322  }
323 
324 }

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.