diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java index 33b9d16724..985351ce10 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java @@ -70,7 +70,7 @@ final class ContactNode extends BlackboardArtifactNode { @Override protected Sheet createSheet() { - Sheet sheet = super.createSheet(); + Sheet sheet = new Sheet(); final BlackboardArtifact artifact = getArtifact(); BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); @@ -116,10 +116,6 @@ final class ContactNode extends BlackboardArtifactNode { sheetSet.put(new NodeProperty<>(bba.getAttributeType().getTypeName(), bba.getAttributeType().getDisplayName(), "", bba.getDisplayString())); } - // Don't need these values to appear in the Contact property sheet. - sheetSet.remove("S"); - sheetSet.remove("C"); - List children = artifact.getChildren(); if(children != null) { int count = 0; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java index dddf0f6296..1b0d048f35 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java @@ -117,7 +117,7 @@ public class FileViewer extends javax.swing.JPanel implements DataContentViewer } AbstractFile file = selectedNode.getLookup().lookup(AbstractFile.class); - if (file == null) { + if ((file == null) || (file.isDir())) { return; } @@ -189,7 +189,7 @@ public class FileViewer extends javax.swing.JPanel implements DataContentViewer } AbstractFile aFile = node.getLookup().lookup(AbstractFile.class); - if (aFile == null) { + if ((aFile == null) || (aFile.isDir())) { return false; } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 71f801282b..c79c8e613c 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -892,6 +892,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer { "DataResultViewerTable.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0}", "DataResultViewerTable.goToPageTextField.err=Invalid page number"}) void gotoPage() { + int originalPage = currentPage; + try { currentPage = Integer.decode(gotoPageTextField.getText()); } catch (NumberFormatException e) { @@ -900,7 +902,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { } if (currentPage > totalPages || currentPage < 1) { - currentPage = 1; + currentPage = originalPage; JOptionPane.showMessageDialog(DataResultViewerTable.this, Bundle.DataResultViewerTable_goToPageTextField_msgDlg(totalPages), Bundle.DataResultViewerTable_goToPageTextField_err(), diff --git a/Core/src/org/sleuthkit/autopsy/report/ArtifactSelectionDialog.java b/Core/src/org/sleuthkit/autopsy/report/ArtifactSelectionDialog.java index b0655dfa2b..4ab05a11aa 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ArtifactSelectionDialog.java +++ b/Core/src/org/sleuthkit/autopsy/report/ArtifactSelectionDialog.java @@ -54,7 +54,7 @@ public class ArtifactSelectionDialog extends javax.swing.JDialog { /** * Creates new form ArtifactSelectionDialog - * + * * @param parent The parent window * @param modal Block user-input to other top-level windows. */ @@ -110,9 +110,11 @@ public class ArtifactSelectionDialog extends javax.swing.JDialog { @Override public void mousePressed(MouseEvent evt) { int index = artifactList.locationToIndex(evt.getPoint()); - BlackboardArtifact.Type type = model.getElementAt(index); - artifactTypeSelections.put(type, !artifactTypeSelections.get(type)); - artifactList.repaint(); + if (index >= 0) { + BlackboardArtifact.Type type = model.getElementAt(index); + artifactTypeSelections.put(type, !artifactTypeSelections.get(type)); + artifactList.repaint(); + } } }); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java index 44975ade85..6a9253043a 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FilteredEventsModel.java @@ -35,9 +35,7 @@ import javafx.beans.InvalidationListener; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.collections.FXCollections; -import javafx.collections.ObservableList; import javafx.collections.ObservableMap; -import javafx.collections.ObservableSet; import static org.apache.commons.collections4.CollectionUtils.emptyIfNull; import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; import org.joda.time.DateTimeZone; @@ -58,7 +56,6 @@ import org.sleuthkit.autopsy.timeline.events.TagsAddedEvent; import org.sleuthkit.autopsy.timeline.events.TagsDeletedEvent; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState; import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.RootFilterState; -import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.TagsFilterState; import org.sleuthkit.autopsy.timeline.utils.CacheLoaderImpl; import org.sleuthkit.autopsy.timeline.utils.FilterUtils; import org.sleuthkit.autopsy.timeline.zooming.ZoomState; @@ -70,7 +67,6 @@ import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.Tag; -import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TimelineManager; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TimelineEvent; @@ -81,10 +77,8 @@ import org.sleuthkit.datamodel.TimelineFilter.DataSourcesFilter; import org.sleuthkit.datamodel.TimelineFilter.EventTypeFilter; import org.sleuthkit.datamodel.TimelineFilter.FileTypesFilter; import org.sleuthkit.datamodel.TimelineFilter.HashHitsFilter; -import org.sleuthkit.datamodel.TimelineFilter.HashSetFilter; import org.sleuthkit.datamodel.TimelineFilter.HideKnownFilter; import org.sleuthkit.datamodel.TimelineFilter.RootFilter; -import org.sleuthkit.datamodel.TimelineFilter.TagNameFilter; import org.sleuthkit.datamodel.TimelineFilter.TagsFilter; import org.sleuthkit.datamodel.TimelineFilter.TextFilter; @@ -129,8 +123,6 @@ public final class FilteredEventsModel { private final LoadingCache> eventCountsCache; /** Map from datasource id to datasource name. */ private final ObservableMap datasourcesMap = FXCollections.observableHashMap(); - private final ObservableSet< String> hashSets = FXCollections.observableSet(); - private final ObservableList tagNames = FXCollections.observableArrayList(); // end caches /** @@ -171,8 +163,6 @@ public final class FilteredEventsModel { }; datasourcesMap.addListener(filterSyncListener); - hashSets.addListener(filterSyncListener); - tagNames.addListener(filterSyncListener); requestedFilter.set(getDefaultFilter()); @@ -248,15 +238,11 @@ public final class FilteredEventsModel { */ synchronized private void populateFilterData() throws TskCoreException { SleuthkitCase skCase = autoCase.getSleuthkitCase(); - hashSets.addAll(eventManager.getHashSetNames()); //because there is no way to remove a datasource we only add to this map. for (DataSource ds : skCase.getDataSources()) { datasourcesMap.putIfAbsent(ds.getId(), ds.getName()); } - - //should this only be tags applied to files or event bearing artifacts? - tagNames.setAll(skCase.getTagNamesInUse()); } /** @@ -269,22 +255,8 @@ public final class FilteredEventsModel { * with the tags in use in the case */ public void syncFilters(RootFilterState rootFilterState) { - TagsFilterState tagsFilterState = rootFilterState.getTagsFilterState(); - for (TagName tagName : tagNames) { - tagsFilterState.getFilter().addSubFilter(new TagNameFilter(tagName)); - } - for (FilterState tagFilterState : rootFilterState.getTagsFilterState().getSubFilterStates()) { - // disable states for tag names that don't exist in case. - tagFilterState.setDisabled(tagNames.contains(tagFilterState.getFilter().getTagName()) == false); - } - DataSourcesFilter dataSourcesFilter = rootFilterState.getDataSourcesFilterState().getFilter(); datasourcesMap.entrySet().forEach(entry -> dataSourcesFilter.addSubFilter(newDataSourceFromMapEntry(entry))); - - HashHitsFilter hashSetsFilter = rootFilterState.getHashHitsFilterState().getFilter(); - for (String hashSet : hashSets) { - hashSetsFilter.addSubFilter(new HashSetFilter(hashSet)); - } } /** @@ -351,10 +323,8 @@ public final class FilteredEventsModel { -> dataSourcesFilter.addSubFilter(newDataSourceFromMapEntry(dataSourceEntry))); HashHitsFilter hashHitsFilter = new HashHitsFilter(); - hashSets.stream().map(HashSetFilter::new).forEach(hashHitsFilter::addSubFilter); TagsFilter tagsFilter = new TagsFilter(); - tagNames.stream().map(TagNameFilter::new).forEach(tagsFilter::addSubFilter); FileTypesFilter fileTypesFilter = FilterUtils.createDefaultFileTypesFilter(); @@ -388,20 +358,6 @@ public final class FilteredEventsModel { return events; } - /** - * get a count of tagnames applied to the given event ids as a map from - * tagname displayname to count of tag applications - * - * @param eventIDsWithTags the event ids to get the tag counts map for - * - * @return a map from tagname displayname to count of applications - * - * @throws org.sleuthkit.datamodel.TskCoreException - */ - public Map getTagCountsByTagName(Set eventIDsWithTags) throws TskCoreException { - return eventManager.getTagCountsByTagName(eventIDsWithTags); - } - public List getEventIDs(Interval timeRange, FilterState filter) throws TskCoreException { final Interval overlap; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java index 17c6e3c394..3ee088f2ad 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java @@ -24,15 +24,12 @@ import com.google.common.collect.Sets; import com.google.common.eventbus.Subscribe; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.logging.Level; -import java.util.stream.Collectors; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; @@ -82,9 +79,7 @@ import static org.sleuthkit.autopsy.timeline.ui.detailview.EventNodeBase.show; import static org.sleuthkit.autopsy.timeline.ui.detailview.MultiEventNodeBase.CORNER_RADII_3; import org.sleuthkit.autopsy.timeline.ui.detailview.datamodel.DetailViewEvent; import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TimelineEventType; -import org.sleuthkit.datamodel.TimelineEvent; /** * @@ -269,7 +264,7 @@ public abstract class EventNodeBase extends StackP } /** - * defer tooltip content creation till needed, this had a surprisingly large + * defer tooltip content creation until needed, this had a surprisingly large * impact on speed of loading the chart */ @NbBundle.Messages({"# {0} - counts", @@ -293,37 +288,9 @@ public abstract class EventNodeBase extends StackP @Override protected String call() throws Exception { - HashMap hashSetCounts = new HashMap<>(); - if (tlEvent.getEventIDsWithHashHits().isEmpty() == false) { - try { - //TODO:push this to DB - for (TimelineEvent tle : eventsModel.getEventsById(tlEvent.getEventIDsWithHashHits())) { - Set hashSetNames = sleuthkitCase.getContentById(tle.getFileObjID()).getHashSetNames(); - for (String hashSetName : hashSetNames) { - hashSetCounts.merge(hashSetName, 1L, Long::sum); - } - } - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error getting hashset hit info for event.", ex); //NON-NLS - } - } - String hashSetCountsString = hashSetCounts.entrySet().stream() - .map((Map.Entry t) -> t.getKey() + " : " + t.getValue()) - .collect(Collectors.joining("\n")); - - Map tagCounts = new HashMap<>(); - if (tlEvent.getEventIDsWithTags().isEmpty() == false) { - tagCounts.putAll(eventsModel.getTagCountsByTagName(tlEvent.getEventIDsWithTags())); - } - String tagCountsString = tagCounts.entrySet().stream() - .map((Map.Entry t) -> t.getKey() + " : " + t.getValue()) - .collect(Collectors.joining("\n")); - return Bundle.EventNodeBase_tooltip_text(getEventIDs().size(), getEventType(), getDescription(), TimeLineController.getZonedFormatter().print(getStartMillis()), - TimeLineController.getZonedFormatter().print(getEndMillis() + 1000)) - + (hashSetCountsString.isEmpty() ? "" : Bundle.EventNodeBase_toolTip_hashSetHits(hashSetCountsString)) - + (tagCountsString.isEmpty() ? "" : Bundle.EventNodeBase_toolTip_tags(tagCountsString)); + TimeLineController.getZonedFormatter().print(getEndMillis() + 1000)); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java index 66f53c8998..e3c8b8d6d5 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/RootFilterState.java @@ -35,21 +35,21 @@ import org.sleuthkit.datamodel.TimelineFilter.EventTypeFilter; import org.sleuthkit.datamodel.TimelineFilter.FileTypeFilter; import org.sleuthkit.datamodel.TimelineFilter.FileTypesFilter; import org.sleuthkit.datamodel.TimelineFilter.HashHitsFilter; -import org.sleuthkit.datamodel.TimelineFilter.HashSetFilter; import org.sleuthkit.datamodel.TimelineFilter.HideKnownFilter; import org.sleuthkit.datamodel.TimelineFilter.RootFilter; import org.sleuthkit.datamodel.TimelineFilter.TextFilter; +import org.sleuthkit.datamodel.TimelineFilter.TagsFilter; /** A FilterState for RootFilters. Provides named access to the sub - * filterstates. + * filter states. */ public class RootFilterState extends CompoundFilterState { private final CompoundFilterState eventTypeFilterState; private final SqlFilterState knownFilterState; private final SqlFilterState textFilterState; - private final TagsFilterState tagsFilterState; - private final CompoundFilterState hashHitsFilterState; + private final SqlFilterState tagsFilterState; + private final SqlFilterState hashHitsFilterState; private final CompoundFilterState dataSourcesFilterState; private final CompoundFilterState fileTypesFilterState; @@ -63,8 +63,8 @@ public class RootFilterState extends CompoundFilterState(delegate.getEventTypeFilter()), new SqlFilterState<>(delegate.getKnownFilter()), new SqlFilterState<>(delegate.getTextFilter()), - new TagsFilterState(delegate.getTagsFilter()), - new CompoundFilterState<>(delegate.getHashHitsFilter()), + new SqlFilterState<>(delegate.getTagsFilter()), + new SqlFilterState<>(delegate.getHashHitsFilter()), new CompoundFilterState<>(delegate.getDataSourcesFilter()), new CompoundFilterState<>(delegate.getFileTypesFilter()) ); @@ -74,8 +74,8 @@ public class RootFilterState extends CompoundFilterState eventTypeFilterState, SqlFilterState knownFilterState, SqlFilterState textFilterState, - TagsFilterState tagsFilterState, - CompoundFilterState hashHitsFilterState, + SqlFilterState tagsFilterState, + SqlFilterState hashHitsFilterState, CompoundFilterState dataSourcesFilterState, CompoundFilterState fileTypesFilterState) { super(filter, Arrays.asList(eventTypeFilterState, knownFilterState, textFilterState, tagsFilterState, hashHitsFilterState, dataSourcesFilterState, fileTypesFilterState)); @@ -133,11 +133,11 @@ public class RootFilterState extends CompoundFilterState getTagsFilterState() { return tagsFilterState; } - public CompoundFilterState getHashHitsFilterState() { + public SqlFilterState getHashHitsFilterState() { return hashHitsFilterState; } @@ -161,18 +161,6 @@ public class RootFilterState extends CompoundFilterState> getSubFilterStates() { ImmutableMap, Integer> filterOrder diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/SqlFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/SqlFilterState.java index 5180dd3425..2f33ebec61 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/SqlFilterState.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/SqlFilterState.java @@ -36,6 +36,15 @@ public class SqlFilterState extends AbstractF // the "Hide Known Filters", "Tags", "Hashsets" and "Text". // There are better ways to do this, but this works in a pinch this(filter, !(filter instanceof TimelineFilter.HideKnownFilter || filter instanceof TimelineFilter.TagsFilter || filter instanceof TimelineFilter.HashHitsFilter || filter instanceof TimelineFilter.TextFilter)); + + selectedProperty().addListener(selectedProperty -> { + if (filter instanceof TimelineFilter.TagsFilter) { + ((TimelineFilter.TagsFilter)filter).setTagged(isSelected()); + } else if (filter instanceof TimelineFilter.HashHitsFilter) { + ((TimelineFilter.HashHitsFilter)filter).setTagged(isSelected()); + } + }); + } /** diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TagsFilterState.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TagsFilterState.java deleted file mode 100755 index 12b17ed201..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/datamodel/TagsFilterState.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2018-2019 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.timeline.ui.filtering.datamodel; - -import com.google.common.collect.Lists; -import java.util.Collection; -import javafx.collections.ListChangeListener; -import org.sleuthkit.datamodel.TimelineFilter.TagNameFilter; -import org.sleuthkit.datamodel.TimelineFilter.TagsFilter; - -/** - * Specialization of CompoundFilterState for TagName/Tags-Filter. - * - * Newly added subfilters made to be SELECTED when they are added. - */ -public class TagsFilterState extends CompoundFilterState { - - public TagsFilterState(TagsFilter delegate) { - super(delegate); - installSelectNewFiltersListener(); - - } - - public TagsFilterState(TagsFilter delegate, Collection> subFilterStates) { - super(delegate, subFilterStates); - installSelectNewFiltersListener(); - } - - private void installSelectNewFiltersListener() { - getSubFilterStates().addListener((ListChangeListener.Change> change) -> { - while (change.next()) { - change.getAddedSubList().forEach(filterState -> filterState.setSelected(true)); - } - }); - } - - @Override - public TagsFilterState copyOf() { - TagsFilterState copy = new TagsFilterState(getFilter().copyOf(), - Lists.transform(getSubFilterStates(), FilterState::copyOf)); - - copy.setSelected(isSelected()); - copy.setDisabled(isDisabled()); - return copy; - } - - @Override - public TagsFilter getActiveFilter() { - if (isActive() == false) { - return null; - } - - TagsFilter copy = new TagsFilter(); - //add active subfilters to copy. - getSubFilterStates().stream() - .filter(FilterState::isActive) - .map(FilterState::getActiveFilter) - .forEach(copy::addSubFilter); - - return copy; - } -} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED index 6ff7b8fdcf..805e776717 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -51,6 +51,7 @@ ExtractOs.windowsVolume.label=OS Drive (Windows) ExtractOs.yellowDogLinuxOs.label=Linux (Yellow Dog) ExtractOs.yellowDogLinuxVolume.label=OS Drive (Linux Yellow Dog) ExtractOS_progressMessage=Checking for OS +ExtractRecycleBin_module_name=Recycle Bin ExtractSafari_Error_Getting_History=An error occurred while processing Safari history files. ExtractSafari_Error_Parsing_Bookmark=An error occured while processing Safari Bookmark files ExtractSafari_Error_Parsing_Cookies=An error occured while processing Safari Cookies files diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRecycleBin.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRecycleBin.java new file mode 100755 index 0000000000..3fa746cf82 --- /dev/null +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRecycleBin.java @@ -0,0 +1,620 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * + * Copyright 2012 42six Solutions. + * Contact: aebadirad 42six com + * Project Contact/Architect: 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.recentactivity; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import org.joda.time.Instant; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.services.FileManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_ACCOUNT; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_DELETED; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.FsContent; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.TskDataException; + +/** + * This module is based on the RecycleBin python module from Mark McKinnon. + * + * @see + * Recycle_Bin.py + * + */ +final class ExtractRecycleBin extends Extract { + + private static final Logger logger = Logger.getLogger(ExtractRecycleBin.class.getName()); + + private static final String RECYCLE_BIN_ARTIFACT_NAME = "TSK_RECYCLE_BIN"; //NON-NLS + + private static final int V1_FILE_NAME_OFFSET = 24; + private static final int V2_FILE_NAME_OFFSET = 28; + + @Messages({ + "ExtractRecycleBin_module_name=Recycle Bin" + }) + ExtractRecycleBin() { + this.moduleName = Bundle.ExtractRecycleBin_module_name(); + } + + @Override + void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) { + // At this time it was decided that we would not include TSK_RECYCLE_BIN + // in the default list of BlackboardArtifact types. + try { + createRecycleBinArtifactType(); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("%s may not have been created.", RECYCLE_BIN_ARTIFACT_NAME), ex); + } + + BlackboardArtifact.Type recycleBinArtifactType; + + try { + recycleBinArtifactType = tskCase.getArtifactType(RECYCLE_BIN_ARTIFACT_NAME); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to retrive custom artifact type %s", RECYCLE_BIN_ARTIFACT_NAME), ex); // NON-NLS + // If this doesn't work bail. + return; + } + + // map SIDs to user names so that we can include that in the artifact + Map userNameMap; + try { + userNameMap = makeUserNameMap(dataSource); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to create OS Account user name map", ex); + // This is not the end of the world we will just continue without + // user names + userNameMap = new HashMap<>(); + } + + FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); + + // Collect all of the $R files so that we can later easily map them to corresponding $I file + Map> rFileMap; + try { + rFileMap = makeRFileMap(dataSource); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to create $R file map for dataSource: %s", dataSource.getName()), ex); + return; // No $R files, no need to continue; + } + + // Get the $I files + List iFiles; + try { + iFiles = fileManager.findFiles(dataSource, "$I%"); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to find recycle bin I files.", ex); //NON-NLS + return; // No need to continue + } + + String tempRARecycleBinPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), "recyclebin"); //NON-NLS + + // cycle through the $I files and process each. + for (AbstractFile iFile : iFiles) { + + if (context.dataSourceIngestIsCancelled()) { + return; + } + + processIFile(context, recycleBinArtifactType, iFile, userNameMap, rFileMap, tempRARecycleBinPath); + } + + (new File(tempRARecycleBinPath)).delete(); + } + + /** + * Process each individual iFile. + * + * @param context + * @param recycleBinArtifactType Module created artifact type + * @param iFile The AbstractFile to process + * @param userNameMap Map of user ids to names + * @param tempRARecycleBinPath Temp directory path + */ + private void processIFile(IngestJobContext context, BlackboardArtifact.Type recycleBinArtifactType, AbstractFile iFile, Map userNameMap, Map> rFileMap, String tempRARecycleBinPath) { + String tempFilePath = tempRARecycleBinPath + File.separator + Instant.now().getMillis() + iFile.getName(); + try { + try { + ContentUtils.writeToFile(iFile, new File(tempFilePath)); + } catch (IOException ex) { + logger.log(Level.WARNING, String.format("Unable to write %s to temp directory. File name: %s", iFile.getName(), tempFilePath), ex); //NON-NLS + // if we cannot make a copy of the $I file for later processing + // move onto the next file + return; + } + + // get the original name, dates, etc. from the $I file + RecycledFileMetaData metaData; + try { + metaData = parseIFile(tempFilePath); + } catch (IOException ex) { + logger.log(Level.WARNING, String.format("Unable to parse iFile %s", iFile.getName()), ex); //NON-NLS + // Unable to parse the $I file move onto the next file + return; + } + + // each user has its own Recyle Bin folder. Figure out the user name based on its name . + String userID = getUserIDFromPath(iFile.getParentPath()); + String userName = ""; + if (!userID.isEmpty()) { + userName = userNameMap.get(userID); + } else { + // If the iFile doesn't have a user ID in its parent + // directory structure then it is not from the recyle bin + return; + } + + // get the corresponding $R file, which is in the same folder and has the file content + String rFileName = iFile.getName().replace("$I", "$R"); //NON-NLS + List rFiles = rFileMap.get(rFileName); + if (rFiles == null) { + return; + } + SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); + for (AbstractFile rFile : rFiles) { + if (context.dataSourceIngestIsCancelled()) { + return; + } + + if (iFile.getParentPath().equals(rFile.getParentPath()) + && iFile.getMetaFlagsAsString().equals(rFile.getMetaFlagsAsString())) { + try { + postArtifact(createArtifact(rFile, recycleBinArtifactType, metaData.getFullWindowsPath(), userName, metaData.getDeletedTimeStamp())); + + // If we are processing a disk image, we will also make a deleted file entry so that the user + // sees the deleted file in its original folder. We re-use the metadata address so that the user + // can see the content. + if (rFile instanceof FsContent) { + // if the user deleted a folder, then we need to recusively go into it. Note the contents of the $R folder + // do not have corresponding $I files anymore. Only the $R folder does. + if (rFile.isDir()) { + AbstractFile directory = getOrMakeFolder(Case.getCurrentCase().getSleuthkitCase(), (FsContent) rFile, metaData.getFullWindowsPath()); + popuplateDeletedDirectory(Case.getCurrentCase().getSleuthkitCase(), directory, rFile.getChildren(), metaData.getFullWindowsPath(), metaData.getDeletedTimeStamp()); + + } else { + AbstractFile folder = getOrMakeFolder(Case.getCurrentCase().getSleuthkitCase(), (FsContent) rFile.getParent(), Paths.get(metaData.getFullWindowsPath()).getParent().toString()); + addFileSystemFile(skCase, (FsContent)rFile, folder, Paths.get(metaData.getFullWindowsPath()).getFileName().toString(), metaData.getDeletedTimeStamp()); + } + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to add attributes to artifact %s", rFile.getName()), ex); //NON-NLS + } + } + } + } finally { + (new File(tempFilePath)).delete(); + } + } + + /** + * Add the children of recycled $R folder to the folder. + * + * @param skCase The current Sleuthkit case + * @param parentFolder The folder to folder the deleted files are to be + * added. + * @param children The recycled children of the $R folder + * @param parentPath String path to the directory the children were + * deleted from + * @param deletedTimeStamp The time at which the files were deleted, + * inherited from the $R file. + * + * @throws TskCoreException + */ + private void popuplateDeletedDirectory(SleuthkitCase skCase, AbstractFile parentFolder, List recycledChildren, String parentPath, long deletedTimeStamp) throws TskCoreException { + if (recycledChildren == null) { + return; + } + + for (Content child : recycledChildren) { + if (child instanceof FsContent) { + FsContent fsContent = (FsContent) child; + if (fsContent.isFile()) { + addFileSystemFile(skCase, fsContent, parentFolder, fsContent.getName(), deletedTimeStamp); + } else if (fsContent.isDir()) { + String newPath = parentPath + "\\" + fsContent.getName(); + AbstractFile childFolder = getOrMakeFolder(skCase, (FsContent) fsContent, parentPath); + popuplateDeletedDirectory(skCase, childFolder, fsContent.getChildren(), newPath, deletedTimeStamp); + } + } + } + } + + /** + * Parse the $I file. + * + * File format prior to Windows 10: + * + * + * + * + * + * + *
OffsetSizeDescription
08Header
88File Size
168Deleted Timestamp
24520File Name
+ * + * File format Windows 10+ + * + * + * + * + * + * + * + *
OffsetSizeDescription
08Header
88File Size
168Deleted TimeStamp
244File Name Length
28varFile Name
+ * + * For versions of Windows prior to 10, header = 0x01. Windows 10+ header == + * 0x02 + * + * @param iFilePath Path to local copy of file in temp folder + * + * @throws FileNotFoundException + * @throws IOException + */ + private RecycledFileMetaData parseIFile(String iFilePath) throws FileNotFoundException, IOException { + byte[] allBytes = Files.readAllBytes(Paths.get(iFilePath)); + + ByteBuffer byteBuffer = ByteBuffer.wrap(allBytes); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + + long version = byteBuffer.getLong(); + long fileSize = byteBuffer.getLong(); + long timestamp = byteBuffer.getLong(); + + // Convert from windows FILETIME to Unix Epoch seconds + timestamp = Util.filetimeToMillis(timestamp) / 1000; + + byte[] stringBytes; + + if (version == 1) { + stringBytes = Arrays.copyOfRange(allBytes, V1_FILE_NAME_OFFSET, allBytes.length); + } else { + int fileNameLength = byteBuffer.getInt() * 2; //Twice the bytes for unicode + stringBytes = Arrays.copyOfRange(allBytes, V2_FILE_NAME_OFFSET, V2_FILE_NAME_OFFSET + fileNameLength); + } + + String fileName = new String(stringBytes, "UTF-16LE"); //NON-NLS + + return new RecycledFileMetaData(fileSize, timestamp, fileName); + } + + /** + * Create a map of userids to usernames from the OS Accounts. + * + * @param dataSource + * + * @return A Map of userIDs and userNames + * + * @throws TskCoreException + */ + private Map makeUserNameMap(Content dataSource) throws TskCoreException { + Map userNameMap = new HashMap<>(); + + List accounts = blackboard.getArtifacts(TSK_OS_ACCOUNT.getTypeID(), dataSource.getId()); + + for (BlackboardArtifact account : accounts) { + BlackboardAttribute nameAttribute = getAttributeForArtifact(account, TSK_USER_NAME); + BlackboardAttribute idAttribute = getAttributeForArtifact(account, TSK_USER_ID); + + String userName = nameAttribute != null ? nameAttribute.getDisplayString() : ""; + String userID = idAttribute != null ? idAttribute.getDisplayString() : ""; + + if (!userID.isEmpty()) { + userNameMap.put(userID, userName); + } + } + + return userNameMap; + } + + /** + * Get a list of files that start with $R and create a map of the file to + * their name. + * + * @param dataSource + * + * @return File map + * + * @throws TskCoreException + */ + private Map> makeRFileMap(Content dataSource) throws TskCoreException { + FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); + List rFiles = fileManager.findFiles(dataSource, "$R%"); + Map> fileMap = new HashMap<>(); + + for (AbstractFile rFile : rFiles) { + String fileName = rFile.getName(); + List fileList = fileMap.get(fileName); + + if (fileList == null) { + fileList = new ArrayList<>(); + fileMap.put(fileName, fileList); + } + + fileList.add(rFile); + } + + return fileMap; + } + + /** + * Helper functions to get the user ID from the iFile parent path. User ids + * will be of the form S-. + * + * @param iFileParentPath String parent path of the iFile + * + * @return String user id + */ + private String getUserIDFromPath(String iFileParentPath) { + int index = iFileParentPath.indexOf('-') - 1; + if (index >= 0) { + return (iFileParentPath.substring(index)).replace("/", ""); + } else { + return ""; + } + } + + /** + * Gets the attribute for the given type from the given artifact. + * + * @param artifact BlackboardArtifact to get the attribute from + * @param type The BlackboardAttribute Type to get + * + * @return BlackboardAttribute for given artifact and type + * + * @throws TskCoreException + */ + private BlackboardAttribute getAttributeForArtifact(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) throws TskCoreException { + return artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(type.getTypeID()))); + } + + /** + * Create TSK_RECYCLE_BIN artifact type. + * + * @throws TskCoreException + */ + private void createRecycleBinArtifactType() throws TskCoreException { + try { + tskCase.addBlackboardArtifactType(RECYCLE_BIN_ARTIFACT_NAME, "Recycle Bin"); //NON-NLS + } catch (TskDataException ex) { + logger.log(Level.INFO, String.format("%s may have already been defined for this case", RECYCLE_BIN_ARTIFACT_NAME)); + } + + } + + /** + * Create the new artifact for the give rFile + * + * @param rFile AbstractFile to create the artifact for + * @param type Type of artifact to create + * @param fileName The original path of the deleted file + * @param userName The name of the user that deleted the file + * @param dateTime The time in epoch seconds that the file was deleted + * + * @return Newly created artifact + * + * @throws TskCoreException + */ + private BlackboardArtifact createArtifact(AbstractFile rFile, BlackboardArtifact.Type type, String fileName, String userName, long dateTime) throws TskCoreException { + BlackboardArtifact bba = rFile.newArtifact(type.getTypeID()); + bba.addAttribute(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + bba.addAttribute(new BlackboardAttribute(TSK_DATETIME_DELETED, getName(), dateTime)); + bba.addAttribute(new BlackboardAttribute(TSK_USER_NAME, getName(), userName == null || userName.isEmpty() ? "" : userName)); + return bba; + } + + /** + * Returns a folder for the given path. If the path does not exist the + * the folder is created. Recursively makes as many parent folders as needed. + * + * @param skCase + * @param dataSource + * @param path + * + * @return AbstractFile for the given path. + * + * @throws TskCoreException + */ + private AbstractFile getOrMakeFolder(SleuthkitCase skCase, FsContent dataSource, String path) throws TskCoreException { + + String parentPath = getParentPath(path); + String folderName = getFileName(path); + + List files = null; + if (parentPath != null) { + if (!parentPath.equals("/")) { + parentPath = parentPath + "/"; + } + + files = skCase.findAllFilesWhere(String.format("fs_obj_id=%s AND parent_path='%s' AND name='%s'", + dataSource.getFileSystemId(), SleuthkitCase.escapeSingleQuotes(parentPath), folderName != null ? SleuthkitCase.escapeSingleQuotes(folderName) : "")); + } else { + files = skCase.findAllFilesWhere(String.format("fs_obj_id=%s AND parent_path='/' AND name=''", dataSource.getFileSystemId())); + } + + if (files == null || files.isEmpty()) { + AbstractFile parent = getOrMakeFolder(skCase, dataSource, parentPath); + return skCase.addVirtualDirectory(parent.getId(), folderName); + } else { + return files.get(0); + } + } + + /** + * Adds a new file system file that is unallocated and maps to the original + * file in recycle bin directory. + * + * @param skCase The current case. + * @param recycleBinFile The file from the recycle bin. + * @param parentDir The directory that the recycled file was deleted. + * @param fileName The name of the file. + * @param deletedTime The time the file was deleted. + * + * @throws TskCoreException + */ + private void addFileSystemFile(SleuthkitCase skCase, FsContent recycleBinFile, Content parentDir, String fileName, long deletedTime) throws TskCoreException { + skCase.addFileSystemFile( + recycleBinFile.getDataSourceObjectId(), + recycleBinFile.getFileSystemId(), + fileName, + recycleBinFile.getMetaAddr(), + (int) recycleBinFile.getMetaSeq(), + recycleBinFile.getAttrType(), + recycleBinFile.getAttributeId(), + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC, + (short) (TskData.TSK_FS_META_FLAG_ENUM.UNALLOC.getValue() | TskData.TSK_FS_META_FLAG_ENUM.USED.getValue()), + recycleBinFile.getSize(), + recycleBinFile.getCtime(), recycleBinFile.getCrtime(), recycleBinFile.getAtime(), deletedTime, + true, parentDir); + } + + /** + * Clean up the windows path string to match what the autopsy db uses. + * + * @param path The file\folder path to normalize + * + * @return New path string with the root removed (ie X:) and the slashes + * changed from windows to unix. + */ + String normalizeFilePath(String pathString) { + if (pathString == null || pathString.isEmpty()) { + return null; + } + + Path path = Paths.get(pathString); + int nameCount = path.getNameCount(); + if(nameCount > 0) { + String rootless = "/" + path.subpath(0, nameCount); + return rootless.replace("\\", "/"); + } else { + return "/"; + } + } + + /** + * Helper function get from the given path either the file name or + * the last directory in the path. + * + * @param filePath The file\directory path + * + * @return If file path, returns the file name. If directory path the + * The last directory in the path is returned. + */ + String getFileName(String filePath) { + Path fileNamePath = Paths.get(filePath).getFileName(); + if (fileNamePath != null) { + return fileNamePath.toString(); + } + return filePath; + } + + /** + * Returns the parent path for the given path. + * + * @param path Path string + * + * @return The parent path for the given path. + */ + String getParentPath(String path) { + Path parentPath = Paths.get(path).getParent(); + if (parentPath != null) { + return normalizeFilePath(parentPath.toString()); + } + return null; + } + + /** + * Stores the data from the $I files. + */ + final class RecycledFileMetaData { + + private final long fileSize; + private final long deletedTimeStamp; + private final String fileName; + + /** + * Constructs a new instance. + * + * @param fileSize Size of the deleted file. + * @param deletedTimeStamp Time the file was deleted. + * @param fileName Name of the deleted file. + */ + RecycledFileMetaData(Long fileSize, long deletedTimeStamp, String fileName) { + this.fileSize = fileSize; + this.deletedTimeStamp = deletedTimeStamp; + this.fileName = fileName; + } + + /** + * Returns the size of the recycled file. + * + * @return Size of deleted file + */ + long getFileSize() { + return fileSize; + } + + /** + * Returns the time the file was deleted. + * + * @return deleted time in epoch seconds. + */ + long getDeletedTimeStamp() { + return deletedTimeStamp; + } + + /** + * Returns the full path to the deleted file or folder. This path will + * include the drive letter, ie C:\ + * + * @return String name of the deleted file + */ + String getFullWindowsPath() { + return fileName.trim(); + } + } +} diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java index db815e9274..444a6d638d 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java @@ -77,6 +77,7 @@ public final class RAImageIngestModule implements DataSourceIngestModule { Extract dataSourceAnalyzer = new DataSourceUsageAnalyzer(); Extract safari = new ExtractSafari(); Extract zoneInfo = new ExtractZoneIdentifier(); + Extract recycleBin = new ExtractRecycleBin(); extractors.add(chrome); extractors.add(firefox); @@ -89,6 +90,7 @@ public final class RAImageIngestModule implements DataSourceIngestModule { extractors.add(osExtract); // this needs to run before the DataSourceUsageAnalyzer extractors.add(dataSourceAnalyzer); //this needs to run after ExtractRegistry and ExtractOs extractors.add(zoneInfo); // this needs to run after the web browser modules + extractors.add(recycleBin); // this needs to run after ExtractRegistry and ExtractOS browserExtractors.add(chrome); browserExtractors.add(firefox); diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java index a6c6416416..ff95c60ca8 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java @@ -51,6 +51,12 @@ import org.sleuthkit.datamodel.TskCoreException; class Util { private static Logger logger = Logger.getLogger(Util.class.getName()); + + /** Difference between Filetime epoch and Unix epoch (in ms). */ + private static final long FILETIME_EPOCH_DIFF = 11644473600000L; + + /** One millisecond expressed in units of 100s of nanoseconds. */ + private static final long FILETIME_ONE_MILLISECOND = 10 * 1000; private Util() { } @@ -176,4 +182,16 @@ class Util { } return results; } + + /** + * Converts a windows FILETIME to java-unix epoch milliseconds + * + * @param filetime 100 nanosecond intervals from jan 1, 1601 + * + * @return java-unix epoch milliseconds + */ + static long filetimeToMillis(final long filetime) { + return (filetime / FILETIME_ONE_MILLISECOND) - FILETIME_EPOCH_DIFF; + } + } diff --git a/Running_Linux_OSX.txt b/Running_Linux_OSX.txt index 1edf48eb13..7244c6320d 100644 --- a/Running_Linux_OSX.txt +++ b/Running_Linux_OSX.txt @@ -9,10 +9,19 @@ The following need to be done at least once. They do not need to be repeated for -- Linux: % sudo apt-get install testdisk -- OS X: % brew install testdisk -- Install Oracle Java and set JAVA_HOME. --- Linux: Use the instructions here: https://medium.com/coderscorner/installing-oracle-java-8-in-ubuntu-16-10-845507b13343 - NOTE: You may need to log out and back in again after setting JAVA_HOME before the Autopsy +- Install a Java 8 JRE and JavaFX 8 and set JAVA_HOME. +-- Linux: Any Java 8 version of OpenJDK/OpenJFX distribution should suffice. The following instructions use the Zulu Community distribution. + 1. Download a 64 bit Java 8 JRE for your specific platform from https://www.azul.com/downloads/zulu-community + 2. Install the JRE. e.g. 'sudo apt install ./zulu8.40.0.25-ca-jre8.0.222-linux_amd64.deb' + 3. Download a 64 bit Java 8 JavaFX for your specific platform from the same location. + 4. Extract the contents of the JavaFX archive into the folder where the JRE was installed. + e.g. 'cd /usr/lib/jvm/zre-8-amd64; sudo tar xzf ~/Downloads/zulu8.40.0.25-ca-fx-jre8.0.222-linux_x64.tar.gz --strip-components=1' + 5. Confirm Java 8 is being found by running 'java -version' + 6. Set JAVA_HOME environment variable to location of JRE installation (e.g. /usr/lib/jvm/zre-8-amd64) + + NOTE: You may need to log out and back in again after setting JAVA_HOME before the Autopsy unix_setup.sh script can see the value. + -- OS X: Use The Oracle website: https://www.java.com/ Set JAVA_HOME with something like: export JAVA_HOME=`/usr/libexec/java_home` in .bash_profile diff --git a/docs/doxygen-user/Doxyfile b/docs/doxygen-user/Doxyfile index 7991a0cd95..1bb3773a70 100644 --- a/docs/doxygen-user/Doxyfile +++ b/docs/doxygen-user/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy User Documentation" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.12.0 +PROJECT_NUMBER = 4.13.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -1025,7 +1025,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = 4.12.0 +HTML_OUTPUT = 4.13.0 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index bf66159b48..b640c41549 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.12.0 +PROJECT_NUMBER = 4.13.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears a the top of each page and should give viewer a @@ -1063,7 +1063,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = api-docs/4.12.0/ +HTML_OUTPUT = api-docs/4.13.0/ # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/nbproject/project.properties b/nbproject/project.properties index 5d59189544..8132096d48 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -4,7 +4,7 @@ app.title=Autopsy ### lowercase version of above app.name=${branding.token} ### if left unset, version will default to today's date -app.version=4.12.0 +app.version=4.13.0 ### build.type must be one of: DEVELOPMENT, RELEASE #build.type=RELEASE build.type=DEVELOPMENT diff --git a/test/script/tskdbdiff.py b/test/script/tskdbdiff.py index 5518c97512..baab8573df 100644 --- a/test/script/tskdbdiff.py +++ b/test/script/tskdbdiff.py @@ -430,8 +430,8 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info report_index = line.find('INSERT INTO "reports"') > -1 or line.find('INSERT INTO reports ') > -1 layout_index = line.find('INSERT INTO "tsk_file_layout"') > -1 or line.find('INSERT INTO tsk_file_layout ') > -1 data_source_info_index = line.find('INSERT INTO "data_source_info"') > -1 or line.find('INSERT INTO data_source_info ') > -1 - event_description_index = line.find('INSERT INTO "tsk_event_descriptions"') > -1 or line.find('INSERT INTO data_source_info ') > -1 - events_index = line.find('INSERT INTO "tsk_events"') > -1 or line.find('INSERT INTO data_source_info ') > -1 + event_description_index = line.find('INSERT INTO "tsk_event_descriptions"') > -1 or line.find('INSERT INTO tsk_event_descriptions ') > -1 + events_index = line.find('INSERT INTO "tsk_events"') > -1 or line.find('INSERT INTO tsk_events ') > -1 ingest_job_index = line.find('INSERT INTO "ingest_jobs"') > -1 or line.find('INSERT INTO ingest_jobs ') > -1 examiners_index = line.find('INSERT INTO "tsk_examiners"') > -1 or line.find('INSERT INTO tsk_examiners ') > -1 ig_groups_index = line.find('INSERT INTO "image_gallery_groups"') > -1 or line.find('INSERT INTO image_gallery_groups ') > -1