diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 344a24cb79..83b05019ca 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -43,6 +43,8 @@ import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesKey; import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.ModuleContentEvent; +import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -94,6 +96,25 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { || eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { + + /** + * If a new file has been added but does not have an extension + * there is nothing to do. + */ + if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) { + if ((evt.getOldValue() instanceof ModuleContentEvent) == false) { + return; + } + ModuleContentEvent moduleContentEvent = (ModuleContentEvent) evt.getOldValue(); + if ((moduleContentEvent.getSource() instanceof AbstractFile) == false) { + return; + } + AbstractFile abstractFile = (AbstractFile) moduleContentEvent.getSource(); + if (abstractFile.getNameExtension().isEmpty()) { + return; + } + } + /** * Checking for a current case is a stop gap measure until a * different way of handling the closing of cases is worked @@ -415,7 +436,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { @Override public void update(Observable o, Object arg) { - refresh(true); + refresh(false); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java index 659ee84870..6fa6487b5e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Collections; +import java.util.Comparator; import java.util.EnumSet; import java.util.List; import java.util.Observable; @@ -252,7 +253,12 @@ public class Tags implements AutopsyVisitableItem { ? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(filteringDSObjId) : Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(); } - Collections.sort(tagNamesInUse); + Collections.sort(tagNamesInUse, new Comparator() { + @Override + public int compare(TagName o1, TagName o2) { + return TagUtils.getDecoratedTagDisplayName(o1).compareTo(TagUtils.getDecoratedTagDisplayName(o2)); + } + }); keys.addAll(tagNamesInUse); } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(TagNameNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/AbstractWaypointFetcher.java b/Core/src/org/sleuthkit/autopsy/geolocation/AbstractWaypointFetcher.java index 2c33d054cb..fccdfacd29 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/AbstractWaypointFetcher.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/AbstractWaypointFetcher.java @@ -26,6 +26,7 @@ import javafx.util.Pair; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; +import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationParseResult; import org.sleuthkit.autopsy.geolocation.datamodel.Track; import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint; import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder; @@ -70,25 +71,33 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter } + /** * Called after all of the MapWaypoints are created from all of the * TSK_GPS_XXX objects. - * + * * @param mapWaypoints List of filtered MapWaypoints. + * @param tracks The tracks that were successfully parsed. + * @param wasEntirelySuccessful True if no errors occurred while processing. */ - abstract void handleFilteredWaypointSet(Set mapWaypoints, List> tracks); + abstract void handleFilteredWaypointSet(Set mapWaypoints, List> tracks, + boolean wasEntirelySuccessful); @Override - public void process(List waypoints) { - List tracks = new ArrayList<>(); + public void process(GeoLocationParseResult waypointResults) { + GeoLocationParseResult trackResults = null; if (filters.getArtifactTypes().contains(ARTIFACT_TYPE.TSK_GPS_TRACK)) { try { - tracks = Track.getTracks(Case.getCurrentCase().getSleuthkitCase(), filters.getDataSources()); + trackResults = Track.getTracks(Case.getCurrentCase().getSleuthkitCase(), filters.getDataSources()); } catch (GeoLocationDataException ex) { logger.log(Level.WARNING, "Exception thrown while retrieving list of Tracks", ex); } } - Pair, List>> waypointsAndTracks = createWaypointList(waypoints, tracks); + + Pair, List>> waypointsAndTracks = createWaypointList( + waypointResults.getItems(), + (trackResults == null) ? new ArrayList() : trackResults.getItems()); + final Set pointSet = MapWaypoint.getWaypoints(waypointsAndTracks.getKey()); final List> trackSets = new ArrayList<>(); @@ -96,7 +105,9 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter trackSets.add(MapWaypoint.getWaypoints(t)); } - handleFilteredWaypointSet(pointSet, trackSets); + handleFilteredWaypointSet( + pointSet, trackSets, + (trackResults == null || trackResults.isSuccessfullyParsed()) && waypointResults.isSuccessfullyParsed()); } /** diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED index f3dab8dd7e..d16fa37761 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED @@ -24,6 +24,8 @@ GeolocationTC_connection_failure_message_title=Connection Failure GeolocationTC_empty_waypoint_message=Unable to generate KML report due to a lack of waypoints.\nPlease make sure there are waypoints visible before generating the KML report GeolocationTC_KML_report_title=KML Report GeolocationTC_report_progress_title=KML Report Progress +GeolocationTopComponent.WaypointFetcher.onErrorDescription=There was an error gathering some GPS Track Data. Some results have been excluded. +GeolocationTopComponent.WaypointFetcher.onErrorTitle=Error gathering GPS Track Data GeoTopComponent_filer_data_invalid_msg=Unable to run waypoint filter.\nPlease select one or more data sources. GeoTopComponent_filer_data_invalid_Title=Filter Failure GeoTopComponent_filter_exception_msg=Exception occurred during waypoint filtering. diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java index ae387c7847..31ef953fdc 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java @@ -494,6 +494,10 @@ public final class GeolocationTopComponent extends TopComponent { * Extends AbstractWaypointFetcher to handle the returning of * the filters set of MapWaypoints. */ + @Messages({ + "GeolocationTopComponent.WaypointFetcher.onErrorTitle=Error gathering GPS Track Data", + "GeolocationTopComponent.WaypointFetcher.onErrorDescription=There was an error gathering some GPS Track Data. Some results have been excluded." + }) final private class WaypointFetcher extends AbstractWaypointFetcher { WaypointFetcher(GeoFilter filters) { @@ -501,8 +505,16 @@ public final class GeolocationTopComponent extends TopComponent { } @Override - void handleFilteredWaypointSet(Set mapWaypoints, List> tracks) { + void handleFilteredWaypointSet(Set mapWaypoints, List> tracks, boolean wasEntirelySuccessful) { addWaypointsToMap(mapWaypoints, tracks); + + // if there is an error, present to the user. + if (!wasEntirelySuccessful) { + JOptionPane.showMessageDialog(GeolocationTopComponent.this, + Bundle.GeolocationTopComponent_WaypointFetcher_onErrorDescription(), + Bundle.GeolocationTopComponent_WaypointFetcher_onErrorTitle(), + JOptionPane.ERROR_MESSAGE); + } } } } diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/GeoLocationParseResult.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/GeoLocationParseResult.java new file mode 100644 index 0000000000..2089bc2cb6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/GeoLocationParseResult.java @@ -0,0 +1,84 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.geolocation.datamodel; + +import java.util.ArrayList; +import java.util.List; +import org.python.google.common.collect.ImmutableList; + +/** + * The result of attempting to parse GeoLocation objects. + */ +public class GeoLocationParseResult { + + private boolean successfullyParsed; + private final List items = new ArrayList<>(); + + /** + * Returns a GeoLocationParseResult with no items and declared successfully + * parsed. + */ + public GeoLocationParseResult() { + successfullyParsed = true; + } + + /** + * Returns a new GeoLocationParseResult. + * + * @param items The items to copy to this result (can be null). + * @param successfullyParsed Whether or not the operation was entirely + * successful. + */ + public GeoLocationParseResult(List items, boolean successfullyParsed) { + this.successfullyParsed = successfullyParsed; + + if (items != null) { + this.items.addAll(items); + } + } + + /** + * Adds the content of the GeoLocationParseResult parameter to this. Items + * will be concatenated and this object's successfullyParsed status will be + * true if it is already true and the object is true as well. + * + * @param toAdd The GeoLocationParseResult to add. + */ + public void add(GeoLocationParseResult toAdd) { + this.successfullyParsed = this.successfullyParsed && toAdd.isSuccessfullyParsed(); + this.items.addAll(toAdd.getItems()); + } + + /** + * Whether or not the GeoLocation object has been successfully parsed. + * + * @return Whether or not the GeoLocation object has been successfully + * parsed. + */ + public boolean isSuccessfullyParsed() { + return successfullyParsed; + } + + /** + * @return The successfully parsed GeoLocation objects. + */ + public List getItems() { + return ImmutableList.copyOf(items); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/GeoPath.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/GeoPath.java index c61dea93be..a1d369808a 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/GeoPath.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/GeoPath.java @@ -31,7 +31,6 @@ import org.sleuthkit.datamodel.TskCoreException; * Class representing a series of waypoints that form a path. */ public class GeoPath { - private final List waypointList; private final String pathName; private final BlackboardArtifact artifact; @@ -74,21 +73,26 @@ public class GeoPath { * * @throws GeoLocationDataException */ - static public List getTracks(SleuthkitCase skCase, List sourceList) throws GeoLocationDataException { + public static GeoLocationParseResult getTracks(SleuthkitCase skCase, List sourceList) throws GeoLocationDataException { List artifacts = null; + boolean allParsedSuccessfully = true; List tracks = new ArrayList<>(); try { artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK); for (BlackboardArtifact artifact : artifacts) { if (sourceList == null || sourceList.contains(artifact.getDataSource())) { - Track route = new Track(artifact); - tracks.add(route); + try { + tracks.add(new Track(artifact)); + + } catch (GeoLocationDataException e) { + allParsedSuccessfully = false; + } } } } catch (TskCoreException ex) { throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_BOOKMARK", ex); } - return tracks; + return new GeoLocationParseResult(tracks, allParsedSuccessfully); } /** diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Track.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Track.java index 68da242b8a..af19623100 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Track.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Track.java @@ -22,18 +22,21 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.logging.Level; import org.openide.util.NbBundle.Messages; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil; import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil.InvalidJsonException; import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints; +import org.sleuthkit.autopsy.coreutils.Logger; /** * A GPS track with which wraps the TSK_GPS_TRACK artifact. */ public final class Track extends GeoPath { - + private static final Logger LOGGER = Logger.getLogger(Track.class.getName()); + private final Long startTimestamp; private final Long endTimeStamp; @@ -130,14 +133,17 @@ public final class Track extends GeoPath { */ private GeoTrackPoints getPointsList(Map attributeMap) throws GeoLocationDataException { BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS); - if (attribute != null) { - try { - return BlackboardJsonAttrUtil.fromAttribute(attribute, GeoTrackPoints.class); - } catch (InvalidJsonException ex) { - throw new GeoLocationDataException("Unable to parse track points in TSK_GEO_TRACKPOINTS attribute", ex); - } + if (attribute == null) { + LOGGER.log(Level.SEVERE, "No TSK_GEO_TRACKPOINTS attribute was present on the artifact."); + throw new GeoLocationDataException("No TSK_GEO_TRACKPOINTS attribute present in attribute map to parse."); + } + + try { + return BlackboardJsonAttrUtil.fromAttribute(attribute, GeoTrackPoints.class); + } catch (InvalidJsonException ex) { + LOGGER.log(Level.SEVERE, "TSK_GEO_TRACKPOINTS could not be properly parsed from TSK_GEO_TRACKPOINTS attribute."); + throw new GeoLocationDataException("Unable to parse track points in TSK_GEO_TRACKPOINTS attribute", ex); } - return null; } /** diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java index cf414a2fdc..076ac8b8f9 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java @@ -22,6 +22,7 @@ package org.sleuthkit.autopsy.geolocation.datamodel; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; @@ -40,15 +41,15 @@ import org.sleuthkit.datamodel.DataSource; public final class WaypointBuilder { private static final Logger logger = Logger.getLogger(WaypointBuilder.class.getName()); - - private final static String TIME_TYPE_IDS = String.format("%d, %d", - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), + + private final static String TIME_TYPE_IDS = String.format("%d, %d", + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID()); - - private final static String GEO_ATTRIBUTE_TYPE_IDS = String.format("%d, %d, %d", - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID(), - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS.getTypeID()); + + private final static String GEO_ATTRIBUTE_TYPE_IDS = String.format("%d, %d, %d", + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS.getTypeID()); // SELECT statement for getting a list of waypoints where %s is a comma separated list // of attribute type ids. @@ -96,9 +97,10 @@ public final class WaypointBuilder { /** * This function will be called after the waypoints have been filtered. * - * @param waypoints The list of waypoints. + * @param waypoints The list of waypoints and whether they were all + * successfully parsed. */ - void process(List waypoints); + void process(GeoLocationParseResult waypoints); } /** @@ -478,7 +480,7 @@ public final class WaypointBuilder { skCase.getCaseDbAccessManager().select(query, new CaseDbAccessManager.CaseDbAccessQueryCallback() { @Override public void process(ResultSet rs) { - List waypoints = new ArrayList<>(); + GeoLocationParseResult waypointResults = new GeoLocationParseResult<>(); try { while (rs.next()) { int artifact_type_id = rs.getInt("artifact_type_id"); //NON-NLS @@ -486,12 +488,13 @@ public final class WaypointBuilder { ARTIFACT_TYPE type = ARTIFACT_TYPE.fromID(artifact_type_id); if (artifactTypes.contains(type)) { - waypoints.addAll(getWaypointForArtifact(skCase.getBlackboardArtifact(artifact_id), type)); + waypointResults.add(getWaypointForArtifact(skCase.getBlackboardArtifact(artifact_id), type)); } } - queryCallBack.process(waypoints); - } catch (GeoLocationDataException | SQLException | TskCoreException ex) { + + queryCallBack.process(waypointResults); + } catch (SQLException | TskCoreException ex) { logger.log(Level.WARNING, "Failed to filter waypoint.", ex); //NON-NLS } @@ -522,7 +525,7 @@ public final class WaypointBuilder { // FROM blackboard_attributes // WHERE attribute_type_id IN (%d, %d) return String.format(SELECT_WO_TIMESTAMP, - String.format(GEO_ARTIFACT_QUERY_ID_ONLY,TIME_TYPE_IDS), + String.format(GEO_ARTIFACT_QUERY_ID_ONLY, TIME_TYPE_IDS), getWaypointListQuery(dataSources)); } @@ -619,6 +622,51 @@ public final class WaypointBuilder { dataSourceList); } + /** + * A parser that could throw a GeoLocationDataException when there is a + * parse issue. + * + * @param The return type. + */ + private interface ParserWithError { + + T parse(BlackboardArtifact artifact) throws GeoLocationDataException; + } + + /** + * Parses one waypoint. + * + * @param parser The parser to use. + * @param artifact The artifact to be parsed. + * + * @return Returns a parse result that is either successful with a parsed + * waypoint or unsuccessful with an exception. + */ + private static GeoLocationParseResult parseWaypoint(ParserWithError parser, BlackboardArtifact artifact) { + try { + return new GeoLocationParseResult<>(Arrays.asList(parser.parse(artifact)), true); + } catch (GeoLocationDataException ex) { + return new GeoLocationParseResult<>(null, false); + } + } + + /** + * Parses a list of waypoints. + * + * @param parser The parser to use. + * @param artifact The artifact to be parsed. + * + * @return Returns a parse result that is either successful with a parsed + * waypoint or unsuccessful with an exception. + */ + private static GeoLocationParseResult parseWaypoints(ParserWithError> parser, BlackboardArtifact artifact) { + try { + return new GeoLocationParseResult<>(parser.parse(artifact), true); + } catch (GeoLocationDataException ignored) { + return new GeoLocationParseResult<>(null, false); + } + } + /** * Create a Waypoint object for the given Blackboard artifact. * @@ -626,37 +674,33 @@ public final class WaypointBuilder { * @param type The type of artifact * * @return A new waypoint object - * - * @throws GeoLocationDataException */ - static private List getWaypointForArtifact(BlackboardArtifact artifact, ARTIFACT_TYPE type) throws GeoLocationDataException { - List waypoints = new ArrayList<>(); + private static GeoLocationParseResult getWaypointForArtifact(BlackboardArtifact artifact, ARTIFACT_TYPE type) { + GeoLocationParseResult waypoints = new GeoLocationParseResult<>(); switch (type) { case TSK_METADATA_EXIF: - waypoints.add(new EXIFWaypoint(artifact)); + waypoints.add(parseWaypoint(EXIFWaypoint::new, artifact)); break; case TSK_GPS_BOOKMARK: - waypoints.add(new BookmarkWaypoint(artifact)); + waypoints.add(parseWaypoint(BookmarkWaypoint::new, artifact)); break; case TSK_GPS_TRACKPOINT: - waypoints.add(new TrackpointWaypoint(artifact)); + waypoints.add(parseWaypoint(TrackpointWaypoint::new, artifact)); break; case TSK_GPS_SEARCH: - waypoints.add(new SearchWaypoint(artifact)); + waypoints.add(parseWaypoint(SearchWaypoint::new, artifact)); break; case TSK_GPS_ROUTE: - Route route = new Route(artifact); - waypoints.addAll(route.getRoute()); + waypoints.add(parseWaypoints((a) -> new Route(a).getRoute(), artifact)); break; case TSK_GPS_LAST_KNOWN_LOCATION: - waypoints.add(new LastKnownWaypoint(artifact)); + waypoints.add(parseWaypoint(LastKnownWaypoint::new, artifact)); break; case TSK_GPS_TRACK: - Track track = new Track(artifact); - waypoints.addAll(track.getPath()); + waypoints.add(parseWaypoints((a) -> new Track(a).getPath(), artifact)); break; default: - waypoints.add(new CustomArtifactWaypoint(artifact)); + waypoints.add(parseWaypoint(CustomArtifactWaypoint::new, artifact)); } return waypoints; diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/kml/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/report/modules/kml/Bundle.properties-MERGED index b97907cce9..c1fe7e6ac1 100755 --- a/Core/src/org/sleuthkit/autopsy/report/modules/kml/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/report/modules/kml/Bundle.properties-MERGED @@ -2,6 +2,7 @@ KMLReport.bookmarkError=Could not extract Bookmark information. # {0} - filePath KMLReport.errorGeneratingReport=Error adding {0} to case as a report. KMLReport.exifPhotoError=Could not extract photos with EXIF metadata. +KMLReport.failedToCompleteReport=Failed to complete report. KMLReport.gpsBookmarkError=Could not get GPS Bookmarks from database. KMLReport.gpsRouteDatabaseError=Could not get GPS Routes from database. KMLReport.gpsRouteError=Could not extract GPS Route information. @@ -9,6 +10,7 @@ KMLReport.gpsSearchDatabaseError=Could not get GPS Searches from database. KMLReport.kmlFileWriteError=Could not write the KML file. KMLReport.locationDatabaseError=Could not get GPS Last Known Location from database. KMLReport.locationError=Could not extract Last Known Location information. +KMLReport.partialFailure=There was an error creating the report. Some items were not exported. KMLReport.stylesheetError=Error placing KML stylesheet. The .KML file will not function properly. KMLReport.trackpointDatabaseError=Could not get GPS Trackpoints from database. KMLReport.trackpointError=Could not extract Trackpoint information. diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java b/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java index 3a038adea5..607b8879fe 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java @@ -46,6 +46,7 @@ import org.openide.filesystems.FileUtil; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; +import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationParseResult; import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint; import org.sleuthkit.autopsy.geolocation.datamodel.Route; import org.sleuthkit.autopsy.geolocation.datamodel.Track; @@ -126,6 +127,8 @@ public final class KMLReport implements GeneralReportModule { * @param waypointList */ @Messages({ + "KMLReport.failedToCompleteReport=Failed to complete report.", + "KMLReport.partialFailure=There was an error creating the report. Some items were not exported.", "KMLReport.unableToExtractPhotos=Could not extract photo information.", "KMLReport.exifPhotoError=Could not extract photos with EXIF metadata.", "KMLReport.bookmarkError=Could not extract Bookmark information.", @@ -208,10 +211,15 @@ public final class KMLReport implements GeneralReportModule { try { makeRoutes(skCase); - makeTracks(skCase); + boolean entirelySuccessful = makeTracks(skCase); + if (!entirelySuccessful) { + result = ReportProgressPanel.ReportStatus.ERROR; + errorMessage = Bundle.KMLReport_partialFailure(); + } + addLocationsToReport(skCase, baseReportDir); } catch (GeoLocationDataException | IOException | TskCoreException ex) { - errorMessage = "Failed to complete report."; + errorMessage = Bundle.KMLReport_failedToCompleteReport(); logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS result = ReportProgressPanel.ReportStatus.ERROR; } @@ -515,14 +523,18 @@ public final class KMLReport implements GeneralReportModule { * Add the track to the track folder in the document. * * @param skCase Currently open case. - * + * @return The operation was entirely successful. + * * @throws TskCoreException */ - void makeTracks(SleuthkitCase skCase) throws GeoLocationDataException, TskCoreException { + boolean makeTracks(SleuthkitCase skCase) throws GeoLocationDataException, TskCoreException { List tracks = null; - + boolean successful = true; + if (waypointList == null) { - tracks = Track.getTracks(skCase, null); + GeoLocationParseResult result = Track.getTracks(skCase, null); + tracks = result.getItems(); + successful = result.isSuccessfullyParsed(); } else { tracks = WaypointBuilder.getTracks(waypointList); } @@ -533,6 +545,8 @@ public final class KMLReport implements GeneralReportModule { } addTrackToReport(track); } + + return successful; } /** diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java index 6eaddeef9d..0af5965b16 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/CategorizeAction.java @@ -82,6 +82,12 @@ public class CategorizeAction extends Action { this.tagName = tagName; setGraphic(getGraphic(tagName)); setEventHandler(actionEvent -> addCatToFiles(selectedFileIDs)); + + int rank = tagName.getRank(); + // Only map to a key if the rank is less than 10 + if(rank < 10) { + setAccelerator(new KeyCodeCombination(KeyCode.getKeyCode(Integer.toString(rank)))); + } } static public Menu getCategoriesMenu(ImageGalleryController controller) { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/GuiUtils.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/GuiUtils.java index 4699046655..7e528c4186 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/GuiUtils.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/GuiUtils.java @@ -21,12 +21,19 @@ package org.sleuthkit.autopsy.imagegallery.gui; import java.io.IOException; import java.net.URL; import java.util.logging.Level; +import javafx.geometry.HPos; +import javafx.geometry.Pos; import javafx.scene.control.ButtonBase; import javafx.scene.control.CustomMenuItem; import javafx.scene.control.Dialog; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; import javafx.scene.image.Image; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.ColumnConstraints; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; import javafx.stage.Stage; import org.controlsfx.control.action.Action; import org.controlsfx.control.action.ActionUtils; @@ -67,7 +74,31 @@ public final class GuiUtils { * @return */ public static MenuItem createAutoAssigningMenuItem(ButtonBase button, Action action) { - MenuItem menuItem = new CustomMenuItem(new Label(action.getText(), action.getGraphic())); + Label mainLabel = new Label(action.getText(), action.getGraphic()); + + String hkDisplayText = action.getAccelerator() != null ? action.getAccelerator().getDisplayText() : ""; + Label hotKeyLabel = new Label(hkDisplayText); + + hotKeyLabel.setMaxWidth(100); + + GridPane grid = new GridPane(); + grid.setAlignment(Pos.CENTER); + grid.setHgap(10); + + ColumnConstraints column1 = new ColumnConstraints(); + column1.setHalignment(HPos.LEFT); + grid.getColumnConstraints().add(column1); + + ColumnConstraints column2 = new ColumnConstraints(); + column2.setHalignment(HPos.RIGHT); + column2.setMaxWidth(Double.MAX_VALUE); + grid.getColumnConstraints().add(column2); + + grid.add(mainLabel, 0, 0); + grid.add(hotKeyLabel, 1, 0); + grid.setMaxWidth(Double.MAX_VALUE); + + MenuItem menuItem = new CustomMenuItem(grid); ActionUtils.configureMenuItem(action, menuItem); menuItem.setOnAction(actionEvent -> { action.handle(actionEvent); diff --git a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py index 0e4face2bc..3ee603a243 100644 --- a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py +++ b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py @@ -183,7 +183,8 @@ class GPXParserFileIngestModule(FileIngestModule): point.latitude, point.longitude, elevation, None, 0, 0, 0, timeStamp)) try: - geoArtifactHelper.addTrack("Track", geoPointList, None) + if not geoPointList.isEmpty(): + geoArtifactHelper.addTrack("Track", geoPointList, None) except Blackboard.BlackboardException as e: self.log(Level.SEVERE, "Error posting GPS track artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) @@ -236,7 +237,8 @@ class GPXParserFileIngestModule(FileIngestModule): Waypoint(point.latitude, point.longitude, point.elevation, point.name)) try: - geoArtifactHelper.addRoute(None, None, geoWaypoints, None) + if not geoWaypoints.isEmpty(): + geoArtifactHelper.addRoute(None, None, geoWaypoints, None) except Blackboard.BlackboardException as e: self.log("Error posting GPS route artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())