Autopsy 4.22.1
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 */
19package org.sleuthkit.autopsy.modules.drones;
20
21import java.io.BufferedReader;
22import java.io.File;
23import java.io.FileReader;
24import java.io.IOException;
25import java.nio.file.Paths;
26import java.time.ZoneOffset;
27import java.time.ZonedDateTime;
28import java.time.format.DateTimeFormatter;
29import java.time.format.DateTimeParseException;
30import java.util.ArrayList;
31import java.util.HashMap;
32import java.util.List;
33import java.util.Map;
34import java.util.logging.Level;
35import java.util.logging.Logger;
36import org.openide.util.NbBundle;
37import org.openide.util.NbBundle.Messages;
38import org.sleuthkit.autopsy.casemodule.services.FileManager;
39import org.sleuthkit.autopsy.coreutils.FileUtil;
40import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
41import org.sleuthkit.autopsy.ingest.IngestJobContext;
42import org.sleuthkit.datamodel.AbstractFile;
43import org.sleuthkit.datamodel.Content;
44import org.sleuthkit.datamodel.blackboardutils.GeoArtifactsHelper;
45import org.sleuthkit.datamodel.TskCoreException;
46import org.sleuthkit.datamodel.Blackboard.BlackboardException;
47import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints;
48import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints.TrackPoint;
49
57final 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-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.